Release 960131
Wed Jan 31 10:58:00 1996 Alexandre Julliard <julliard@sunsite.unc.edu>
* [configure.in]
Added --with-dll option to build libwine.so.
* [controls/listbox.c]
Fixed ListBoxDirectory(), DlgDirSelect() and
DlgDirList(). Hopefully their behavior is correct now.
* [controls/menu.c]
Use SEGPTRs in ChangeMenu(), InsertMenu(), AppendMenu() and
ModifyMenu() for the item data, to avoid corrupting the pointer
for owner-drawn items.
* [controls/static.c]
Attempt to load OEM icons for SS_ICON controls. Probably not
entirely correct.
Don't clip the text output.
* [files/directory.c]
Add temp dir and Windows dir to environment.
* [files/dos_fs.c]
Fixed a few path handling bugs in DOSFS_GetUnixFileName().
Cache last used directory in DOSFS_FindNext() to avoid quadratic
search time.
* [files/drive.c]
New format for drives configuration in wine.conf; allows
specifying the type, label and serial number of a drive.
* [files/file.c]
New function FILE_OpenUnixFile to make sure we don't open a
directory instead of a file.
Fixed DOSFS_GetUnixFileName() check_last flag in FILE_MakeDir().
* [files/profile.c]
Rewrote profile handling. Should be closer to Windows behavior now.
New function PROFILE_GetWineIniString() to get a string from wine.conf.
Support environment variables in wine.conf.
* [loader/task.c]
Fixed the order of deletion in TASK_DeleteTask() to avoid memory
corruption.
* [memory/global.c]
Create a discarded block on GlobalAlloc() if the size is 0; thanks
to John Harvey for noticing this.
* [memory/local.c]
LOCAL_GetHeap: make sure the pointer is valid before checking
magic number.
* [misc/main.c]
Moved profile and registry saving to ExitWindows(), so we don't
try to save them in case of a crash.
* [miscemu/int21.c]
INT21_GetFreeDiskSpace: try to compute the cluster size from the
filesystem size instead of hard-coding it to 64.
Fixed functions 0x3f and 0x40 to use _hread and _hwrite to allow
reading or writing 65535 bytes (thanks to Bruce Milner for this one).
* [windows/message.c]
Fixed bug in linked-list handling in MSG_DeleteQueue().
Simplified SetMessageQueue().
* [wine.ini] [wine.man]
Updated for new drives configuration format.
Tue Jan 30 11:24:46 1996 William Magro <wmagro@tc.cornell.edu>
* [controls/edit.c]
Implemented ES_PASSWORD style, EM_SETPASSWORDCHAR and
EM_GETPASSWORDCHAR messages.
* [controls/widgets.c]
Adjusted class creation flags to better match values Windows uses.
* [include/windows.h]
Fixed ES_NOHIDESEL typo.
* [loader/ne_image.c]
Added detection for zero offset in RADDR fixups. Quicken
was in an infinite loop here.
Mon Jan 29 20:12:22 1996 Albrecht Kleine <kleine@ak.sax.de>
* [files/dos_fs.c]
Bugfix: range error in month value (0..11 set to 1..12).
* [windows/caret.c]
Changed ROP2-mode to R2_NOTXORPEN in CARET_Callback for pulsed
appearance of the caret.
* [windows/mdi.c] [include/mdi.h]
Changed MDITile(): added a new parameter WORD wParam for
WM_MDITILE second tiling method (MDITILE_HORIZONTAL in wParam) as
used in Win3.1
Sun Jan 28 14:20:00 1996 Cameron Heide <heide@ee.ualberta.ca>
* [miscemu/int2f.c]
Added a small bit of MSCDEX emulation.
* [windows/alias.c]
ALIAS_RegisterAlias was returning the hash value when it should
have been returning the record number.
Sat Jan 27 10:53:51 1996 Jim Peterson <jspeter@birch.ee.vt.edu>
* [include/shell.h] [include/wintypes.h]
Moved definition of HKEY and LPHKEY types to include/wintypes.h.
Declared FONTENUMPROC in wintypes.h.
* [include/windows.h]
Added definition of KERNINGPAIR and LPKERNINGPAIR types. Added
declarations for CopyCursor(), CopyIcon(), EnumFontFamilies(),
ExtractIcon(), FatalAppExit(), FindExecutable(), GetClipCursor(),
GetKerningPairs(), GetQueueStatus(), GetRasterizerCaps(),
IsGDIObject(), IsMenu(), IsTask(), RegCloseKey(), RegCreateKey(),
RegDeleteKey(), RegEnumKey(), RegOpenKey(), RegQueryValue(),
RegSetValue(), ResetDC(), ShellExecute(), SystemParametersInfo(),
and wsprintf().
* [tools/makehtml.pl] [documentation/apiw.index]
New files that scan windows.h, commdlg.h, and toolhelp.h and output
an HTML sorted list with optional links to www.willows.com and a
tally of unimplemented APIW functions.
* [objects/cursoricon.c]
Added Win32 versions of CopyIcon() and CopyCursor() for use in
libwine.
* [win32/resource.c] [win32/winprocs.c]
Added '#include "libres.h"' and explicit declarations of windows
procs in order to avoid warnings.
* [windows/utility.c]
Added Win32 version of MulDiv() for libwine.
* [*/*] [include/windows.h]
Changed several function declarations to comply more strictly to
the windows API (without, hopefully, altering their functionality).
* [controls/menu.c]
Made the return value of CheckMenuItem be the previous state of
the menu item if it was found, otherwise -1 as specified in the
SDK. This conflicts with the APIW specification, which says it
should return TRUE if successful, otherwise FALSE.
* [include/windows.h]
Added obsolete WM_SIZE message wParam names for compatibility.
Added WinHelp() command constants, even though they are not yet
supported.
* [rc/winerc.c]
Tidied up transform_binary_file(). In argument checking, flattened
any invalid characters specified with the prefix argument.
* [library/libres.c]
Made FindResource() case-insensitive when parameter 'name' is a string.
Sat Jan 27 02:30 1996 Uwe Bonnes <bon@elektron.ikp.physik.th-darmstadt.de
* [files/drive.c]
If root "/" is given in wine.conf, use it as last resort.
* [files/file.c]
Report ER_AccessDenied it disk ist not writable
More Debug Output
* [miscemu/int21.c]
Squeezed some bugs in ExtendedOpenCreateFile
* [windows/winpos.c]
Some windows may not be moved or resized. We are missing some
structures to be exact, but the approach should help in some cases
and make things worse in much fewer.
Fri Jan 26 10:24:00 1996 Thomas Sandford <t.d.g.sandford@prds-grn.demon.co.uk>
* [loader/pe_image.c]
fixup_imports: Find builtins for Borland style entries, too
Fri Jan 26 10:24:00 1996 Martin von Loewis <loewis@informatik.hu-berlin.de>
* [controls/menu.c]
LoadMenu: branch to Win32 for PE modules
* [if1632/gdi.spec][if1632/kernel32.spec][if1632/user32.spec]
DeleteObject, GetPixel, SetPixel,WritePrivateProfileStringA,
WriteProfileStringA, EmptyClipboard, EnableMenuItem, EnableScrollBar,
EnableWindow, InvalidateRect, SetWindowTextA, WinHelpA: new relays
DrawTextA, MoveToEx, GetClientRect, InvalidateRect, LoadBitmapA/W,
LoadAcceleratorsA/W, LoadMenu[Indirect]A/W, LoadStringA/W: changed
to convert parameters or naming convention
* [include/kernel32.h][include/wintypes.h]
moved WCHAR, defined LPWSTR
* [include/string32.h][win32/string32.c][include/struct32.h]
New files
* [loader/module.h]
LoadModule: exit after returning from PE_LoadModule
* [loader/pe_image.c]
my_wcstombs: isascii does not work on Linux for Unicode
PE_LoadImage: Handle directories
* [misc/user32.c]
USER32_RECT32to16, USER32_RECT16to32: new functions
implemented new user32 relays
* [misc/newfns.c]
WIN32_WinHelpA: new function
* [win32/param32.c]
New file
* [win32/resource.c]
GetResDirEntry: added support for named entries
WIN32_LoadAcceleratorsW: invoke *32 resource functions
WIN32_LoadBitmapA: convert name to unicode if appropriate
WIN32_ParseMenu: new function
implemented new resource functions from user32.spec
Wed Jan 24 18:09:00 1996 Alex Korobka <alex@phm30.pharm.sunysb.edu>
* [objects/cursoricon.c]
GetIconId() and LoadIconHandler() functions.
* [windows/mdi.c]
Better maximization support, TranslateMDISysAccel() function,
misc improvements.
* [windows/defwnd.c]
Fix for WM_WINDOWPOSCHANGED message handler.
* [windows/winpos.c]
Rewrote WindowFromPoint() function.
Sun Jan 21 1996 17:05:09 Marcus Meissner <msmeissn@faui01.informatik.uni-erlangen.de>
* [include/toolhelp.h] [misc/toolhelp.c]
Added Notify(Un)Register, but no callbacks yet.
Fri Jan 19 01:43:37 1996 Victor Schneider <root@tailor.roman.org>
* [Makefile.in]
Added target for libwine.so.1.0.
* [library/winmain.c]
For WINELIBDLL, _WinMain just returns hInstance instead of calling
WinMain().
* [misc/main.c]
For WINELIBDLL, renamed main() to _wine_main() for calling from the
stub main function.
* [library/winestub.c] (new file)
Provides a stub main() function for using libwine.so.
Tue Jan 16 11:04:34 1996 Anand Kumria <akumria@ozemail.com.au>
* [winsocket.c]
Fix EPERM problem.
* [global.c]
Attempt to do some sanity checking in MemManInfo().
* [Changelog]
Fix changelog oversight for previous entry.
diff --git a/files/Makefile.in b/files/Makefile.in
index 5fee060..cf17c1c 100644
--- a/files/Makefile.in
+++ b/files/Makefile.in
@@ -5,7 +5,8 @@
directory.c \
dos_fs.c \
drive.c \
- file.c
+ file.c \
+ profile.c
all: $(MODULE).o
diff --git a/files/directory.c b/files/directory.c
index 9de0c52..5365bd6 100644
--- a/files/directory.c
+++ b/files/directory.c
@@ -13,6 +13,7 @@
#include "drive.h"
#include "file.h"
#include "msdos.h"
+#include "options.h"
#include "xmalloc.h"
#include "stddebug.h"
#include "debug.h"
@@ -42,8 +43,7 @@
const char *dos_name ,*unix_name;
BYTE attr;
- GetPrivateProfileString( "wine", keyname, defval,
- path, sizeof(path), WineIniFileName() );
+ PROFILE_GetWineIniString( "wine", keyname, defval, path, sizeof(path) );
if (!(unix_name = DOSFS_GetUnixFileName( path, TRUE )) ||
!FILE_Stat( unix_name, &attr, NULL, NULL, NULL ) ||
!(attr & FA_DIRECTORY))
@@ -117,7 +117,7 @@
*/
int DIR_Init(void)
{
- char path[MAX_PATHNAME_LEN];
+ char path[MAX_PATHNAME_LEN], *env_p;
int drive;
const char *cwd;
@@ -153,14 +153,26 @@
DRIVE_Chdir( drive, DIR_WindowsDosDir + 2 );
}
- GetPrivateProfileString( "wine", "path", "c:\\windows;c:\\windows\\system",
- path, sizeof(path), WineIniFileName() );
+ PROFILE_GetWineIniString("wine", "path", "c:\\windows;c:\\windows\\system",
+ path, sizeof(path) );
DIR_ParseWindowsPath( path );
dprintf_dosfs( stddeb, "WindowsDir = %s\nSystemDir = %s\n",
DIR_WindowsDosDir, DIR_SystemDosDir );
dprintf_dosfs( stddeb, "TempDir = %s\nCwd = %c:\\%s\n",
DIR_TempDosDir, 'A' + drive, DRIVE_GetDosCwd( drive ) );
+
+ /* Put the temp and Windows directories into the environment */
+
+ env_p = (char *)xmalloc( strlen(DIR_TempDosDir) + 5 );
+ strcpy( env_p, "TEMP=" );
+ strcpy( env_p + 5, DIR_TempDosDir );
+ putenv( env_p );
+ env_p = (char *)xmalloc( strlen(DIR_WindowsDosDir) + 7 );
+ strcpy( env_p, "windir=" );
+ strcpy( env_p + 7, DIR_WindowsDosDir );
+ putenv( env_p );
+
return 1;
}
diff --git a/files/dos_fs.c b/files/dos_fs.c
index 6f4e921..29131ec 100644
--- a/files/dos_fs.c
+++ b/files/dos_fs.c
@@ -25,7 +25,7 @@
#include "debug.h"
/* Chars we don't want to see in DOS file names */
-#define INVALID_DOS_CHARS "*?<>|\"+=,; "
+#define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
static const char *DOSFS_Devices[][2] =
{
@@ -264,7 +264,8 @@
if (pTime)
*pTime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
if (pDate)
- *pDate = ((tm->tm_year - 80) << 9) + (tm->tm_mon << 5) + tm->tm_mday;
+ *pDate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
+ + tm->tm_mday;
}
@@ -506,7 +507,7 @@
p += strlen(p);
while (!IS_END_OF_NAME(*name)) name++;
}
- else
+ else if (!check_last)
{
*p++ = '/';
for (len--; !IS_END_OF_NAME(*name) && (len > 1); name++, len--)
@@ -517,16 +518,16 @@
}
if (!found)
{
- if (*name) /* Not last */
- {
- DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
- return NULL;
- }
if (check_last)
{
DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
return NULL;
}
+ if (*name) /* Not last */
+ {
+ DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
+ return NULL;
+ }
}
if (!buffer[0]) strcpy( buffer, "/" );
dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: returning %s\n", buffer );
@@ -571,19 +572,20 @@
return NULL;
}
- strcpy( buffer, "A:\\" );
- buffer[0] += drive;
- if ((name[0] == '\\') || (name[0] == '/'))
+ p = buffer;
+ *p++ = 'A' + drive;
+ *p++ = ':';
+ if (IS_END_OF_NAME(*name))
{
while ((*name == '\\') || (*name == '/')) name++;
- p = buffer + 2;
}
else
{
- lstrcpyn( buffer + 3, DRIVE_GetDosCwd(drive), len - 3 );
- if (buffer[3]) p = buffer + strlen(buffer);
- else p = buffer + 2;
+ *p++ = '\\';
+ lstrcpyn( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
+ if (*p) p += strlen(p); else p--;
}
+ *p = '\0';
len = MAX_PATHNAME_LEN - (int)(p - buffer);
while (*name)
@@ -616,10 +618,10 @@
name++;
len--;
}
+ *p = '\0';
}
while ((*name == '\\') || (*name == '/')) name++;
}
- *p = '\0';
if (!buffer[2])
{
buffer[2] = '\\';
@@ -639,10 +641,12 @@
int DOSFS_FindNext( const char *path, const char *mask, int drive,
BYTE attr, int skip, DOS_DIRENT *entry )
{
- DIR *dir;
+ static DIR *dir = NULL;
struct dirent *dirent;
int count = 0;
- char buffer[MAX_PATHNAME_LEN], *p;
+ static char buffer[MAX_PATHNAME_LEN];
+ static int cur_pos = 0;
+ char *p;
const char *hash_name;
if ((attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
@@ -656,8 +660,17 @@
return 1;
}
- if (!(dir = opendir( path ))) return 0;
- strcpy( buffer, path );
+ /* Check the cached directory */
+ if (dir && !strcmp( buffer, path ) && (cur_pos <= skip)) skip -= cur_pos;
+ else /* Not in the cache, open it anew */
+ {
+ dprintf_dosfs( stddeb, "DOSFS_FindNext: cache miss, path=%s skip=%d buf=%s cur=%d\n",
+ path, skip, buffer, cur_pos );
+ cur_pos = skip;
+ if (dir) closedir(dir);
+ if (!(dir = opendir( path ))) return 0;
+ lstrcpyn( buffer, path, sizeof(buffer) - 1 );
+ }
strcat( buffer, "/" );
p = buffer + strlen(buffer);
attr |= FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
@@ -668,7 +681,7 @@
count++;
hash_name = DOSFS_Hash( dirent->d_name, TRUE );
if (!DOSFS_Match( mask, hash_name )) continue;
- strcpy( p, dirent->d_name );
+ lstrcpyn( p, dirent->d_name, sizeof(buffer) - (int)(p - buffer) );
if (!FILE_Stat( buffer, &entry->attr, &entry->size,
&entry->date, &entry->time ))
@@ -681,9 +694,11 @@
lstrcpyn( entry->unixname, dirent->d_name, sizeof(entry->unixname) );
dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s %02x %ld\n",
entry->name, entry->attr, entry->size );
- closedir( dir );
+ cur_pos += count;
+ p[-1] = '\0'; /* Remove trailing slash in buffer */
return count;
}
closedir( dir );
+ dir = NULL;
return 0; /* End of directory */
}
diff --git a/files/drive.c b/files/drive.c
index ae2558d..5c72a54 100644
--- a/files/drive.c
+++ b/files/drive.c
@@ -1,5 +1,5 @@
/*
- * DOS drive handling functions
+ * DOS drives handling functions
*
* Copyright 1993 Erik Bos
* Copyright 1996 Alexandre Julliard
@@ -7,12 +7,26 @@
#include <string.h>
#include <stdlib.h>
+#include <sys/stat.h>
+
+#if defined(__linux__) || defined(sun)
+#include <sys/vfs.h>
+#endif
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/errno.h>
+#endif
+#ifdef __svr4__
+#include <sys/statfs.h>
+#endif
#include "windows.h"
#include "dos_fs.h"
#include "drive.h"
#include "file.h"
#include "msdos.h"
+#include "options.h"
#include "task.h"
#include "xmalloc.h"
#include "stddebug.h"
@@ -20,74 +34,128 @@
typedef struct
{
- char *root; /* root dir in Unix format without trailing '/' */
- char *dos_cwd; /* cwd in DOS format without leading or trailing '\' */
- char *unix_cwd; /* cwd in Unix format without leading or trailing '/' */
- char label[12]; /* drive label */
- DWORD serial; /* drive serial number */
- WORD type; /* drive type */
- BYTE disabled; /* disabled flag */
+ char *root; /* root dir in Unix format without trailing / */
+ char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
+ char *unix_cwd; /* cwd in Unix format without leading or trailing / */
+ char label[12]; /* drive label */
+ DWORD serial; /* drive serial number */
+ DRIVETYPE type; /* drive type */
+ BYTE disabled; /* disabled flag */
} DOSDRIVE;
+
+static const char *DRIVE_Types[] =
+{
+ "floppy", /* TYPE_FLOPPY */
+ "hd", /* TYPE_HD */
+ "cdrom", /* TYPE_CDROM */
+ "network" /* TYPE_NETWORK */
+};
+
+
static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
-static int DRIVE_CurDrive = 0;
+static int DRIVE_CurDrive = -1;
static HTASK DRIVE_LastTask = 0;
+
+/***********************************************************************
+ * DRIVE_GetDriveType
+ */
+static DRIVETYPE DRIVE_GetDriveType( const char *name )
+{
+ char buffer[20];
+ int i;
+
+ PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
+ for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
+ {
+ if (!lstrcmpi( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
+ }
+ fprintf( stderr, "%s: unknown type '%s', defaulting to 'hd'.\n",
+ name, buffer );
+ return TYPE_HD;
+}
+
+
/***********************************************************************
* DRIVE_Init
*/
int DRIVE_Init(void)
{
- int i, count = 0;
- char drive[2] = "A";
+ int i, len, count = 0;
+ char name[] = "Drive A";
char path[MAX_PATHNAME_LEN];
+ char buffer[20];
char *p;
+ DOSDRIVE *drive;
- for (i = 0; i < MAX_DOS_DRIVES; i++, drive[0]++)
+ for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
{
- GetPrivateProfileString( "drives", drive, "",
- path, sizeof(path)-1, WineIniFileName() );
+ PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
if (path[0])
{
p = path + strlen(path) - 1;
while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
- DOSDrives[i].root = xstrdup( path );
- DOSDrives[i].dos_cwd = xstrdup( "" );
- DOSDrives[i].unix_cwd = xstrdup( "" );
- sprintf( DOSDrives[i].label, "DRIVE-%c ", drive[0] );
- DOSDrives[i].serial = 0x12345678;
- DOSDrives[i].type = (i < 2) ? DRIVE_REMOVABLE : DRIVE_FIXED;
- DOSDrives[i].disabled = 0;
+ drive->root = xstrdup( path );
+ drive->dos_cwd = xstrdup( "" );
+ drive->unix_cwd = xstrdup( "" );
+ drive->type = DRIVE_GetDriveType( name );
+ drive->disabled = 0;
+
+ /* Get the drive label */
+ PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
+ if ((len = strlen(drive->label)) < 11)
+ {
+ /* Pad label with spaces */
+ memset( drive->label + len, ' ', 11 - len );
+ drive->label[12] = '\0';
+ }
+
+ /* Get the serial number */
+ PROFILE_GetWineIniString( name, "Serial", "12345678",
+ buffer, sizeof(buffer) );
+ drive->serial = strtoul( buffer, NULL, 16 );
+
+ /* Make the first hard disk the current drive */
+ if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
+ DRIVE_CurDrive = i;
+
count++;
+ dprintf_dosfs( stddeb, "%s: path=%s type=%s label='%s' serial=%08lx\n",
+ name, path, DRIVE_Types[drive->type],
+ drive->label, drive->serial );
}
- dprintf_dosfs( stddeb, "Drive %c -> %s\n", 'A' + i,
- path[0] ? path : "** None **" );
+ else dprintf_dosfs( stddeb, "%s: not defined\n", name );
}
if (!count)
{
fprintf( stderr, "Warning: no valid DOS drive found\n" );
/* Create a C drive pointing to Unix root dir */
- DOSDrives[i].root = xstrdup( "/" );
- DOSDrives[i].dos_cwd = xstrdup( "" );
- DOSDrives[i].unix_cwd = xstrdup( "" );
- sprintf( DOSDrives[i].label, "DRIVE-%c ", drive[0] );
- DOSDrives[i].serial = 0x12345678;
- DOSDrives[i].type = DRIVE_FIXED;
- DOSDrives[i].disabled = 0;
+ DOSDrives[2].root = xstrdup( "/" );
+ DOSDrives[2].dos_cwd = xstrdup( "" );
+ DOSDrives[2].unix_cwd = xstrdup( "" );
+ strcpy( DOSDrives[2].label, "Drive C " );
+ DOSDrives[2].serial = 0x12345678;
+ DOSDrives[2].type = TYPE_HD;
+ DOSDrives[2].disabled = 0;
+ DRIVE_CurDrive = 2;
}
- /* Make the first hard disk the current drive */
- for (i = 0; i < MAX_DOS_DRIVES; i++, drive[0]++)
+ /* Make sure the current drive is valid */
+ if (DRIVE_CurDrive == -1)
{
- if (DOSDrives[i].root && !DOSDrives[i].disabled &&
- DOSDrives[i].type != DRIVE_REMOVABLE)
+ for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
{
- DRIVE_CurDrive = i;
- break;
+ if (drive->root && !drive->disabled)
+ {
+ DRIVE_CurDrive = i;
+ break;
+ }
}
}
+
return 1;
}
@@ -143,7 +211,7 @@
*/
int DRIVE_FindDriveRoot( const char **path )
{
- int drive;
+ int drive, rootdrive = -1;
const char *p1, *p2;
dprintf_dosfs( stddeb, "DRIVE_FindDriveRoot: searching '%s'\n", *path );
@@ -154,6 +222,13 @@
p2 = DOSDrives[drive].root;
dprintf_dosfs( stddeb, "DRIVE_FindDriveRoot: checking %c: '%s'\n",
'A' + drive, p2 );
+
+ while (*p2 == '/') p2++;
+ if (!*p2)
+ {
+ rootdrive = drive;
+ continue; /* Look if there's a better match */
+ }
for (;;)
{
while ((*p1 == '\\') || (*p1 == '/')) p1++;
@@ -175,7 +250,7 @@
break; /* No match, go to next drive */
}
}
- return -1;
+ return rootdrive;
}
@@ -263,6 +338,16 @@
/***********************************************************************
+ * DRIVE_GetType
+ */
+DRIVETYPE DRIVE_GetType( int drive )
+{
+ if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
+ return DOSDrives[drive].type;
+}
+
+
+/***********************************************************************
* DRIVE_Chdir
*/
int DRIVE_Chdir( int drive, const char *path )
@@ -339,11 +424,52 @@
/***********************************************************************
+ * DRIVE_GetFreeSpace
+ */
+int DRIVE_GetFreeSpace( int drive, DWORD *size, DWORD *available )
+{
+ struct statfs info;
+
+ if (!DRIVE_IsValid(drive))
+ {
+ DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
+ return 0;
+ }
+
+#ifdef __svr4__
+ if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
+#else
+ if (statfs( DOSDrives[drive].root, &info) < 0)
+#endif
+ {
+ FILE_SetDosError();
+ fprintf(stderr,"dosfs: cannot do statfs(%s)\n", DOSDrives[drive].root);
+ return 0;
+ }
+
+ *size = info.f_bsize * info.f_blocks;
+#ifdef __svr4__
+ *available = info.f_bfree * info.f_bsize;
+#else
+ *available = info.f_bavail * info.f_bsize;
+#endif
+ return 1;
+}
+
+
+/***********************************************************************
* GetDriveType (KERNEL.136)
*/
WORD GetDriveType( INT drive )
{
dprintf_dosfs( stddeb, "GetDriveType(%c:)\n", 'A' + drive );
- if (!DRIVE_IsValid(drive)) return 0;
- return DOSDrives[drive].type;
+ switch(DRIVE_GetType(drive))
+ {
+ case TYPE_FLOPPY: return DRIVE_REMOVABLE;
+ case TYPE_HD: return DRIVE_FIXED;
+ case TYPE_CDROM: return DRIVE_REMOVABLE;
+ case TYPE_NETWORK: return DRIVE_REMOTE;
+ case TYPE_INVALID:
+ default: return DRIVE_CANNOTDETERMINE;
+ }
}
diff --git a/files/file.c b/files/file.c
index 19cc6fb..3d00a25 100644
--- a/files/file.c
+++ b/files/file.c
@@ -34,6 +34,7 @@
*/
void FILE_SetDosError(void)
{
+ dprintf_file(stddeb, "FILE_SetDosError: errno = %d\n", errno );
switch (errno)
{
case EAGAIN:
@@ -44,18 +45,18 @@
break;
case ENOSPC:
DOS_ERROR( ER_DiskFull, EC_MediaError, SA_Abort, EL_Disk );
- break;
+ break;
case EACCES:
case EPERM:
case EROFS:
- DOS_ERROR( ER_WriteProtected, EC_AccessDenied, SA_Abort, EL_Disk );
+ DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
break;
case EBUSY:
DOS_ERROR( ER_LockViolation, EC_AccessDenied, SA_Abort, EL_Disk );
- break;
+ break;
case ENOENT:
DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
- break;
+ break;
case EISDIR:
DOS_ERROR( ER_CanNotMakeDir, EC_AccessDenied, SA_Abort, EL_Unknown );
break;
@@ -65,7 +66,7 @@
break;
case EEXIST:
DOS_ERROR( ER_FileExists, EC_Exists, SA_Abort, EL_Disk );
- break;
+ break;
default:
perror( "int21: unknown errno" );
DOS_ERROR( ER_GeneralFailure, EC_SystemFailure, SA_Abort, EL_Unknown );
@@ -98,6 +99,9 @@
return -1;
}
*fp = (BYTE)handle;
+ dprintf_file(stddeb,
+ "FILE_AllocTaskHandle: returning %d for handle %d file %d of %d \n",
+ (fp - files),handle,pdb->nbFiles - i, pdb->nbFiles );
return (HFILE)(fp - files);
}
@@ -118,6 +122,8 @@
exit(1);
}
files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
+ dprintf_file( stddeb,"FILE_FreeTaskHandle: dos=%d unix=%d\n",
+ handle, files[handle]);
if ((handle<0) || (handle >= (INT)pdb->nbFiles) || (files[handle] == 0xff))
{
fprintf( stderr, "FILE_FreeTaskHandle: invalid file handle %d\n",
@@ -166,6 +172,7 @@
if (!pdb) return;
files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
+ fprintf(stderr,"FILE_CloseAllFiles: closing %d files\n",pdb->nbFiles);
for (count = pdb->nbFiles; count > 0; count--, files++)
{
if (*files != 0xff)
@@ -178,12 +185,46 @@
/***********************************************************************
+ * FILE_OpenUnixFile
+ */
+static int FILE_OpenUnixFile( const char *name, int mode )
+{
+ int handle;
+ struct stat st;
+
+ if ((handle = open( name, mode )) == -1)
+ {
+ if (Options.allowReadOnly && (mode == O_RDWR))
+ {
+ if ((handle = open( name, O_RDONLY )) != -1)
+ fprintf( stderr, "Warning: could not open %s for writing, opening read-only.\n", name );
+ }
+ }
+ if (handle != -1) /* Make sure it's not a directory */
+ {
+ if ((fstat( handle, &st ) == -1))
+ {
+ FILE_SetDosError();
+ close( handle );
+ handle = -1;
+ }
+ else if (S_ISDIR(st.st_mode))
+ {
+ DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
+ close( handle );
+ handle = -1;
+ }
+ }
+ return handle;
+}
+
+
+/***********************************************************************
* FILE_Open
*/
int FILE_Open( LPCSTR path, int mode )
{
const char *unixName;
- int handle;
dprintf_file(stddeb, "FILE_Open: '%s' %04x\n", path, mode );
if ((unixName = DOSFS_IsDevice( path )) != NULL)
@@ -191,26 +232,13 @@
dprintf_file( stddeb, "FILE_Open: opening device '%s'\n", unixName );
if (!unixName[0]) /* Non-existing device */
{
+ dprintf_file(stddeb, "FILE_Open: Non-existing device\n");
DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
return -1;
}
- handle = open( unixName, mode );
}
- else
- {
- if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return -1;
-
- if ((handle = open( unixName, mode )) == -1)
- {
- if (Options.allowReadOnly && (mode == O_RDWR))
- {
- if ((handle = open( unixName, O_RDONLY )) != -1)
- fprintf( stderr, "Warning: could not open %s for writing, opening read-only.\n", unixName );
- }
- }
- }
- if (handle == -1) FILE_SetDosError();
- return handle;
+ else if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return -1;
+ return FILE_OpenUnixFile( unixName, mode );
}
@@ -327,7 +355,7 @@
DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
return 0;
}
- if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return 0;
+ if (!(unixName = DOSFS_GetUnixFileName( path, FALSE ))) return 0;
if ((mkdir( unixName, 0777 ) == -1) && (errno != EEXIST))
{
FILE_SetDosError();
@@ -373,6 +401,7 @@
HFILE dosHandle;
if ((handle = FILE_GetUnixHandle( hFile )) == -1) return HFILE_ERROR;
+ dprintf_file( stddeb, "FILE_Dup for handle %d\n",handle);
if ((newhandle = dup(handle)) == -1)
{
FILE_SetDosError();
@@ -380,6 +409,7 @@
}
if ((dosHandle = FILE_AllocTaskHandle( newhandle )) == HFILE_ERROR)
close( newhandle );
+ dprintf_file( stddeb, "FILE_Dup return handle %d\n",dosHandle);
return dosHandle;
}
@@ -396,6 +426,7 @@
int handle, newhandle;
if ((handle = FILE_GetUnixHandle( hFile1 )) == -1) return HFILE_ERROR;
+ dprintf_file( stddeb, "FILE_Dup2 for handle %d\n",handle);
if ((hFile2 < 0) || (hFile2 >= (INT)pdb->nbFiles))
{
DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
@@ -414,8 +445,14 @@
return HFILE_ERROR;
}
files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
- if (files[hFile2] != 0xff) close( files[hFile2] );
+ if (files[hFile2] != 0xff)
+ {
+ dprintf_file( stddeb, "FILE_Dup2 closing old handle2 %d\n",
+ files[hFile2]);
+ close( files[hFile2] );
+ }
files[hFile2] = (BYTE)newhandle;
+ dprintf_file( stddeb, "FILE_Dup2 return handle2 %d\n",newhandle);
return hFile2;
}
@@ -435,7 +472,7 @@
ofs->cBytes = sizeof(OFSTRUCT);
ofs->nErrCode = 0;
if (mode & OF_REOPEN) name = ofs->szPathName;
- dprintf_file( stddeb, "Openfile: %s %04x\n", name, mode );
+ dprintf_file( stddeb, "FILE_Openfile: %s %04x\n", name, mode );
/* OF_PARSE simply fills the structure */
@@ -444,10 +481,12 @@
if (!(dosName = DOSFS_GetDosTrueName( name, FALSE )))
{
ofs->nErrCode = DOS_ExtendedError;
+ dprintf_file( stddeb, "FILE_Openfile: %s return = -1\n", name);
return -1;
}
lstrcpyn( ofs->szPathName, dosName, sizeof(ofs->szPathName) );
ofs->fFixedDisk = (GetDriveType( dosName[0]-'A' ) != DRIVE_REMOVABLE);
+ dprintf_file( stddeb, "FILE_Openfile: %s return = 0\n", name);
return 0;
}
@@ -459,18 +498,21 @@
if ((unixName = DOSFS_GetUnixFileName( name, FALSE )) == NULL)
{
ofs->nErrCode = DOS_ExtendedError;
+ dprintf_file( stddeb, "FILE_Openfile: %s return = -1\n", name);
return -1;
}
- dprintf_file( stddeb, "OpenFile: creating '%s'\n", unixName );
+ dprintf_file( stddeb, "FILE_OpenFile: creating '%s'\n", unixName );
handle = open( unixName, O_TRUNC | O_RDWR | O_CREAT, 0666 );
if (handle == -1)
{
FILE_SetDosError();
ofs->nErrCode = DOS_ExtendedError;
+ dprintf_file( stddeb, "FILE_Openfile: %s return = -1\n", name);
return -1;
}
lstrcpyn( ofs->szPathName, DOSFS_GetDosTrueName( name, FALSE ),
sizeof(ofs->szPathName) );
+ dprintf_file( stddeb, "FILE_Openfile: %s return = %d \n", name, handle);
return handle;
}
@@ -540,21 +582,26 @@
}
not_found:
- dprintf_file( stddeb, "OpenFile: '%s' not found\n", name );
+ dprintf_file( stddeb, "FILE_OpenFile: '%s' not found\n", name );
DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
ofs->nErrCode = ER_FileNotFound;
+ dprintf_file( stddeb, "FILE_Openfile: %s return =-1\n", name);
return -1;
found:
- dprintf_file( stddeb, "OpenFile: found '%s'\n", unixName );
+ dprintf_file( stddeb, "FILE_OpenFile: found '%s'\n", unixName );
lstrcpyn( ofs->szPathName, DOSFS_GetDosTrueName( ofs->szPathName, FALSE ),
sizeof(ofs->szPathName) );
- if (mode & OF_PARSE) return 0;
-
+ if (mode & OF_PARSE)
+ {
+ dprintf_file( stddeb, "FILE_Openfile: %s return = 0\n", name);
+ return 0;
+ }
if (mode & OF_DELETE)
{
if (unlink( unixName ) == -1) goto not_found;
+ dprintf_file( stddeb, "FILE_Openfile: %s return = 0\n", name);
return 0;
}
@@ -569,27 +616,24 @@
unixMode = O_RDONLY; break;
}
- if ((handle = open( unixName, unixMode )) == -1)
- {
- if (Options.allowReadOnly && (unixMode == O_RDWR))
- {
- if ((handle = open( unixName, O_RDONLY )) != -1)
- fprintf( stderr, "Warning: could not open %s for writing, opening read-only.\n", unixName );
- }
- }
- if (handle == -1) goto not_found;
+ if ((handle = FILE_OpenUnixFile( unixName, unixMode )) == -1)
+ goto not_found;
if (fstat( handle, &st ) != -1)
{
if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
{
if (memcmp( ofs->reserved, &st.st_mtime, sizeof(ofs->reserved) ))
+ {
+ dprintf_file( stddeb, "FILE_Openfile: %s return = -1\n", name);
return -1;
+ }
}
memcpy( ofs->reserved, &st.st_mtime, sizeof(ofs->reserved) );
}
if (mode & OF_EXIST) close( handle );
+ dprintf_file( stddeb, "FILE_Openfile: %s return = %d\n", name,handle);
return handle;
}
@@ -654,6 +698,7 @@
int unixHandle;
HFILE handle;
+ dprintf_file( stddeb, "OpenFile %s \n",name);
if ((unixHandle = FILE_OpenFile( name, ofs, mode )) == -1)
return HFILE_ERROR;
if ((handle = FILE_AllocTaskHandle( unixHandle )) == HFILE_ERROR)
@@ -673,9 +718,9 @@
{
int handle;
- dprintf_file( stddeb, "_lclose: handle %d\n", hFile );
if ((handle = FILE_GetUnixHandle( hFile )) == -1) return HFILE_ERROR;
+ dprintf_file( stddeb, "_lclose: doshandle %d unixhandle %d\n", hFile,handle );
if (handle <= 2)
{
fprintf( stderr, "_lclose: internal error: closing handle %d\n", handle );
@@ -738,7 +783,7 @@
LONG _llseek( HFILE hFile, LONG lOffset, INT nOrigin )
{
int handle, origin, result;
-
+
dprintf_file( stddeb, "_llseek: handle %d, offset %ld, origin %d\n",
hFile, lOffset, nOrigin);
@@ -805,10 +850,10 @@
dprintf_file( stddeb, "_hread: %d %p %ld\n", hFile, buffer, count );
- if ((handle = FILE_GetUnixHandle( hFile )) == -1) return HFILE_ERROR;
+ if ((handle = FILE_GetUnixHandle( hFile )) == -1) return -1;
if (!count) return 0;
if ((result = read( handle, buffer, count )) == -1) FILE_SetDosError();
- return (result == -1) ? HFILE_ERROR : result;
+ return result;
}
diff --git a/files/profile.c b/files/profile.c
new file mode 100644
index 0000000..50816e1
--- /dev/null
+++ b/files/profile.c
@@ -0,0 +1,686 @@
+/*
+ * Profile functions
+ *
+ * Copyright 1993 Miguel de Icaza
+ * Copyright 1996 Alexandre Julliard
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "dos_fs.h"
+#include "windows.h"
+#include "xmalloc.h"
+#include "stddebug.h"
+#include "debug.h"
+
+typedef struct tagPROFILEKEY
+{
+ char *name;
+ char *value;
+ struct tagPROFILEKEY *next;
+} PROFILEKEY;
+
+typedef struct tagPROFILESECTION
+{
+ char *name;
+ struct tagPROFILEKEY *key;
+ struct tagPROFILESECTION *next;
+} PROFILESECTION;
+
+
+typedef struct
+{
+ int changed;
+ PROFILESECTION *section;
+ char *dos_name;
+} PROFILE;
+
+
+/* Cached profile file */
+static PROFILE CurProfile = { FALSE, NULL, NULL };
+
+/* wine.ini profile content */
+static PROFILESECTION *WineProfile;
+
+#define PROFILE_MAX_LINE_LEN 1024
+
+/* Wine profile name in $HOME directory; must begin with slash */
+static const char PROFILE_WineIniName[] = "/.winerc";
+
+/* Check for comments in profile */
+#define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
+
+
+/***********************************************************************
+ * PROFILE_CopyEntry
+ *
+ * Copy the content of an entry into a buffer, removing quotes, and possibly
+ * translating environment variables.
+ */
+static void PROFILE_CopyEntry( char *buffer, const char *value, int len,
+ int handle_env )
+{
+ char quote = '\0';
+ const char *p;
+
+ if ((*value == '\'') || (*value == '\"'))
+ {
+ if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++;
+ }
+
+ if (!handle_env)
+ {
+ lstrcpyn( buffer, value, len );
+ if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0';
+ return;
+ }
+
+ for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
+ {
+ if ((*p == '$') && (p[1] == '{'))
+ {
+ char env_val[1024];
+ const char *env_p;
+ const char *p2 = strchr( p, '}' );
+ if (!p2) continue; /* ignore it */
+ lstrcpyn( env_val, p + 2, MIN( sizeof(env_val), (int)(p2-p)-1 ) );
+ if ((env_p = getenv( env_val )) != NULL)
+ {
+ lstrcpyn( buffer, env_p, len );
+ buffer += strlen( buffer );
+ len -= strlen( buffer );
+ }
+ p = p2 + 1;
+ }
+ }
+ *buffer = '\0';
+}
+
+
+/***********************************************************************
+ * PROFILE_Save
+ *
+ * Save a profile tree to a file.
+ */
+static void PROFILE_Save( FILE *file, PROFILESECTION *section )
+{
+ PROFILEKEY *key;
+
+ for ( ; section; section = section->next)
+ {
+ if (section->name) fprintf( file, "[%s]\n", section->name );
+ for (key = section->key; key; key = key->next)
+ {
+ fprintf( file, "%s", key->name );
+ if (key->value) fprintf( file, "=%s", key->value );
+ fprintf( file, "\n" );
+ }
+ }
+}
+
+
+/***********************************************************************
+ * PROFILE_Free
+ *
+ * Free a profile tree.
+ */
+static void PROFILE_Free( PROFILESECTION *section )
+{
+ PROFILESECTION *next_section;
+ PROFILEKEY *key, *next_key;
+
+ for ( ; section; section = next_section)
+ {
+ if (section->name) free( section->name );
+ for (key = section->key; key; key = next_key)
+ {
+ next_key = key->next;
+ if (key->name) free( key->name );
+ if (key->value) free( key->value );
+ free( key );
+ }
+ next_section = section->next;
+ free( section );
+ }
+}
+
+
+/***********************************************************************
+ * PROFILE_Load
+ *
+ * Load a profile tree from a file.
+ */
+static PROFILESECTION *PROFILE_Load( FILE *file )
+{
+ char buffer[PROFILE_MAX_LINE_LEN];
+ char *p, *p2;
+ int line = 0;
+ PROFILESECTION *section, *first_section;
+ PROFILESECTION **prev_section;
+ PROFILEKEY *key, **prev_key;
+
+ first_section = (PROFILESECTION *)xmalloc( sizeof(*section) );
+ first_section->name = NULL;
+ first_section->key = NULL;
+ first_section->next = NULL;
+ prev_section = &first_section->next;
+ prev_key = &first_section->key;
+
+ while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
+ {
+ line++;
+ p = buffer + strlen(buffer) - 1;
+ while ((p > buffer) && ((*p == '\n') || isspace(*p))) *p-- = '\0';
+ p = buffer;
+ while (*p && isspace(*p)) p++;
+ if (*p == '[') /* section start */
+ {
+ if (!(p2 = strrchr( p, ']' )))
+ {
+ fprintf( stderr, "PROFILE_Load: Invalid section header at line %d: '%s'\n",
+ line, p );
+ }
+ else
+ {
+ *p2 = '\0';
+ p++;
+ section = (PROFILESECTION *)xmalloc( sizeof(*section));
+ section->name = xstrdup( p );
+ section->key = NULL;
+ section->next = NULL;
+ *prev_section = section;
+ prev_section = §ion->next;
+ prev_key = §ion->key;
+ continue;
+ }
+ }
+ if ((p2 = strchr( p, '=' )) != NULL)
+ {
+ char *p3 = p2 - 1;
+ while ((p3 > p) && isspace(*p3)) *p3-- = '\0';
+ *p2++ = '\0';
+ while (*p2 && isspace(*p2)) p2++;
+ }
+ key = (PROFILEKEY *)xmalloc( sizeof(*key) );
+ key->name = xstrdup( p );
+ key->value = p2 ? xstrdup( p2 ) : NULL;
+ key->next = NULL;
+ *prev_key = key;
+ prev_key = &key->next;
+ }
+ if (debugging_profile)
+ {
+ fprintf( stddeb, "PROFILE_Load:\n" );
+ PROFILE_Save( stddeb, first_section );
+ fprintf( stddeb, "PROFILE_Load finished.\n" );
+ }
+ return first_section;
+}
+
+
+/***********************************************************************
+ * PROFILE_DeleteSection
+ *
+ * Delete a section from a profile tree.
+ */
+static BOOL PROFILE_DeleteSection( PROFILESECTION **section, const char *name )
+{
+ while (*section)
+ {
+ if ((*section)->name && !lstrcmpi( (*section)->name, name ))
+ {
+ PROFILESECTION *to_del = *section;
+ *section = to_del->next;
+ to_del->next = NULL;
+ PROFILE_Free( to_del );
+ return TRUE;
+ }
+ section = &(*section)->next;
+ }
+ return FALSE;
+}
+
+
+/***********************************************************************
+ * PROFILE_DeleteKey
+ *
+ * Delete a key from a profile tree.
+ */
+static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
+ const char *section_name, const char *key_name )
+{
+ while (*section)
+ {
+ if ((*section)->name && !lstrcmpi( (*section)->name, section_name ))
+ {
+ PROFILEKEY **key = &(*section)->key;
+ while (*key)
+ {
+ if (!lstrcmpi( (*key)->name, key_name ))
+ {
+ PROFILEKEY *to_del = *key;
+ *key = to_del->next;
+ if (to_del->name) free( to_del->name );
+ if (to_del->value) free( to_del->value );
+ free( to_del );
+ return TRUE;
+ }
+ key = &(*key)->next;
+ }
+ }
+ section = &(*section)->next;
+ }
+ return FALSE;
+}
+
+
+/***********************************************************************
+ * PROFILE_Find
+ *
+ * Find a key in a profile tree, optionally creating it.
+ */
+static PROFILEKEY *PROFILE_Find( PROFILESECTION **section,
+ const char *section_name,
+ const char *key_name, int create )
+{
+ while (*section)
+ {
+ if ((*section)->name && !lstrcmpi( (*section)->name, section_name ))
+ {
+ PROFILEKEY **key = &(*section)->key;
+ while (*key)
+ {
+ if (!lstrcmpi( (*key)->name, key_name )) return *key;
+ key = &(*key)->next;
+ }
+ if (!create) return NULL;
+ *key = (PROFILEKEY *)xmalloc( sizeof(PROFILEKEY) );
+ (*key)->name = xstrdup( key_name );
+ (*key)->value = NULL;
+ (*key)->next = NULL;
+ return *key;
+ }
+ section = &(*section)->next;
+ }
+ if (!create) return NULL;
+ *section = (PROFILESECTION *)xmalloc( sizeof(PROFILESECTION) );
+ (*section)->name = xstrdup(section_name);
+ (*section)->next = NULL;
+ (*section)->key = (PROFILEKEY *)xmalloc( sizeof(PROFILEKEY) );
+ (*section)->key->name = xstrdup( key_name );
+ (*section)->key->value = NULL;
+ (*section)->key->next = NULL;
+ return (*section)->key;
+}
+
+
+/***********************************************************************
+ * PROFILE_FlushFile
+ *
+ * Flush the current profile to disk if changed.
+ */
+static BOOL PROFILE_FlushFile(void)
+{
+ char *p, buffer[MAX_PATHNAME_LEN];
+ const char *unix_name;
+ FILE *file = NULL;
+
+ if (!CurProfile.changed || !CurProfile.dos_name) return TRUE;
+ if (!(unix_name = DOSFS_GetUnixFileName( CurProfile.dos_name, FALSE )) ||
+ !(file = fopen( unix_name, "w" )))
+ {
+ /* Try to create it in $HOME/.wine */
+ /* FIXME: this will need a more general solution */
+ if ((p = getenv( "HOME" )) != NULL)
+ {
+ strcpy( buffer, p );
+ strcat( buffer, "/.wine/" );
+ p = buffer + strlen(buffer);
+ strcpy( p, strrchr( CurProfile.dos_name, '\\' ) + 1 );
+ AnsiLower( p );
+ file = fopen( buffer, "w" );
+ unix_name = buffer;
+ }
+ }
+
+ if (!file)
+ {
+ fprintf( stderr, "Warning: could not save profile file %s\n",
+ CurProfile.dos_name );
+ return FALSE;
+ }
+
+ dprintf_profile( stddeb, "Saving '%s' into '%s'\n",
+ CurProfile.dos_name, unix_name );
+ PROFILE_Save( file, CurProfile.section );
+ fclose( file );
+ CurProfile.changed = FALSE;
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * PROFILE_Open
+ *
+ * Open a profile file, checking the cached file first.
+ */
+static BOOL PROFILE_Open( const char *filename )
+{
+ char buffer[MAX_PATHNAME_LEN];
+ const char *dos_name, *unix_name;
+ char *newdos_name, *p;
+ FILE *file = NULL;
+
+ if (strchr( filename, '/' ) || strchr( filename, '\\' ) ||
+ strchr( filename, ':' ))
+ {
+ if (!(dos_name = DOSFS_GetDosTrueName( filename, FALSE))) return FALSE;
+ }
+ else
+ {
+ GetWindowsDirectory( buffer, sizeof(buffer) );
+ strcat( buffer, "\\" );
+ strcat( buffer, filename );
+ if (!(dos_name = DOSFS_GetDosTrueName( buffer, FALSE ))) return FALSE;
+ }
+ if (CurProfile.dos_name && !strcmp( dos_name, CurProfile.dos_name ))
+ {
+ dprintf_profile( stddeb, "PROFILE_Open(%s): already opened\n",
+ filename );
+ return TRUE;
+ }
+
+ /* Flush the previous profile */
+
+ newdos_name = xstrdup( dos_name );
+ PROFILE_FlushFile();
+ PROFILE_Free( CurProfile.section );
+ if (CurProfile.dos_name) free( CurProfile.dos_name );
+ CurProfile.section = NULL;
+ CurProfile.dos_name = newdos_name;
+
+ /* Try to open the profile file, first in $HOME/.wine */
+
+ /* FIXME: this will need a more general solution */
+ if ((p = getenv( "HOME" )) != NULL)
+ {
+ strcpy( buffer, p );
+ strcat( buffer, "/.wine/" );
+ p = buffer + strlen(buffer);
+ strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
+ AnsiLower( p );
+ if ((file = fopen( buffer, "r" )))
+ dprintf_profile( stddeb, "Found it in %s\n", buffer );
+ }
+
+ if (!file && ((unix_name = DOSFS_GetUnixFileName( dos_name, TRUE ))))
+ {
+ if ((file = fopen( unix_name, "r" )))
+ dprintf_profile( stddeb, "Found it in %s\n", unix_name );
+ }
+
+ if (file)
+ {
+ CurProfile.section = PROFILE_Load( file );
+ fclose( file );
+ }
+ else
+ fprintf( stderr, "Warning: profile file %s not found\n", newdos_name );
+ dprintf_profile( stddeb, "PROFILE_Open(%s): successful\n", filename );
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * PROFILE_GetSection
+ *
+ * Enumerate all the keys of a section.
+ */
+static INT PROFILE_GetSection( PROFILESECTION *section,
+ const char *section_name,
+ char *buffer, INT len, int handle_env )
+{
+ PROFILEKEY *key;
+ while (section)
+ {
+ if (section->name && !lstrcmpi( section->name, section_name ))
+ {
+ for (key = section->key; key; key = key->next)
+ {
+ if (len <= 2) break;
+ if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
+ PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
+ len -= strlen(buffer) - 1;
+ buffer += strlen(buffer) + 1;
+ }
+ *buffer = '\0';
+ return len - 1;
+ }
+ section = section->next;
+ }
+ buffer[0] = buffer[1] = '\0';
+ return len - 2;
+}
+
+
+/***********************************************************************
+ * PROFILE_GetString
+ *
+ * Get a profile string.
+ */
+static INT PROFILE_GetString( const char *section, const char *key_name,
+ const char *def_val, char *buffer, INT len )
+{
+ PROFILEKEY *key = NULL;
+
+ if (!def_val) def_val = "";
+ if (key_name)
+ {
+ key = PROFILE_Find( &CurProfile.section, section, key_name, FALSE );
+ PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
+ len, FALSE );
+ dprintf_profile( stddeb, "PROFILE_GetString('%s','%s','%s'): returning '%s'\n",
+ section, key_name, def_val, buffer );
+ return strlen( buffer );
+ }
+ return PROFILE_GetSection(CurProfile.section, section, buffer, len, FALSE);
+}
+
+
+/***********************************************************************
+ * PROFILE_SetString
+ *
+ * Set a profile string.
+ */
+static BOOL PROFILE_SetString( const char *section_name, const char *key_name,
+ const char *value )
+{
+ BOOL ret;
+
+ if (!key_name) /* Delete a whole section */
+ {
+ dprintf_profile(stddeb, "PROFILE_DeleteSection('%s')\n", section_name);
+ ret = PROFILE_DeleteSection( &CurProfile.section, section_name );
+ CurProfile.changed |= ret;
+ return ret;
+ }
+ else if (!value) /* Delete a key */
+ {
+ dprintf_profile( stddeb, "PROFILE_DeleteKey('%s','%s')\n",
+ section_name, key_name );
+ ret = PROFILE_DeleteKey( &CurProfile.section, section_name, key_name );
+ CurProfile.changed |= ret;
+ return ret;
+ }
+ else /* Set the key value */
+ {
+ PROFILEKEY *key = PROFILE_Find( &CurProfile.section, section_name,
+ key_name, TRUE );
+ dprintf_profile( stddeb, "PROFILE_SetString('%s','%s','%s'): ",
+ section_name, key_name, value );
+ if (key->value)
+ {
+ if (!strcmp( key->value, value ))
+ {
+ dprintf_profile( stddeb, "no change needed\n" );
+ return TRUE; /* No change needed */
+ }
+ dprintf_profile( stddeb, "replacing '%s'\n", key->value );
+ free( key->value );
+ }
+ else dprintf_profile( stddeb, "creating key\n" );
+ key->value = xstrdup( value );
+ CurProfile.changed = TRUE;
+ return TRUE;
+ }
+}
+
+
+/***********************************************************************
+ * PROFILE_GetWineIniString
+ *
+ * Get a config string from the wine.ini file.
+ */
+int PROFILE_GetWineIniString( const char *section, const char *key_name,
+ const char *def, char *buffer, int len )
+{
+ if (key_name)
+ {
+ PROFILEKEY *key = PROFILE_Find(&WineProfile, section, key_name, FALSE);
+ PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def,
+ len, TRUE );
+ dprintf_profile( stddeb, "PROFILE_GetWineIniString('%s','%s','%s'): returning '%s'\n",
+ section, key_name, def, buffer );
+ return strlen( buffer );
+ }
+ return PROFILE_GetSection( WineProfile, section, buffer, len, TRUE );
+}
+
+
+/***********************************************************************
+ * PROFILE_LoadWineIni
+ *
+ * Load the wine.ini file.
+ */
+int PROFILE_LoadWineIni(void)
+{
+ char buffer[MAX_PATHNAME_LEN];
+ const char *p;
+ FILE *f;
+
+ if ((p = getenv( "HOME" )) != NULL)
+ {
+ lstrcpyn( buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName) );
+ strcat( buffer, PROFILE_WineIniName );
+ if ((f = fopen( buffer, "r" )) != NULL)
+ {
+ WineProfile = PROFILE_Load( f );
+ fclose( f );
+ return 1;
+ }
+ }
+ else fprintf( stderr, "Warning: could not get $HOME value for config file.\n" );
+
+ /* Try global file */
+
+ if ((f = fopen( WINE_INI_GLOBAL, "r" )) != NULL)
+ {
+ WineProfile = PROFILE_Load( f );
+ fclose( f );
+ return 1;
+ }
+ fprintf( stderr, "Can't open configuration file %s or $HOME%s\n",
+ WINE_INI_GLOBAL, PROFILE_WineIniName );
+ return 0;
+}
+
+
+/***********************************************************************
+ * GetProfileInt (KERNEL.57)
+ */
+UINT GetProfileInt( LPCSTR section, LPCSTR entry, INT def_val )
+{
+ return GetPrivateProfileInt( section, entry, def_val, "win.ini" );
+}
+
+
+/***********************************************************************
+ * GetProfileString (KERNEL.58)
+ */
+INT GetProfileString( LPCSTR section, LPCSTR entry, LPCSTR def_val,
+ LPSTR buffer, INT len )
+{
+ return GetPrivateProfileString( section, entry, def_val,
+ buffer, len, "win.ini" );
+}
+
+
+/***********************************************************************
+ * WriteProfileString (KERNEL.59)
+ */
+BOOL WriteProfileString( LPCSTR section, LPCSTR entry, LPCSTR string )
+{
+ return WritePrivateProfileString( section, entry, string, "win.ini" );
+}
+
+
+/***********************************************************************
+ * GetPrivateProfileInt (KERNEL.127)
+ */
+UINT GetPrivateProfileInt( LPCSTR section, LPCSTR entry, INT def_val,
+ LPCSTR filename )
+{
+ char buffer[20];
+ char *p;
+ long result;
+
+ GetPrivateProfileString( section, entry, "",
+ buffer, sizeof(buffer), filename );
+ if (!buffer[0]) return (UINT)def_val;
+ result = strtol( buffer, &p, 0 );
+ if (p == buffer) return 0; /* No digits at all */
+#ifdef WINELIB32
+ return (UINT)result;
+#else
+ if (result > 65535) return 65535;
+ if (result >= 0) return (UINT)result;
+ if (result < -32768) return -32768;
+ return (UINT)(INT)result;
+#endif
+}
+
+
+/***********************************************************************
+ * GetPrivateProfileString (KERNEL.128)
+ */
+INT GetPrivateProfileString( LPCSTR section, LPCSTR entry, LPCSTR def_val,
+ LPSTR buffer, INT len, LPCSTR filename )
+{
+ if (PROFILE_Open( filename ))
+ return PROFILE_GetString( section, entry, def_val, buffer, len );
+ lstrcpyn( buffer, def_val, len );
+ return strlen( buffer );
+}
+
+
+/***********************************************************************
+ * WritePrivateProfileString (KERNEL.129)
+ */
+BOOL WritePrivateProfileString( LPCSTR section, LPCSTR entry, LPCSTR string,
+ LPCSTR filename )
+{
+ if (!PROFILE_Open( filename )) return FALSE;
+ if (!section) return PROFILE_FlushFile();
+ return PROFILE_SetString( section, entry, string );
+}
+
+
+/***********************************************************************
+ * WriteOutProfiles (KERNEL.315)
+ */
+void WriteOutProfiles(void)
+{
+ PROFILE_FlushFile();
+}