Release 970112

Sat Jan 11 18:17:59 1997  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [controls/menu.c]
	Updated to new Win32 types.

	* [controls/listbox.c]
	Fixed Winfile extended selection bug.

	* [files/directory.c]
	Changed DIR_SearchPath to return both long and short file names.

	* [files/dos_fs.c]
	Implemented VFAT ioctl to retrieve the original short filenames
	from a VFAT filesystem (Linux only for now).
	Replaced DOSFS_GetUnixFileName()/DOSFS_GetDosTrueName() by
	DOS_GetFullName().
	Properly implemented GetShortPathName() and GetFullPathName().
	Made all functions re-entrant.

	* [files/file.c] [misc/main.c]
	Replaced -allowreadonly option by -failreadonly. The default is
	now to report success when opening a read-only file for writing.

	* [objects/metafile.c]
	Fixed bug in DIB bitmaps pointer calculation.

	* [scheduler/process.c]
	Implemented environment strings and Get/SetStdHandle with process
 	environment block.

	* [tools/build.c]
	Rewrote BuildContext32() to avoid instructions that may not be
	supported by all assemblers.
	
Fri Jan 10 17:11:09 1997  David Faure  <david.faure@ifhamy.insa-lyon.fr>

	* [windows/event.c]
	Created table keyc2vkey, which associate a vkey(+extended bit) to
	any keycode. Changed EVENT_event_to_vkey to use this table to
	return the correct vkey. Changed EVENT_ToAscii to get the keycode
	from this table too.  Assigned OEM specific vkeys arbitrarily.

Fri Jan 10 09:26:17 1997  John Harvey <john@division.co.uk>

	* [misc/winsock.c] [misc/winsoc_async.c]
        Fixed svr4 header files.
        Changed bzero() to memset().

	* [tools/fnt2bdf.c]
        Removed bcopy() and used memcpy() instead.

	* [debugger/msc.c]
        Include string.h instead of strings.h

	* [debugger/stabs.c]
        Include string.h instead of strings.h.
        Define __ELF__ for svr4 systems.

	* [loader/signal.c]
        Use wait() instead of wait4() which doesnt exist on Unixware.

	* [memory/global.c]
        Use sysconf() instead of getpagesize() for svr4 systems.

Thu Jan  9 21:07:20 1997  Robert Pouliot <krynos@clic.net>

	* [Make.rules.in] [Makefile.in] [make_os2.sh] [rc/Makefile.in]
	  [tools/Makefile.in] [documentation/wine_os2.txt]
	Patches for OS/2 support. Note that it doesn't compile yet.

Tue Jan  7 20:03:53 1997  Eric Youngdale <eric@sub2304.jic.com>

	* [debugger/*]
	Many more debugger improvements (see debugger/README for details).

Tue Jan  7 15:12:21 1997  Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>

	* [windows/graphics.c] [objects/text.c] [graphics/x11drv/*]
	  [graphics/metafiledrv/*]
	Moved some device dependent code into the resp. subdirs.

	* [include/gdi.h] [include/metafiledrv.h] [include/x11drv.h]
	Prototypes added,
	DC_FUNCTIONS: GetPixel added, some unnecessary functions removed.

	* [objects/region.c]
	CreatePolyPolygonRgn32 added.

	* [files/dos_fs.c]
	QueryDosDevice added.

	* [misc/lstr.c]
	FormatMessage: broken heap management fixed.

	* [scheduler/process.c] [scheduler/thread.c]
	Get/SetThreadPriority/PriorityClass added.

Mon Jan  6 21:55:30 1997  Philippe De Muyter  <phdm@info.ucl.ac.be>

	* [misc/keyboard.c]
	ToAscii : Use EVENT_ToAscii instead.

	* [windows/event.c]
	keypad_key : Do not convert XK_Mode_switch to VK_MENU; recognize
	keypad cursor keys.
	EVENT_event_to_vkey : New function, to transform a X keycode
	into a MSwin vkey + extended bit.
	EVENT_ToAscii : New function, to transform a vkey + extended bit
	(+ key state table) into ascii char(s), using XLookupString, and
	recognizing dead chars.
	EVENT_key : Transform AltGr into Ctrl+Alt sequence; call
	EVENT_event_to_vkey for keycode to vkey conversion; fixed
	previous, context and extended bits.

	* [windows/keyboard.c]
	Include stddebug.h, to get -debugmsg messages.
	GetKeyState : Handle VK_MBUTTON case.
	GetKeyboardState, SetKeyboardState : Debugging messages added.

	* [windows/message.c]
	TranslateMessage : Handle dead chars.

Mon Jan  6 20:10:11 1997  Dominik Strasser  <bm424953@muenchen.org>

	* [if1632/crtdll.spec] [misc/crtdll.c]
	C++ functions new/delete/set_new_handler implemented.

Mon Jan  6 15:48:15 1997 Frans van Dorsselaer <dorssel@rulhmpc49.LeidenUniv.nl>

	* [controls/edit.c] [include/windows.h]
	Moved the edit control to 32 bits.
	Included new (win95) message definitions in windows.h
	Implemented EM_SCROLLCARET, EM_SETMARGINS, EM_GETMARGINS,
	EM_GETLIMITTEXT, EM_POSFROMCHAR, EM_CHARFROMPOS.
	Broke EM_SETWORDBREAKPROC (internal wordwrap still works).
	Fixed some bugs, introduced a couple of others.
	Text buffer is now initially in 32-bit heap.

	* [controls/EDIT.TODO] [controls/combo.c] [controls/widgets.c]
	  [if1632/wprocs.spec] [library/miscstubs.c] [windows/defdlg.c]
	  [misc/commdlg.c]
	Updated to work with 32-bit edit control.

Sat Jan  4 22:07:27 1997  O.Flebbe  <O.Flebbe@science-computing.uni-tuebingen.de>

	* [loader/pe_image.c]
	Use mmap rather then malloc. Better workaround for clean
	segments.
diff --git a/files/directory.c b/files/directory.c
index 157fea4..1c64f74 100644
--- a/files/directory.c
+++ b/files/directory.c
@@ -6,13 +6,13 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <errno.h>
 
 #include "windows.h"
 #include "winerror.h"
-#include "dos_fs.h"
 #include "drive.h"
 #include "file.h"
 #include "heap.h"
@@ -43,25 +43,20 @@
                         char **dos_path, char **unix_path )
 {
     char path[MAX_PATHNAME_LEN];
-    const char *dos_name ,*unix_name;
+    DOS_FULL_NAME full_name;
+
     BY_HANDLE_FILE_INFORMATION info;
 
     PROFILE_GetWineIniString( "wine", keyname, defval, path, sizeof(path) );
-    if (!(unix_name = DOSFS_GetUnixFileName( path, TRUE )) ||
-        !FILE_Stat( unix_name, &info ) ||
+    if (!DOSFS_GetFullName( path, TRUE, &full_name ) ||
+        !FILE_Stat( full_name.long_name, &info ) ||
         !(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
     {
         fprintf(stderr, "Invalid path '%s' for %s directory\n", path, keyname);
         return 0;
     }
-    if (!(dos_name = DOSFS_GetDosTrueName( unix_name, TRUE )))
-    {
-        fprintf( stderr, "Could not get DOS name for %s directory '%s'\n",
-                 keyname, unix_name );
-        return 0;
-    }
-    *unix_path = HEAP_strdupA( SystemHeap, 0, unix_name );
-    *dos_path  = HEAP_strdupA( SystemHeap, 0, dos_name );
+    *unix_path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
+    *dos_path  = HEAP_strdupA( SystemHeap, 0, full_name.short_name );
     return 1;
 }
 
@@ -72,7 +67,7 @@
 void DIR_ParseWindowsPath( char *path )
 {
     char *p;
-    const char *dos_name ,*unix_name;
+    DOS_FULL_NAME full_name;
     BY_HANDLE_FILE_INFORMATION info;
     int i;
 
@@ -87,22 +82,18 @@
                      MAX_PATH_ELEMENTS );
             break;
         }
-        if (!(unix_name = DOSFS_GetUnixFileName( path, TRUE )) ||
-            !FILE_Stat( unix_name, &info ) ||
+        if (!DOSFS_GetFullName( path, TRUE, &full_name ) ||
+            !FILE_Stat( full_name.long_name, &info ) ||
             !(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
         {
             fprintf(stderr,"Warning: invalid dir '%s' in path, deleting it.\n",
                     path );
             continue;
         }
-        if (!(dos_name = DOSFS_GetDosTrueName( unix_name, TRUE )))
-        {
-            fprintf( stderr, "Warning: could not get DOS name for '%s' in path, deleting it.\n",
-                     unix_name );
-            continue;
-        }
-        DIR_UnixPath[DIR_PathElements] = HEAP_strdupA(SystemHeap,0,unix_name);
-        DIR_DosPath[DIR_PathElements]  = HEAP_strdupA(SystemHeap,0,dos_name);
+        DIR_UnixPath[DIR_PathElements] = HEAP_strdupA( SystemHeap, 0,
+                                                       full_name.long_name );
+        DIR_DosPath[DIR_PathElements]  = HEAP_strdupA( SystemHeap, 0,
+                                                       full_name.short_name );
         DIR_PathElements++;
     }
 
@@ -137,9 +128,8 @@
     }
     else
     {
-        cwd = DOSFS_GetDosTrueName( path, TRUE );
         DRIVE_SetCurrentDrive( drive );
-        DRIVE_Chdir( drive, cwd + 2 );
+        DRIVE_Chdir( drive, cwd );
     }
 
     if (!(DIR_GetPath( "windows", "c:\\windows",
@@ -344,7 +334,8 @@
  */
 BOOL32 CreateDirectory32A( LPCSTR path, LPSECURITY_ATTRIBUTES lpsecattribs )
 {
-    const char *unixName;
+    DOS_FULL_NAME full_name;
+    LPCSTR unixName;
 
     dprintf_file( stddeb, "CreateDirectory32A(%s,%p)\n", path, lpsecattribs );
     if ((unixName = DOSFS_IsDevice( path )) != NULL)
@@ -353,8 +344,8 @@
         DOS_ERROR( ER_AccessDenied, EC_AccessDenied, SA_Abort, EL_Disk );
         return FALSE;
     }
-    if (!(unixName = DOSFS_GetUnixFileName( path, FALSE ))) return 0;
-    if ((mkdir( unixName, 0777 ) == -1) && (errno != EEXIST))
+    if (!DOSFS_GetFullName( path, FALSE, &full_name )) return 0;
+    if ((mkdir( full_name.long_name, 0777 ) == -1) && (errno != EEXIST))
     {
         FILE_SetDosError();
         return FALSE;
@@ -409,7 +400,8 @@
  */
 BOOL32 RemoveDirectory32A( LPCSTR path )
 {
-    const char *unixName;
+    DOS_FULL_NAME full_name;
+    LPCSTR unixName;
 
     dprintf_file(stddeb, "RemoveDirectory: '%s'\n", path );
 
@@ -419,8 +411,8 @@
         DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
         return FALSE;
     }
-    if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return FALSE;
-    if (rmdir( unixName ) == -1)
+    if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
+    if (rmdir( full_name.long_name ) == -1)
     {
         FILE_SetDosError();
         return FALSE;
@@ -442,6 +434,56 @@
 
 
 /***********************************************************************
+ *           DIR_TryPath
+ *
+ * Helper function for DIR_SearchPath.
+ */
+static BOOL32 DIR_TryPath( LPCSTR unix_dir, LPCSTR dos_dir, LPCSTR name,
+                           DOS_FULL_NAME *full_name )
+{
+    LPSTR p_l = full_name->long_name + strlen(unix_dir) + 1;
+    LPSTR p_s = full_name->short_name + strlen(dos_dir) + 1;
+
+    if ((p_s >= full_name->short_name + sizeof(full_name->short_name) - 14) ||
+        (p_l >= full_name->long_name + sizeof(full_name->long_name) - 1))
+    {
+        DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
+        return FALSE;
+    }
+    if (!DOSFS_FindUnixName( unix_dir, name, p_l,
+                   sizeof(full_name->long_name) - (p_l - full_name->long_name),
+                   p_s, DRIVE_GetFlags( dos_dir[0] - 'A' ) ))
+        return FALSE;
+    strcpy( full_name->long_name, unix_dir );
+    p_l[-1] = '/';
+    strcpy( full_name->short_name, dos_dir );
+    p_s[-1] = '\\';
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *           DIR_TryModulePath
+ *
+ * Helper function for DIR_SearchPath.
+ */
+static BOOL32 DIR_TryModulePath( LPCSTR name, DOS_FULL_NAME *full_name )
+{
+    /* FIXME: for now, GetModuleFileName32A can't return more */
+    /* than OFS_MAXPATHNAME. This may change with Win32. */
+    char buffer[OFS_MAXPATHNAME];
+    LPSTR p;
+
+    if (!GetCurrentTask()) return FALSE;
+    GetModuleFileName32A( GetCurrentTask(), buffer, sizeof(buffer) );
+    if (!(p = strrchr( buffer, '\\' ))) return FALSE;
+    if (sizeof(buffer) - (++p - buffer) <= strlen(name)) return FALSE;
+    strcpy( p, name );
+    return DOSFS_GetFullName( buffer, TRUE, full_name );
+}
+
+
+/***********************************************************************
  *           DIR_SearchPath
  *
  * Implementation of SearchPath32A. 'win32' specifies whether the search
@@ -450,17 +492,24 @@
  * FIXME: should return long path names.
  */
 DWORD DIR_SearchPath( LPCSTR path, LPCSTR name, LPCSTR ext,
-                      DWORD buflen, LPSTR buffer, LPSTR *lastpart,
-                      BOOL32 win32 )
+                      DOS_FULL_NAME *full_name, BOOL32 win32 )
 {
     DWORD len;
-    LPSTR tmp;
+    LPCSTR p;
     int i;
+    LPSTR tmp = NULL;
+    BOOL32 ret = TRUE;
 
     /* First check the supplied parameters */
 
-    if (strchr( name, '.' )) ext = NULL;  /* Ignore the specified extension */
+    if (!(p = strrchr( name, '\\' ))) p = name;
+    if (!(p = strrchr( p, '/' ))) p = name;
+    if (strchr( p, '.' )) ext = NULL;  /* Ignore the specified extension */
+    if ((*name && (name[1] == ':')) ||
+        strchr( name, '/' ) || strchr( name, '\\' ))
+        path = NULL;  /* Ignore path if name already contains a path */
     if (path && !*path) path = NULL;  /* Ignore empty path */
+
     len = strlen(name);
     if (ext) len += strlen(ext);
     if (path) len += strlen(path) + 1;
@@ -482,110 +531,52 @@
         }
         else strcpy( tmp, name );
         if (ext) strcat( tmp, ext );
+        name = tmp;
     }
-    else tmp = (LPSTR)name;
     
     /* If we have an explicit path, everything's easy */
 
-    if (path || (*tmp && (tmp[1] == ':')) ||
-        strchr( tmp, '/' ) || strchr( tmp, '\\' ))
+    if (path || (*name && (name[1] == ':')) ||
+        strchr( name, '/' ) || strchr( name, '\\' ))
     {
-        if (!DOSFS_GetUnixFileName( tmp, TRUE )) goto not_found;
-        lstrcpyn32A( buffer, tmp, buflen );
-        if (tmp != name) HeapFree( GetProcessHeap(), 0, tmp );
-        return len;
+        ret = DOSFS_GetFullName( name, TRUE, full_name );
+        goto done;
     }
 
     /* Try the path of the current executable (for Win32 search order) */
 
-    if (win32 && GetCurrentTask())
-    {
-        LPSTR p;
-        GetModuleFileName32A( GetCurrentTask(), buffer, buflen );
-        if ((p = strrchr( buffer, '\\' )))
-        {
-            lstrcpyn32A( p + 1, tmp, (INT32)buflen - (p - buffer) );
-            if (DOSFS_GetUnixFileName( buffer, TRUE ))
-            {
-                *p = '\0';
-                goto found;
-            }
-        }
-    }
+    if (win32 && DIR_TryModulePath( name, full_name )) goto done;
 
     /* Try the current directory */
 
-    if (DOSFS_GetUnixFileName( tmp, TRUE ))
-    {
-        GetCurrentDirectory32A( buflen, buffer );
-        goto found;
-    }
+    if (DOSFS_GetFullName( name, TRUE, full_name )) goto done;
 
     /* Try the Windows directory */
 
-    if (DOSFS_FindUnixName( DIR_WindowsUnixDir, name, NULL, 0,
-                            DRIVE_GetFlags( DIR_WindowsDosDir[0] - 'A' ) ))
-    {
-        lstrcpyn32A( buffer, DIR_WindowsDosDir, buflen );
-        goto found;
-    }
+    if (DIR_TryPath( DIR_WindowsUnixDir, DIR_WindowsDosDir, name, full_name ))
+        goto done;
 
     /* Try the Windows system directory */
 
-    if (DOSFS_FindUnixName( DIR_SystemUnixDir, name, NULL, 0,
-                            DRIVE_GetFlags( DIR_SystemDosDir[0] - 'A' ) ))
-    {
-        lstrcpyn32A( buffer, DIR_SystemDosDir, buflen );
-        goto found;
-    }
+    if (DIR_TryPath( DIR_SystemUnixDir, DIR_SystemDosDir, name, full_name ))
+        goto done;
 
     /* Try the path of the current executable (for Win16 search order) */
 
-    if (!win32 && GetCurrentTask())
-    {
-        LPSTR p;
-        GetModuleFileName32A( GetCurrentTask(), buffer, buflen );
-        if ((p = strrchr( buffer, '\\' )))
-        {
-            lstrcpyn32A( p + 1, tmp, (INT32)buflen - (p - buffer) );
-            if (DOSFS_GetUnixFileName( buffer, TRUE ))
-            {
-                *p = '\0';
-                goto found;
-            }
-        }
-    }
+    if (!win32 && DIR_TryModulePath( name, full_name )) goto done;
+
     /* Try all directories in path */
 
     for (i = 0; i < DIR_PathElements; i++)
     {
-        if (DOSFS_FindUnixName( DIR_UnixPath[i], name, NULL, 0,
-                                DRIVE_GetFlags( DIR_DosPath[i][0] - 'A' ) ))
-        {
-            lstrcpyn32A( buffer, DIR_DosPath[i], buflen );
-            goto found;
-        }
+        if (DIR_TryPath( DIR_UnixPath[i], DIR_DosPath[i], name, full_name ))
+            goto done;
     }
 
-not_found:
-    if (tmp != name) HeapFree( GetProcessHeap(), 0, tmp );
-    SetLastError( ERROR_FILE_NOT_FOUND );
-    DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
-    return 0;
-
-found:
-    len = strlen(buffer);
-    if (lastpart) *lastpart = buffer + len + 1;
-    if (len + 1 < buflen)
-    {
-        buffer += len;
-        *buffer++ = '\\';
-        buflen -= len + 1;
-        lstrcpyn32A( buffer, tmp, buflen );
-    }
-    len += strlen(tmp) + 1;
-    if (tmp != name) HeapFree( GetProcessHeap(), 0, tmp );
-    return len;
+    ret = FALSE;
+done:
+    if (tmp) HeapFree( GetProcessHeap(), 0, tmp );
+    return ret;
 }
 
 
@@ -595,7 +586,17 @@
 DWORD SearchPath32A( LPCSTR path, LPCSTR name, LPCSTR ext, DWORD buflen,
                      LPSTR buffer, LPSTR *lastpart )
 {
-    return DIR_SearchPath( path, name, ext, buflen, buffer, lastpart, TRUE );
+    LPSTR p, res;
+    DOS_FULL_NAME full_name;
+
+    if (!DIR_SearchPath( path, name, ext, &full_name, TRUE )) return 0;
+    lstrcpyn32A( buffer, full_name.short_name, buflen );
+    res = full_name.long_name +
+              strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' ));
+    if (*res && (buflen > 3)) lstrcpyn32A( buffer + 3, res + 1, buflen - 3 );
+    for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
+    if (lastpart) *lastpart = strrchr( buffer, '\\' ) + 1;
+    return *res ? strlen(res) + 2 : 3;
 }
 
 
@@ -605,25 +606,30 @@
 DWORD SearchPath32W( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen,
                      LPWSTR buffer, LPWSTR *lastpart )
 {
+    LPWSTR p;
+    LPSTR res;
+    DOS_FULL_NAME full_name;
+
     LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
     LPSTR extA  = HEAP_strdupWtoA( GetProcessHeap(), 0, ext );
-    LPSTR lastpartA;
-    LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, buflen + 1 );
-
-    DWORD ret = DIR_SearchPath( pathA, nameA, extA, buflen, bufferA,
-                                &lastpartA, TRUE );
-    lstrcpyAtoW( buffer, bufferA );
-    if (lastpart)
-    {
-        if (lastpartA) *lastpart = buffer + (lastpartA - bufferA);
-        else *lastpart = NULL;
-    }
-    HeapFree( GetProcessHeap(), 0, bufferA );
+    DWORD ret = DIR_SearchPath( pathA, nameA, extA, &full_name, TRUE );
     HeapFree( GetProcessHeap(), 0, extA );
     HeapFree( GetProcessHeap(), 0, nameA );
     HeapFree( GetProcessHeap(), 0, pathA );
-    return ret;
+    if (!ret) return 0;
+
+    lstrcpynAtoW( buffer, full_name.short_name, buflen );
+    res = full_name.long_name +
+              strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' ));
+    if (*res && (buflen > 3)) lstrcpynAtoW( buffer + 3, res + 1, buflen - 3 );
+    for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
+    if (lastpart)
+    {
+        for (p = *lastpart = buffer; *p; p++)
+            if (*p == '\\') *lastpart = p + 1;
+    }
+    return *res ? strlen(res) + 2 : 3;
 }
 
 
diff --git a/files/dos_fs.c b/files/dos_fs.c
index fb3c0e7..9b081a0 100644
--- a/files/dos_fs.c
+++ b/files/dos_fs.c
@@ -22,7 +22,6 @@
 
 #include "windows.h"
 #include "winerror.h"
-#include "dos_fs.h"
 #include "drive.h"
 #include "file.h"
 #include "heap.h"
@@ -31,13 +30,12 @@
 #include "debug.h"
 
 /* Define the VFAT ioctl to get both short and long file names */
-#if 0  /* not working yet */
+/* FIXME: is it possible to get this to work on other systems? */
 #ifdef linux
 #define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, long)
 #else   /* linux */
 #undef VFAT_IOCTL_READDIR_BOTH  /* just in case... */
 #endif  /* linux */
-#endif
 
 /* Chars we don't want to see in DOS file names */
 #define INVALID_DOS_CHARS  "*?<>|\"+=,;[] \345"
@@ -58,6 +56,7 @@
     { "COM4", "" }
 };
 
+
 #define GET_DRIVE(path) \
     (((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
 
@@ -134,47 +133,17 @@
 
 
 /***********************************************************************
- *           DOSFS_CheckDotDot
- *
- * Remove all '.' and '..' at the beginning of 'name'.
- */
-static const char * DOSFS_CheckDotDot( const char *name, char *buffer,
-                                       char sep , int *len )
-{
-    char *p = buffer + strlen(buffer);
-
-    while (*name == '.')
-    {
-        if (IS_END_OF_NAME(name[1]))
-        {
-            name++;
-            while ((*name == '\\') || (*name == '/')) name++;
-        }
-        else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
-        {
-            name += 2;
-            while ((*name == '\\') || (*name == '/')) name++;
-            while ((p > buffer) && (*p != sep)) { p--; (*len)++; }
-            *p = '\0';  /* Remove trailing separator */
-        }
-        else break;
-    }
-    return name;
-}
-
-
-/***********************************************************************
  *           DOSFS_ToDosFCBFormat
  *
  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
  * expanding wild cards and converting to upper-case in the process.
  * File name can be terminated by '\0', '\\' or '/'.
- * Return NULL if the name is not a valid DOS name.
+ * Return FALSE if the name is not a valid DOS name.
+ * 'buffer' must be at least 12 characters long.
  */
-const char *DOSFS_ToDosFCBFormat( const char *name )
+BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
 {
     static const char invalid_chars[] = INVALID_DOS_CHARS;
-    static char buffer[12];
     const char *p = name;
     int i;
 
@@ -188,7 +157,7 @@
             buffer[1] = '.';
             p++;
         }
-        return (!*p || (*p == '/') || (*p == '\\')) ? buffer : NULL;
+        return (!*p || (*p == '/') || (*p == '\\'));
     }
 
     for (i = 0; i < 8; i++)
@@ -208,7 +177,7 @@
             buffer[i] = '?';
             break;
         default:
-            if (strchr( invalid_chars, *p )) return NULL;
+            if (strchr( invalid_chars, *p )) return FALSE;
             buffer[i] = toupper(*p);
             p++;
             break;
@@ -223,7 +192,7 @@
     else
     {
         /* Check if name too long */
-        if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return NULL;
+        if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
     }
     if (*p == '.') p++;  /* Skip dot */
 
@@ -237,7 +206,7 @@
             buffer[i] = ' ';
             break;
         case '.':
-            return NULL;  /* Second extension not allowed */
+            return FALSE;  /* Second extension not allowed */
         case '?':
             p++;
             /* fall through */
@@ -245,14 +214,14 @@
             buffer[i] = '?';
             break;
         default:
-            if (strchr( invalid_chars, *p )) return NULL;
+            if (strchr( invalid_chars, *p )) return FALSE;
             buffer[i] = toupper(*p);
             p++;
             break;
         }
     }
     buffer[11] = '\0';
-    return buffer;
+    return TRUE;
 }
 
 
@@ -262,11 +231,10 @@
  * Convert a file name from FCB to DTA format (name.ext, null-terminated)
  * converting to upper-case in the process.
  * File name can be terminated by '\0', '\\' or '/'.
- * Return NULL if the name is not a valid DOS name.
+ * 'buffer' must be at least 13 characters long.
  */
-const char *DOSFS_ToDosDTAFormat( const char *name )
+static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
 {
-    static char buffer[13];
     char *p;
 
     memcpy( buffer, name, 8 );
@@ -276,7 +244,6 @@
     for (p += 3; p[-1] == ' '; p--);
     if (p[-1] == '.') p--;
     *p = '\0';
-    return buffer;
 }
 
 
@@ -397,13 +364,11 @@
 #ifdef VFAT_IOCTL_READDIR_BOTH
     if (dir->fd != -1)
     {
-        LPCSTR fcb_name;
         if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
             return FALSE;
         if (!dir->dirent[0].d_reclen) return FALSE;
-        fcb_name = DOSFS_ToDosFCBFormat( dir->dirent[0].d_name );
-        if (fcb_name) strcpy( dir->short_name, fcb_name );
-        else dir->short_name[0] = '\0';
+        if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
+            dir->short_name[0] = '\0';
         *short_name = dir->short_name;
         if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
         else *long_name = dir->dirent[0].d_name;
@@ -419,80 +384,20 @@
 
 
 /***********************************************************************
- *           DOSFS_ToDosDateTime
- *
- * Convert a Unix time in the DOS date/time format.
- */
-void DOSFS_ToDosDateTime( time_t unixtime, WORD *pDate, WORD *pTime )
-{
-    struct tm *tm = localtime( &unixtime );
-    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 + 1) << 5)
-                 + tm->tm_mday;
-}
-
-/***********************************************************************
- *           DOSFS_DosDateTimeToUnixTime
- *
- * Convert from the DOS (FAT) date/time format into Unix time
- * (borrowed from files/file.c)
- */
-time_t DOSFS_DosDateTimeToUnixTime( WORD date, WORD time )
-{
-    struct tm newtm;
-
-    newtm.tm_sec  = (time & 0x1f) * 2;
-    newtm.tm_min  = (time >> 5) & 0x3f;
-    newtm.tm_hour = (time >> 11);
-    newtm.tm_mday = (date & 0x1f);
-    newtm.tm_mon  = ((date >> 5) & 0x0f) - 1;
-    newtm.tm_year = (date >> 9) + 80;
-    return mktime( &newtm );
-}
-
-
-/***********************************************************************
- *           DOSFS_UnixTimeToFileTime
- *
- * Convert a Unix time to FILETIME format.
- */
-void DOSFS_UnixTimeToFileTime( time_t unixtime, FILETIME *filetime )
-{
-    /* FIXME :-) */
-    filetime->dwLowDateTime  = unixtime;
-    filetime->dwHighDateTime = 0;
-}
-
-
-/***********************************************************************
- *           DOSFS_FileTimeToUnixTime
- *
- * Convert a FILETIME format to Unix time.
- */
-time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime )
-{
-    /* FIXME :-) */
-    return filetime->dwLowDateTime;
-}
-
-
-/***********************************************************************
  *           DOSFS_Hash
  *
  * Transform a Unix file name into a hashed DOS name. If the name is a valid
  * DOS name, it is converted to upper-case; otherwise it is replaced by a
  * hashed version that fits in 8.3 format.
  * File name can be terminated by '\0', '\\' or '/'.
+ * 'buffer' must be at least 13 characters long.
  */
-static const char *DOSFS_Hash( const char *name, int dir_format,
-                               int ignore_case )
+static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
+                        BOOL32 ignore_case )
 {
     static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
 
-    static char buffer[13];
     const char *p, *ext;
     char *dst;
     unsigned short hash;
@@ -508,7 +413,7 @@
             buffer[0] = '.';
             if (!dir_format) buffer[1] = buffer[2] = '\0';
             if (name[1] == '.') buffer[1] = '.';
-            return buffer;
+            return;
         }
 
         /* Simply copy the name, converting to uppercase */
@@ -523,55 +428,53 @@
                 *dst++ = toupper(*name);
         }
         if (!dir_format) *dst = '\0';
+        return;
+    }
+
+    /* Compute the hash code of the file name */
+    /* If you know something about hash functions, feel free to */
+    /* insert a better algorithm here... */
+    if (ignore_case)
+    {
+        for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
+            hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
+        hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
     }
     else
     {
-        /* Compute the hash code of the file name */
-        /* If you know something about hash functions, feel free to */
-        /* insert a better algorithm here... */
-        if (ignore_case)
-        {
-            for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
-                hash = (hash << 3) ^ (hash >> 5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
-            hash = (hash << 3) ^ (hash >> 5) ^ tolower(*p); /* Last character*/
-        }
-        else
-        {
-            for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
-                hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
-            hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
-        }
-
-        /* Find last dot for start of the extension */
-        for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
-            if (*p == '.') ext = p;
-        if (ext && IS_END_OF_NAME(ext[1]))
-            ext = NULL;  /* Empty extension ignored */
-
-        /* Copy first 4 chars, replacing invalid chars with '_' */
-        for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
-        {
-            if (IS_END_OF_NAME(*p) || (p == ext)) break;
-            *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
-        }
-        /* Pad to 5 chars with '~' */
-        while (i-- >= 0) *dst++ = '~';
-
-        /* Insert hash code converted to 3 ASCII chars */
-        *dst++ = hash_chars[(hash >> 10) & 0x1f];
-        *dst++ = hash_chars[(hash >> 5) & 0x1f];
-        *dst++ = hash_chars[hash & 0x1f];
-
-        /* Copy the first 3 chars of the extension (if any) */
-        if (ext)
-        {
-            if (!dir_format) *dst++ = '.';
-            for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
-                *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
-        }
-        if (!dir_format) *dst = '\0';
+        for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
+            hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
+        hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
     }
-    return buffer;
+
+    /* Find last dot for start of the extension */
+    for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
+        if (*p == '.') ext = p;
+    if (ext && IS_END_OF_NAME(ext[1]))
+        ext = NULL;  /* Empty extension ignored */
+
+    /* Copy first 4 chars, replacing invalid chars with '_' */
+    for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
+    {
+        if (IS_END_OF_NAME(*p) || (p == ext)) break;
+        *dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
+    }
+    /* Pad to 5 chars with '~' */
+    while (i-- >= 0) *dst++ = '~';
+
+    /* Insert hash code converted to 3 ASCII chars */
+    *dst++ = hash_chars[(hash >> 10) & 0x1f];
+    *dst++ = hash_chars[(hash >> 5) & 0x1f];
+    *dst++ = hash_chars[hash & 0x1f];
+
+    /* Copy the first 3 chars of the extension (if any) */
+    if (ext)
+    {
+        if (!dir_format) *dst++ = '.';
+        for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
+            *dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
+    }
+    if (!dir_format) *dst = '\0';
 }
 
 
@@ -581,31 +484,34 @@
  * Find the Unix file name in a given directory that corresponds to
  * a file name (either in Unix or DOS format).
  * File name can be terminated by '\0', '\\' or '/'.
- * Return 1 if OK, 0 if no file name matches.
+ * Return TRUE if OK, FALSE if no file name matches.
+ *
+ * 'long_buf' must be at least 'long_len' characters long. If the long name
+ * turns out to be larger than that, the function returns FALSE.
+ * 'short_buf' must be at least 13 characters long.
  */
-BOOL32 DOSFS_FindUnixName( const char *path, const char *name,
-                           char *buffer, int maxlen, UINT32 drive_flags )
+BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
+                           INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
 {
     DOS_DIR *dir;
     LPCSTR long_name, short_name;
-    char dos_name[12];
+    char dos_name[12], tmp_buf[13];
     BOOL32 ret;
 
     const char *p = strchr( name, '/' );
     int len = p ? (int)(p - name) : strlen(name);
+    if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
+    if (long_len < len + 1) return FALSE;
 
     dprintf_dosfs( stddeb, "DOSFS_FindUnixName: %s,%s\n", path, name );
 
-    if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
-
-    dos_name[0] = '\0';
-    if ((p = DOSFS_ToDosFCBFormat( name ))) strcpy( dos_name, p );
+    if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
 
     if (!(dir = DOSFS_OpenDir( path )))
     {
         dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s): can't open dir\n",
                        path, name );
-        return 0;
+        return FALSE;
     }
 
     while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
@@ -613,7 +519,7 @@
         /* Check against Unix name */
         if (len == strlen(long_name))
         {
-            if (drive_flags & DRIVE_CASE_SENSITIVE)
+            if (!ignore_case)
             {
                 if (!lstrncmp32A( long_name, name, len )) break;
             }
@@ -626,14 +532,29 @@
         {
             /* Check against hashed DOS name */
             if (!short_name)
-                short_name = DOSFS_Hash( long_name, TRUE,
-                                       !(drive_flags & DRIVE_CASE_SENSITIVE) );
+            {
+                DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
+                short_name = tmp_buf;
+            }
             if (!strcmp( dos_name, short_name )) break;
         }
     }
-    if (ret && buffer) lstrcpyn32A( buffer, long_name, maxlen );
-    dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s\n",
-                   path, name, ret ? long_name : "** Not found **" );
+    if (ret)
+    {
+        if (long_buf) strcpy( long_buf, long_name );
+        if (short_buf)
+        {
+            if (short_name)
+                DOSFS_ToDosDTAFormat( short_name, short_buf );
+            else
+                DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
+        }
+        dprintf_dosfs( stddeb, "DOSFS_FindUnixName(%s,%s) -> %s (%s)\n",
+                       path, name, long_name, short_buf ? short_buf : "***");
+    }
+    else
+        dprintf_dosfs(stddeb,"DOSFS_FindUnixName(%s,%s) -> ** Not found **\n",
+                      path, name );
     DOSFS_CloseDir( dir );
     return ret;
 }
@@ -702,168 +623,278 @@
 
 
 /***********************************************************************
- *           DOSFS_GetUnixFileName
+ *           DOSFS_GetFullName
  *
- * Convert a file name (DOS or mixed DOS/Unix format) to a valid Unix name.
- * Return NULL if one of the path components does not exist. The last path
+ * Convert a file name (DOS or mixed DOS/Unix format) to a valid
+ * Unix name / short DOS name pair.
+ * Return FALSE if one of the path components does not exist. The last path
  * component is only checked if 'check_last' is non-zero.
+ * The buffers pointed to by 'long_buf' and 'short_buf' must be
+ * at least MAX_PATHNAME_LEN long.
  */
-const char * DOSFS_GetUnixFileName( const char * name, int check_last )
+BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
 {
-    static char buffer[MAX_PATHNAME_LEN];
-    int drive, len;
     BOOL32 found;
     UINT32 flags;
-    char *p, *root;
+    char *p_l, *p_s, *root;
 
-    dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: %s\n", name );
+    dprintf_dosfs( stddeb, "DOSFS_GetFullName: %s (last=%d)\n",
+                   name, check_last );
 
-    if ((drive = DOSFS_GetPathDrive( &name )) == -1) return NULL;
-    flags = DRIVE_GetFlags(drive);
-    lstrcpyn32A( buffer, DRIVE_GetRoot(drive), MAX_PATHNAME_LEN );
-    if (buffer[1]) root = buffer + strlen(buffer);
-    else root = buffer;  /* root directory */
+    if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
+    flags = DRIVE_GetFlags( full->drive );
 
-    if ((*name == '\\') || (*name == '/'))
+    lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
+                 sizeof(full->long_name) );
+    if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
+    else root = full->long_name;  /* root directory */
+
+    strcpy( full->short_name, "A:\\" );
+    full->short_name[0] += full->drive;
+
+    if ((*name == '\\') || (*name == '/'))  /* Absolute path */
     {
         while ((*name == '\\') || (*name == '/')) name++;
     }
-    else
+    else  /* Relative path */
     {
-        lstrcpyn32A( root + 1, DRIVE_GetUnixCwd(drive),
-                     MAX_PATHNAME_LEN - (int)(root - buffer) - 1 );
+        lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
+                     sizeof(full->long_name) - (root - full->long_name) - 1 );
         if (root[1]) *root = '/';
+        lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
+                     sizeof(full->short_name) - 3 );
     }
 
-    p = buffer[1] ? buffer + strlen(buffer) : buffer;
-    len = MAX_PATHNAME_LEN - strlen(buffer);
+    p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
+                             : full->long_name;
+    p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
+                              : full->short_name + 2;
     found = TRUE;
+
     while (*name && found)
     {
-        const char *newname = DOSFS_CheckDotDot( name, root, '/', &len );
-        if (newname != name)
+        /* Check for '.' and '..' */
+
+        if (*name == '.')
         {
-            p = root + strlen(root);
-            name = newname;
-            continue;
+            if (IS_END_OF_NAME(name[1]))
+            {
+                name++;
+                while ((*name == '\\') || (*name == '/')) name++;
+                continue;
+            }
+            else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
+            {
+                name += 2;
+                while ((*name == '\\') || (*name == '/')) name++;
+                while ((p_l > root) && (*p_l != '/')) p_l--;
+                while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
+                *p_l = *p_s = '\0';  /* Remove trailing separator */
+                continue;
+            }
         }
-        if (len <= 1)
+
+        /* Make sure buffers are large enough */
+
+        if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
+            (p_l >= full->long_name + sizeof(full->long_name) - 1))
         {
             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
-            return NULL;
+            return FALSE;
         }
-        if ((found = DOSFS_FindUnixName( buffer, name, p+1, len-1, flags )))
+
+        /* Get the long and short name matching the file name */
+
+        if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
+                         sizeof(full->long_name) - (p_l - full->long_name) - 1,
+                         p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
         {
-            *p = '/';
-            len -= strlen(p);
-            p += strlen(p);
+            *p_l++ = '/';
+            p_l   += strlen(p_l);
+            *p_s++ = '\\';
+            p_s   += strlen(p_s);
             while (!IS_END_OF_NAME(*name)) name++;
         }
         else if (!check_last)
         {
-            *p++ = '/';
-            for (len--; !IS_END_OF_NAME(*name) && (len > 1); name++, len--)
-                *p++ = tolower(*name);
-            *p = '\0';
+            *p_l++ = '/';
+            *p_s++ = '\\';
+            while (!IS_END_OF_NAME(*name) &&
+                   (p_s < full->short_name + sizeof(full->short_name) - 1) &&
+                   (p_l < full->long_name + sizeof(full->long_name) - 1))
+            {
+                *p_l++ = *p_s++ = tolower(*name);
+                name++;
+            }
+            *p_l = *p_s = '\0';
         }
         while ((*name == '\\') || (*name == '/')) name++;
     }
+
     if (!found)
     {
         if (check_last)
         {
             DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
-            return NULL;
+            return FALSE;
         }
         if (*name)  /* Not last */
         {
             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
-            return NULL;
+            return FALSE;
         }
     }
-    if (!buffer[0]) strcpy( buffer, "/" );
-    dprintf_dosfs( stddeb, "DOSFS_GetUnixFileName: returning %s\n", buffer );
-    return buffer;
+    if (!full->long_name[0]) strcpy( full->long_name, "/" );
+    if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
+    dprintf_dosfs( stddeb, "DOSFS_GetFullName: returning %s = %s\n",
+                   full->long_name, full->short_name );
+    return TRUE;
 }
 
 
 /***********************************************************************
- *           DOSFS_GetDosTrueName
- *
- * Convert a file name (DOS or Unix format) to a complete DOS name.
- * Return NULL if the path name is invalid or too long.
- * The unix_format flag is a hint that the file name is in Unix format.
+ *           GetShortPathName32A   (KERNEL32.271)
  */
-const char * DOSFS_GetDosTrueName( const char *name, int unix_format )
+DWORD GetShortPathName32A( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
 {
-    static char buffer[MAX_PATHNAME_LEN];
-    int drive, len;
-    UINT32 flags;
+    DOS_FULL_NAME full_name;
+
+    /* FIXME: is it correct to always return a fully qualified short path? */
+    if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
+    lstrcpyn32A( shortpath, full_name.short_name, shortlen );
+    return strlen( full_name.short_name );
+}
+
+
+/***********************************************************************
+ *           GetShortPathName32W   (KERNEL32.272)
+ */
+DWORD GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
+{
+    DOS_FULL_NAME full_name;
+    DWORD ret = 0;
+    LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
+
+    /* FIXME: is it correct to always return a fully qualified short path? */
+    if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
+    {
+        ret = strlen( full_name.short_name );
+        lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
+    }
+    HeapFree( GetProcessHeap(), 0, longpathA );
+    return ret;
+}
+
+
+/***********************************************************************
+ *           DOSFS_DoGetFullPathName
+ *
+ * Implementation of GetFullPathName32A/W.
+ */
+static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
+                                      BOOL32 unicode )
+{
+    char buffer[MAX_PATHNAME_LEN];
+    int drive;
     char *p;
 
-    dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName(%s,%d)\n", name, unix_format);
+    dprintf_dosfs( stddeb, "GetFullPathName: converting %s\n", name );
 
-    if ((drive = DOSFS_GetPathDrive( &name )) == -1) return NULL;
+    if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
     p = buffer;
     *p++ = 'A' + drive;
     *p++ = ':';
-    if (IS_END_OF_NAME(*name))
+    if (IS_END_OF_NAME(*name))  /* Absolute path */
     {
         while ((*name == '\\') || (*name == '/')) name++;
     }
-    else
+    else  /* Relative path */
     {
         *p++ = '\\';
         lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
         if (*p) p += strlen(p); else p--;
     }
     *p = '\0';
-    len = MAX_PATHNAME_LEN - (int)(p - buffer);
-    flags = DRIVE_GetFlags(drive);
 
     while (*name)
     {
-        const char *newname = DOSFS_CheckDotDot( name, buffer+2, '\\', &len );
-        if (newname != name)
+        if (*name == '.')
         {
-            p = buffer + strlen(buffer);
-            name = newname;
-            continue;
+            if (IS_END_OF_NAME(name[1]))
+            {
+                name++;
+                while ((*name == '\\') || (*name == '/')) name++;
+                continue;
+            }
+            else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
+            {
+                name += 2;
+                while ((*name == '\\') || (*name == '/')) name++;
+                while ((p > buffer + 2) && (*p != '\\')) p--;
+                *p = '\0';  /* Remove trailing separator */
+                continue;
+            }
         }
-        if (len <= 1)
+        if (p >= buffer + sizeof(buffer) - 1)
         {
             DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
-            return NULL;
+            return 0;
         }
         *p++ = '\\';
-        if (unix_format)  /* Hash it into a DOS name */
-        {
-            lstrcpyn32A( p, DOSFS_Hash( name, FALSE,
-                                     !(flags & DRIVE_CASE_SENSITIVE) ),
-                         len );
-            len -= strlen(p);
-            p += strlen(p);
-            while (!IS_END_OF_NAME(*name)) name++;
-        }
-        else  /* Already DOS format, simply upper-case it */
-        {
-            while (!IS_END_OF_NAME(*name) && (len > 1))
-            {
-                *p++ = toupper(*name);
-                name++;
-                len--;
-            }
-            *p = '\0';
-        }
+        while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
+            *p++ = *name++;
+        *p = '\0';
         while ((*name == '\\') || (*name == '/')) name++;
     }
+
     if (!buffer[2])
     {
         buffer[2] = '\\';
         buffer[3] = '\0';
     }
-    dprintf_dosfs( stddeb, "DOSFS_GetDosTrueName: returning %s\n", buffer );
-    return buffer;
+    if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
+        CharUpper32A( buffer );
+
+    if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
+    else lstrcpyn32A( result, buffer, len );
+
+    dprintf_dosfs( stddeb, "GetFullPathName: returning %s\n", buffer );
+    return strlen(buffer);
+}
+
+
+/***********************************************************************
+ *           GetFullPathName32A   (KERNEL32.272)
+ */
+DWORD GetFullPathName32A(LPCSTR name, DWORD len, LPSTR buffer, LPSTR *lastpart)
+{
+    DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
+    if (ret && lastpart)
+    {
+        LPSTR p = buffer + strlen(buffer);
+        while ((p > buffer + 2) && (*p != '\\')) p--;
+        *lastpart = p + 1;
+    }
+    return ret;
+}
+
+
+/***********************************************************************
+ *           GetFullPathName32W   (KERNEL32.273)
+ */
+DWORD GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
+                          LPWSTR *lastpart )
+{
+    LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
+    DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
+    HeapFree( GetProcessHeap(), 0, nameA );
+    if (ret && lastpart)
+    {
+        LPWSTR p = buffer + lstrlen32W(buffer);
+        while ((p > buffer + 2) && (*p != '\\')) p--;
+        *lastpart = p + 1;
+    }
+    return ret;
 }
 
 
@@ -885,6 +916,7 @@
     static int cur_pos = 0;
     static int drive_root = 0;
     char *p;
+    char dos_name[13];
     LPCSTR long_name, short_name;
     UINT32 flags;
     BY_HANDLE_FILE_INFORMATION info;
@@ -893,15 +925,14 @@
     {
         if (skip) return 0;
         entry->dwFileAttributes  = FILE_ATTRIBUTE_LABEL;
-        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime );
-        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime );
-        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime );
+        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
+        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
+        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
         entry->nFileSizeHigh     = 0;
         entry->nFileSizeLow      = 0;
         entry->dwReserved0       = 0;
         entry->dwReserved1       = 0;
-        strcpy( entry->cFileName,
-                DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive )) );
+        DOSFS_ToDosDTAFormat( DRIVE_GetLabel( drive ), entry->cFileName );
         strcpy( entry->cAlternateFileName, entry->cFileName );
         return 1;
     }
@@ -956,8 +987,11 @@
         if (short_mask)
         {
             if (!short_name)
-                short_name = DOSFS_Hash( long_name, TRUE,
-                                         !(flags & DRIVE_CASE_SENSITIVE) );
+            {
+                DOSFS_Hash( long_name, dos_name, TRUE,
+                            !(flags & DRIVE_CASE_SENSITIVE) );
+                short_name = dos_name;
+            }
             if (!DOSFS_MatchShort( short_mask, short_name )) continue;
         }
 
@@ -973,19 +1007,21 @@
 
         /* We now have a matching entry; fill the result and return */
 
-        if (!short_name)
-            short_name = DOSFS_Hash( long_name, TRUE,
-                                     !(flags & DRIVE_CASE_SENSITIVE) );
-
         entry->dwFileAttributes = info.dwFileAttributes;
         entry->ftCreationTime   = info.ftCreationTime;
         entry->ftLastAccessTime = info.ftLastAccessTime;
         entry->ftLastWriteTime  = info.ftLastWriteTime;
         entry->nFileSizeHigh    = info.nFileSizeHigh;
         entry->nFileSizeLow     = info.nFileSizeLow;
-        strcpy( entry->cAlternateFileName, DOSFS_ToDosDTAFormat(short_name) );
+
+        if (short_name)
+            DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
+        else
+            DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
+                        !(flags & DRIVE_CASE_SENSITIVE) );
+
         lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
-        if (!(flags & DRIVE_CASE_PRESERVING)) AnsiLower( entry->cFileName );
+        if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
         dprintf_dosfs( stddeb, "DOSFS_FindNext: returning %s (%s) %02lx %ld\n",
                        entry->cFileName, entry->cAlternateFileName,
                        entry->dwFileAttributes, entry->nFileSizeLow );
@@ -1004,17 +1040,17 @@
  */
 HANDLE16 FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
 {
+    DOS_FULL_NAME full_name;
     HGLOBAL16 handle;
     FIND_FIRST_INFO *info;
-    LPCSTR ptr;
 
     if (!path) return 0;
-    if (!(ptr = DOSFS_GetUnixFileName( path, FALSE )))
+    if (!DOSFS_GetFullName( path, FALSE, &full_name ))
         return INVALID_HANDLE_VALUE16;
     if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
         return INVALID_HANDLE_VALUE16;
     info = (FIND_FIRST_INFO *)GlobalLock16( handle );
-    info->path = HEAP_strdupA( SystemHeap, 0, ptr );
+    info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
     info->mask = strrchr( info->path, '/' );
     *(info->mask++) = '\0';
     if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
@@ -1156,72 +1192,34 @@
 
 
 /***********************************************************************
- *           GetShortPathName32A   (KERNEL32.271)
+ *           DOSFS_UnixTimeToFileTime
+ *
+ * Convert a Unix time to FILETIME format.
+ * The FILETIME structure is a 64-bit value representing the number of
+ * 100-nanosecond intervals since January 1, 1601.
+ * 'remainder' is the fraction of 100-ns intervals smaller than 1 second
+ * that couldn't be stored in the time_t value.
  */
-DWORD GetShortPathName32A( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
+void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
+                               DWORD remainder )
 {
-    LPCSTR dostruename;
-
-    dprintf_dosfs( stddeb, "GetShortPathName32A(%s,%p,%ld)\n",
-                   longpath, shortpath, shortlen );
-
-    dostruename = DOSFS_GetDosTrueName( longpath, TRUE );
-    lstrcpyn32A( shortpath, dostruename, shortlen );
-    return strlen(dostruename);
+    /* FIXME :-) */
+    filetime->dwLowDateTime  = unix_time;
+    filetime->dwHighDateTime = 0;
 }
 
 
 /***********************************************************************
- *           GetShortPathNameW   (KERNEL32.272)
+ *           DOSFS_FileTimeToUnixTime
+ *
+ * Convert a FILETIME format to Unix time.
+ * If not NULL, 'remainder' contains the fractional part of the filetime.
  */
-DWORD GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
+time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
 {
-    LPSTR longpatha = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
-    LPCSTR dostruename = DOSFS_GetDosTrueName( longpatha, TRUE );
-    HeapFree( GetProcessHeap(), 0, longpatha );
-    lstrcpynAtoW( shortpath, dostruename, shortlen );
-    return strlen(dostruename);
-}
-
-
-/***********************************************************************
- *           GetFullPathNameA   (KERNEL32.272)
- */
-DWORD GetFullPathName32A( LPCSTR fn, DWORD buflen, LPSTR buf, LPSTR *lastpart)
-{
-	dprintf_file(stddeb,"GetFullPathNameA(%s)\n",fn);
-	/* FIXME */
-        if (buf) {
-            lstrcpyn32A(buf,fn,buflen);
-            if (lastpart) {
-		*lastpart = strrchr(buf,'\\');
-		if (!*lastpart) *lastpart=buf;
-	    }
-	}
-	return strlen(fn);
-}
-
-/***********************************************************************
- *           GetFullPathName32W   (KERNEL32.273)
- */
-DWORD GetFullPathName32W(LPCWSTR fn,DWORD buflen,LPWSTR buf,LPWSTR *lastpart) {
-	LPWSTR  x;
-
-	dprintf_file(stddeb,"GetFullPathNameW(%p)\n",fn);
-	/* FIXME */
-	if (buf) {
-		lstrcpyn32W(buf,fn,buflen);
-		if (lastpart) {
-			x = buf+lstrlen32W(buf)-1;
-			while (x>=buf && *x!='\\')
-			x--;
-			if (x>=buf)
-				*lastpart=x;
-			else
-				*lastpart=buf;
-		}
-	}
-	return lstrlen32W(fn);
+    /* FIXME :-) */
+    if (remainder) *remainder = 0;
+    return filetime->dwLowDateTime;
 }
 
 
@@ -1230,8 +1228,15 @@
  */
 BOOL32 DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft )
 {
-    time_t unixtime = DOSFS_DosDateTimeToUnixTime(fatdate,fattime);
-    DOSFS_UnixTimeToFileTime(unixtime,ft);
+    struct tm newtm;
+
+    newtm.tm_sec  = (fattime & 0x1f) * 2;
+    newtm.tm_min  = (fattime >> 5) & 0x3f;
+    newtm.tm_hour = (fattime >> 11);
+    newtm.tm_mday = (fatdate & 0x1f);
+    newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
+    newtm.tm_year = (fatdate >> 9) + 80;
+    DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
     return TRUE;
 }
 
@@ -1242,8 +1247,13 @@
 BOOL32 FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
                               LPWORD fattime )
 {
-    time_t unixtime = DOSFS_FileTimeToUnixTime(ft);
-    DOSFS_ToDosDateTime(unixtime,fatdate,fattime);
+    time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
+    struct tm *tm = localtime( &unixtime );
+    if (fattime)
+        *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
+    if (fatdate)
+        *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
+                   + tm->tm_mday;
     return TRUE;
 }
 
@@ -1254,11 +1264,12 @@
 BOOL32 LocalFileTimeToFileTime( const FILETIME *localft, LPFILETIME utcft )
 {
     struct tm *xtm;
+    DWORD remainder;
 
     /* convert from local to UTC. Perhaps not correct. FIXME */
-    xtm = gmtime((time_t*)&(localft->dwLowDateTime));
-    utcft->dwLowDateTime  = mktime(xtm);
-    utcft->dwHighDateTime = 0;
+    time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
+    xtm = gmtime( &unixtime );
+    DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
     return TRUE; 
 }
 
@@ -1269,11 +1280,12 @@
 BOOL32 FileTimeToLocalFileTime( const FILETIME *utcft, LPFILETIME localft )
 {
     struct tm *xtm;
+    DWORD remainder;
 
     /* convert from UTC to local. Perhaps not correct. FIXME */
-    xtm = localtime((time_t*)&(utcft->dwLowDateTime));
-    localft->dwLowDateTime  = mktime(xtm);
-    localft->dwHighDateTime = 0;
+    time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
+    xtm = localtime( &unixtime );
+    DOSFS_UnixTimeToFileTime( mktime(xtm), localft, remainder );
     return TRUE; 
 }
 
@@ -1284,19 +1296,68 @@
 BOOL32 FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
 {
     struct tm *xtm;
-    time_t xtime = DOSFS_FileTimeToUnixTime(ft);
+    DWORD remainder;
+    time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
     xtm = gmtime(&xtime);
-    syst->wYear      = xtm->tm_year;
-    syst->wMonth     = xtm->tm_mon;
-    syst->wDayOfWeek = xtm->tm_wday;
-    syst->wDay	     = xtm->tm_mday;
-    syst->wHour	     = xtm->tm_hour;
-    syst->wMinute    = xtm->tm_min;
-    syst->wSecond    = xtm->tm_sec;
-    syst->wMilliseconds	= 0; /* FIXME */
+    syst->wYear         = xtm->tm_year;
+    syst->wMonth        = xtm->tm_mon;
+    syst->wDayOfWeek    = xtm->tm_wday;
+    syst->wDay	        = xtm->tm_mday;
+    syst->wHour	        = xtm->tm_hour;
+    syst->wMinute       = xtm->tm_min;
+    syst->wSecond       = xtm->tm_sec;
+    syst->wMilliseconds	= remainder / 10000;
     return TRUE; 
 }
 
+/***********************************************************************
+ *           QueryDosDeviceA   (KERNEL32.413)
+ *
+ * returns array of strings terminated by \0, terminated by \0
+ */
+DWORD
+QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
+{
+    LPSTR s;
+    char  buffer[200];
+
+    dprintf_dosfs(stddeb,"QueryDosDevice(%s,...)\n",devname?devname:"<null>");
+    if (!devname) {
+	/* return known MSDOS devices */
+	lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
+	while ((s=strchr(buffer,' ')))
+		*s='\0';
+
+	lstrcpyn32A(target,buffer,bufsize);
+	return strlen(buffer);
+    }
+    lstrcpy32A(buffer,"\\DEV\\");
+    lstrcat32A(buffer,devname);
+    if (s=strchr(buffer,':'))
+    	*s='\0';
+    lstrcpyn32A(target,buffer,bufsize);
+    return strlen(buffer);
+}
+
+
+/***********************************************************************
+ *           QueryDosDeviceW   (KERNEL32.414)
+ *
+ * returns array of strings terminated by \0, terminated by \0
+ */
+DWORD
+QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
+{
+    LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
+    LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
+    DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
+
+    lstrcpynAtoW(target,targetA,bufsize);
+    if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
+    if (targetA) HeapFree(GetProcessHeap(),0,targetA);
+    return ret;
+}
+
 
 /***********************************************************************
  *           SystemTimeToFileTime   (KERNEL32.526)
@@ -1312,6 +1373,6 @@
     xtm.tm_hour	= syst->wHour;
     xtm.tm_min	= syst->wMinute;
     xtm.tm_sec	= syst->wSecond;
-    DOSFS_UnixTimeToFileTime(mktime(&xtm),ft);
+    DOSFS_UnixTimeToFileTime( mktime(&xtm), ft, syst->wMilliseconds * 10000 );
     return TRUE; 
 }
diff --git a/files/drive.c b/files/drive.c
index b876675..d59e02b 100644
--- a/files/drive.c
+++ b/files/drive.c
@@ -25,7 +25,6 @@
 
 #include "windows.h"
 #include "winbase.h"
-#include "dos_fs.h"
 #include "drive.h"
 #include "file.h"
 #include "heap.h"
@@ -410,8 +409,9 @@
  */
 int DRIVE_Chdir( int drive, const char *path )
 {
+    DOS_FULL_NAME full_name;
     char buffer[MAX_PATHNAME_LEN];
-    const char *unix_cwd, *dos_cwd;
+    LPSTR unix_cwd;
     BY_HANDLE_FILE_INFORMATION info;
     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
 
@@ -420,31 +420,30 @@
     buffer[0] += drive;
     lstrcpyn32A( buffer + 2, path, sizeof(buffer) - 2 );
 
-    if (!(unix_cwd = DOSFS_GetUnixFileName( buffer, TRUE ))) return 0;
-    if (!FILE_Stat( unix_cwd, &info )) return 0;
+    if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
+    if (!FILE_Stat( full_name.long_name, &info )) return 0;
     if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
     {
         DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
         return 0;
     }
-    unix_cwd += strlen( DOSDrives[drive].root );
+    unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
     while (*unix_cwd == '/') unix_cwd++;
-    buffer[2] = '/';
-    lstrcpyn32A( buffer + 3, unix_cwd, sizeof(buffer) - 3 );
-    if (!(dos_cwd = DOSFS_GetDosTrueName( buffer, TRUE ))) return 0;
 
     dprintf_dosfs( stddeb, "DRIVE_Chdir(%c:): unix_cwd=%s dos_cwd=%s\n",
-                   'A' + drive, unix_cwd, dos_cwd + 3 );
+                   'A' + drive, unix_cwd, full_name.short_name + 3 );
 
     HeapFree( SystemHeap, 0, DOSDrives[drive].dos_cwd );
     HeapFree( SystemHeap, 0, DOSDrives[drive].unix_cwd );
-    DOSDrives[drive].dos_cwd  = HEAP_strdupA( SystemHeap, 0, dos_cwd + 3 );
+    DOSDrives[drive].dos_cwd  = HEAP_strdupA( SystemHeap, 0,
+                                              full_name.short_name + 3 );
     DOSDrives[drive].unix_cwd = HEAP_strdupA( SystemHeap, 0, unix_cwd );
 
     if (pTask && (pTask->curdrive & 0x80) && 
         ((pTask->curdrive & ~0x80) == drive))
     {
-        lstrcpyn32A( pTask->curdir, dos_cwd + 2, sizeof(pTask->curdir) );
+        lstrcpyn32A( pTask->curdir, full_name.short_name + 2,
+                     sizeof(pTask->curdir) );
         DRIVE_LastTask = GetCurrentTask();
     }
     return 1;
diff --git a/files/file.c b/files/file.c
index f841b81..048e58d 100644
--- a/files/file.c
+++ b/files/file.c
@@ -20,7 +20,6 @@
 
 #include "windows.h"
 #include "winerror.h"
-#include "dos_fs.h"
 #include "drive.h"
 #include "file.h"
 #include "global.h"
@@ -212,11 +211,8 @@
 
     if ((file->unix_handle = open( name, mode, 0666 )) == -1)
     {
-        if (Options.allowReadOnly && (mode == O_RDWR))
-        {
-            if ((file->unix_handle = open( name, O_RDONLY )) != -1)
-                fprintf( stderr, "Warning: could not open %s for writing, opening read-only.\n", name );
-        }
+        if (!Options.failReadOnly && (mode == O_RDWR))
+            file->unix_handle = open( name, O_RDONLY );
     }
     if ((file->unix_handle == -1) || (fstat( file->unix_handle, &st ) == -1))
     {
@@ -243,6 +239,7 @@
  */
 HFILE32 FILE_Open( LPCSTR path, INT32 mode )
 {
+    DOS_FULL_NAME full_name;
     const char *unixName;
 
     dprintf_file(stddeb, "FILE_Open: '%s' %04x\n", path, mode );
@@ -257,9 +254,11 @@
         }
     }
     else /* check for filename, don't check for last entry if creating */
-        if (!(unixName = DOSFS_GetUnixFileName( path, !(mode & O_CREAT) )))
+    {
+        if (!DOSFS_GetFullName( path, !(mode & O_CREAT), &full_name ))
             return HFILE_ERROR32;
-
+        unixName = full_name.long_name;
+    }
     return FILE_OpenUnixFile( unixName, mode );
 }
 
@@ -272,6 +271,7 @@
     HFILE32 handle;
     DOS_FILE *file;
     const char *unixName;
+    DOS_FULL_NAME full_name;
 
     dprintf_file(stddeb, "FILE_Create: '%s' %04x %d\n", path, mode, unique );
 
@@ -285,12 +285,12 @@
     if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
         return INVALID_HANDLE_VALUE32;
 
-    if (!(unixName = DOSFS_GetUnixFileName( path, FALSE )))
+    if (!DOSFS_GetFullName( path, FALSE, &full_name ))
     {
         CloseHandle( handle );
         return INVALID_HANDLE_VALUE32;
     }
-    if ((file->unix_handle = open( unixName,
+    if ((file->unix_handle = open( full_name.long_name,
                            O_CREAT | O_TRUNC | O_RDWR | (unique ? O_EXCL : 0),
                            mode )) == -1)
     {
@@ -301,7 +301,7 @@
 
     /* File created OK, now fill the DOS_FILE */
 
-    file->unix_name = HEAP_strdupA( SystemHeap, 0, unixName );
+    file->unix_name = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
     return handle;
 }
 
@@ -317,9 +317,9 @@
     if (S_ISDIR(st->st_mode))
         info->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
 
-    DOSFS_UnixTimeToFileTime( st->st_mtime, &info->ftCreationTime );
-    DOSFS_UnixTimeToFileTime( st->st_mtime, &info->ftLastWriteTime );
-    DOSFS_UnixTimeToFileTime( st->st_atime, &info->ftLastAccessTime );
+    DOSFS_UnixTimeToFileTime( st->st_mtime, &info->ftCreationTime, 0 );
+    DOSFS_UnixTimeToFileTime( st->st_mtime, &info->ftLastWriteTime, 0 );
+    DOSFS_UnixTimeToFileTime( st->st_atime, &info->ftLastAccessTime, 0 );
 
     info->dwVolumeSerialNumber = 0;  /* FIXME */
     info->nFileSizeHigh = 0;
@@ -385,9 +385,11 @@
  */
 DWORD GetFileAttributes32A( LPCSTR name )
 {
+    DOS_FULL_NAME full_name;
     BY_HANDLE_FILE_INFORMATION info;
 
-    if (!FILE_Stat( name, &info )) return -1;
+    if (!DOSFS_GetFullName( name, TRUE, &full_name )) return -1;
+    if (!FILE_Stat( full_name.long_name, &info )) return -1;
     return info.dwFileAttributes;
 }
 
@@ -500,8 +502,9 @@
 UINT32 GetTempFileName32A( LPCSTR path, LPCSTR prefix, UINT32 unique,
                            LPSTR buffer)
 {
-    LPSTR p;
+    DOS_FULL_NAME full_name;
     int i;
+    LPSTR p;
     UINT32 num = unique ? (unique & 0xffff) : time(NULL) & 0xffff;
 
     if (!path) return 0;
@@ -511,36 +514,38 @@
     for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
     sprintf( p, "%04x.tmp", num );
 
-    if (unique)
-    {
-        lstrcpyn32A( buffer, DOSFS_GetDosTrueName( buffer, FALSE ), 144 );
-        dprintf_file( stddeb, "GetTempFileName: returning %s\n", buffer );
-	if (-1==access(DOSFS_GetUnixFileName(buffer,TRUE),W_OK))
-	    fprintf(stderr,"Warning: GetTempFileName returns '%s', which doesn't seem to be writeable. Please check your configuration file if this generates a failure.\n",buffer);
-        return unique;
-    }
-
     /* Now try to create it */
 
-    do
+    if (!unique)
     {
-        HFILE32 handle;
-        if ((handle = FILE_Create(buffer,0666,TRUE)) != INVALID_HANDLE_VALUE32)
-        {  /* We created it */
-            dprintf_file( stddeb, "GetTempFileName: created %s\n", buffer );
-            CloseHandle( handle );
-            break;
-        }
-        if (DOS_ExtendedError != ER_FileExists) break;  /* No need to go on */
-        num++;
-        sprintf( p, "%04x.tmp", num );
-    } while (num != (unique & 0xffff));
+        do
+        {
+            HFILE32 handle = FILE_Create( buffer, 0666, TRUE );
+            if (handle != INVALID_HANDLE_VALUE32)
+            {  /* We created it */
+                dprintf_file( stddeb, "GetTempFileName: created %s\n", buffer);
+                CloseHandle( handle );
+                break;
+            }
+            if (DOS_ExtendedError != ER_FileExists)
+                break;  /* No need to go on */
+            num++;
+            sprintf( p, "%04x.tmp", num );
+        } while (num != (unique & 0xffff));
+    }
 
-    lstrcpyn32A( buffer, DOSFS_GetDosTrueName( buffer, FALSE ), 144 );
+    /* Get the full path name */
+
+    if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
+    {
+        if (access( full_name.long_name, W_OK ) == -1)
+            fprintf( stderr,
+                     "Warning: GetTempFileName returns '%s', which doesn't seem to be writeable.\n"
+                     "Please check your configuration file if this generates a failure.\n",
+                     buffer);
+    }
     dprintf_file( stddeb, "GetTempFileName: returning %s\n", buffer );
-    if (-1==access(DOSFS_GetUnixFileName(buffer,TRUE),W_OK))
-	fprintf(stderr,"Warning: GetTempFileName returns '%s', which doesn't seem to be writeable. Please check your configuration file if this generates a failure.\n",buffer);
-    return num;
+    return unique ? unique : num;
 }
 
 
@@ -576,7 +581,7 @@
     HFILE32 hFileRet;
     FILETIME filetime;
     WORD filedatetime[2];
-    const char *unixName, *dosName;
+    DOS_FULL_NAME full_name;
     char *p;
     int unixMode;
 
@@ -589,9 +594,10 @@
 
     if (mode & OF_PARSE)
     {
-        if (!(dosName = DOSFS_GetDosTrueName( name, FALSE ))) goto error;
-        lstrcpyn32A( ofs->szPathName, dosName, sizeof(ofs->szPathName) );
-        ofs->fFixedDisk = (GetDriveType16( dosName[0]-'A' ) != DRIVE_REMOVABLE);
+        if (!GetFullPathName32A( name, sizeof(ofs->szPathName),
+                                 ofs->szPathName, NULL )) goto error;
+        ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
+                           != DRIVE_REMOVABLE);
         dprintf_file( stddeb, "OpenFile(%s): OF_PARSE, res = '%s'\n",
                       name, ofs->szPathName );
         return 0;
@@ -604,8 +610,8 @@
     {
         if ((hFileRet = FILE_Create(name,0666,FALSE))== INVALID_HANDLE_VALUE32)
             goto error;
-        lstrcpyn32A( ofs->szPathName, DOSFS_GetDosTrueName( name, FALSE ),
-                     sizeof(ofs->szPathName) );
+        GetFullPathName32A( name, sizeof(ofs->szPathName),
+                            ofs->szPathName, NULL );
         goto success;
     }
 
@@ -614,11 +620,7 @@
     if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
     {
         /* First try the file name as is */
-        if ((unixName = DOSFS_GetUnixFileName( name, TRUE )) != NULL)
-        {
-            lstrcpyn32A( ofs->szPathName, name, sizeof(ofs->szPathName) );
-            goto found;
-        }
+        if (DOSFS_GetFullName( name, TRUE, &full_name )) goto found;
         /* Now remove the path */
         if (name[0] && (name[1] == ':')) name += 2;
         if ((p = strrchr( name, '\\' ))) name = p + 1;
@@ -628,20 +630,17 @@
 
     /* Now look for the file */
 
-    if (!DIR_SearchPath( NULL, name, NULL, sizeof(ofs->szPathName),
-                         ofs->szPathName, NULL, win32 ))
-        goto not_found;
-    if (!(unixName = DOSFS_GetUnixFileName( ofs->szPathName, TRUE )))
-        goto not_found;
+    if (!DIR_SearchPath( NULL, name, NULL, &full_name, win32 )) goto not_found;
 
 found:
-    dprintf_file( stddeb, "OpenFile: found '%s'\n", unixName );
-    lstrcpyn32A(ofs->szPathName, DOSFS_GetDosTrueName( ofs->szPathName, FALSE),
-                sizeof(ofs->szPathName) );
+    dprintf_file( stddeb, "OpenFile: found %s = %s\n",
+                  full_name.long_name, full_name.short_name );
+    lstrcpyn32A( ofs->szPathName, full_name.short_name,
+                 sizeof(ofs->szPathName) );
 
     if (mode & OF_DELETE)
     {
-        if (unlink( unixName ) == -1) goto not_found;
+        if (unlink( full_name.long_name ) == -1) goto not_found;
         dprintf_file( stddeb, "OpenFile(%s): OF_DELETE return = OK\n", name);
         return 1;
     }
@@ -657,8 +656,8 @@
         unixMode = O_RDONLY; break;
     }
 
-    if ((hFileRet = FILE_OpenUnixFile( unixName, unixMode )) == HFILE_ERROR32)
-        goto not_found;
+    hFileRet = FILE_OpenUnixFile( full_name.long_name, unixMode );
+    if (hFileRet == HFILE_ERROR32) goto not_found;
     GetFileTime( hFileRet, NULL, NULL, &filetime );
     FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
     if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
@@ -715,7 +714,7 @@
  */
 HFILE16 _lclose16( HFILE16 hFile )
 {
-    dprintf_file( stddeb, "_lclose: handle %d\n", hFile );
+    dprintf_file( stddeb, "_lclose16: handle %d\n", hFile );
     return CloseHandle( hFile ) ? 0 : HFILE_ERROR16;
 }
 
@@ -725,7 +724,7 @@
  */
 HFILE32 _lclose32( HFILE32 hFile )
 {
-    dprintf_file( stddeb, "_lclose: handle %d\n", hFile );
+    dprintf_file( stddeb, "_lclose32: handle %d\n", hFile );
     return CloseHandle( hFile ) ? 0 : HFILE_ERROR32;
 }
 
@@ -817,11 +816,44 @@
 
 
 /***********************************************************************
+ *           SetFilePointer   (KERNEL32.492)
+ */
+DWORD SetFilePointer( HFILE32 hFile, LONG distance, LONG *highword,
+                      DWORD method )
+{
+    DOS_FILE *file;
+    int origin, result;
+
+    if (highword && *highword)
+    {
+        fprintf( stderr, "SetFilePointer: 64-bit offsets not supported yet\n");
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return 0xffffffff;
+    }
+    dprintf_file( stddeb, "SetFilePointer: handle %d offset %ld origin %ld\n",
+                  hFile, distance, method );
+
+    if (!(file = FILE_GetFile( hFile ))) return 0xffffffff;
+    switch(method)
+    {
+        case 1:  origin = SEEK_CUR; break;
+        case 2:  origin = SEEK_END; break;
+        default: origin = SEEK_SET; break;
+    }
+
+    if ((result = lseek( file->unix_handle, distance, origin )) == -1)
+        FILE_SetDosError();
+    FILE_ReleaseFile( file );
+    return (DWORD)result;
+}
+
+
+/***********************************************************************
  *           _llseek16   (KERNEL.84)
  */
 LONG _llseek16( HFILE16 hFile, LONG lOffset, INT16 nOrigin )
 {
-    return _llseek32( hFile, lOffset, nOrigin );
+    return SetFilePointer( hFile, lOffset, NULL, nOrigin );
 }
 
 
@@ -830,24 +862,7 @@
  */
 LONG _llseek32( HFILE32 hFile, LONG lOffset, INT32 nOrigin )
 {
-    DOS_FILE *file;
-    int origin, result;
-
-    dprintf_file( stddeb, "_llseek: handle %d, offset %ld, origin %d\n", 
-                  hFile, lOffset, nOrigin);
-
-    if (!(file = FILE_GetFile( hFile ))) return HFILE_ERROR32;
-    switch(nOrigin)
-    {
-        case 1:  origin = SEEK_CUR; break;
-        case 2:  origin = SEEK_END; break;
-        default: origin = SEEK_SET; break;
-    }
-
-    if ((result = lseek( file->unix_handle, lOffset, origin )) == -1)
-        FILE_SetDosError();
-    FILE_ReleaseFile( file );
-    return result;
+    return SetFilePointer( hFile, lOffset, NULL, nOrigin );
 }
 
 
@@ -961,47 +976,27 @@
     HGLOBAL16 hPDB = GetCurrentPDB();
     PDB *pdb = (PDB *)GlobalLock16( hPDB );
     BYTE *files = PTR_SEG_TO_LIN( pdb->fileHandlesPtr );
-    WORD i;
 
     dprintf_file( stddeb, "SetHandleCount(%d)\n", count );
 
     if (count < 20) count = 20;  /* No point in going below 20 */
     else if (count > 254) count = 254;
 
-    /* If shrinking the table, make sure all extra file handles are closed */
-    if (count < pdb->nbFiles)
-    {
-        for (i = count; i < pdb->nbFiles; i++)
-            if (files[i] != 0xff)  /* File open */
-            {
-                DOS_ERROR( ER_TooManyOpenFiles, EC_ProgramError,
-                           SA_Abort, EL_Disk );
-                return pdb->nbFiles;
-            }
-    }
-
     if (count == 20)
     {
         if (pdb->nbFiles > 20)
         {
             memcpy( pdb->fileHandles, files, 20 );
-#ifdef WINELIB
-            GlobalFree32( (HGLOBAL32)pdb->fileHandlesPtr );
-            pdb->fileHandlesPtr = (SEGPTR)pdb->fileHandles;
-#else
-            GlobalFree16( GlobalHandle16( SELECTOROF(pdb->fileHandlesPtr) ));
+            GlobalFree16( pdb->hFileHandles );
             pdb->fileHandlesPtr = (SEGPTR)MAKELONG( 0x18,
                                                    GlobalHandleToSel( hPDB ) );
-#endif
+            pdb->hFileHandles = 0;
             pdb->nbFiles = 20;
         }
     }
     else  /* More than 20, need a new file handles table */
     {
         BYTE *newfiles;
-#ifdef WINELIB
-        newfiles = (BYTE *)GlobalAlloc32( GMEM_FIXED, count );
-#else
         HGLOBAL16 newhandle = GlobalAlloc16( GMEM_MOVEABLE, count );
         if (!newhandle)
         {
@@ -1009,21 +1004,16 @@
             return pdb->nbFiles;
         }
         newfiles = (BYTE *)GlobalLock16( newhandle );
-#endif  /* WINELIB */
+
         if (count > pdb->nbFiles)
         {
             memcpy( newfiles, files, pdb->nbFiles );
             memset( newfiles + pdb->nbFiles, 0xff, count - pdb->nbFiles );
         }
         else memcpy( newfiles, files, count );
-#ifdef WINELIB
-        if (pdb->nbFiles > 20) GlobalFree32( (HGLOBAL32)pdb->fileHandlesPtr );
-        pdb->fileHandlesPtr = (SEGPTR)newfiles;
-#else
-        if (pdb->nbFiles > 20)
-            GlobalFree16( GlobalHandle16( SELECTOROF(pdb->fileHandlesPtr) ));
+        if (pdb->nbFiles > 20) GlobalFree16( pdb->hFileHandles );
         pdb->fileHandlesPtr = WIN16_GlobalLock16( newhandle );
-#endif  /* WINELIB */
+        pdb->hFileHandles   = newhandle;
         pdb->nbFiles = count;
     }
     return pdb->nbFiles;
@@ -1095,6 +1085,7 @@
  */
 BOOL32 DeleteFile32A( LPCSTR path )
 {
+    DOS_FULL_NAME full_name;
     const char *unixName;
 
     dprintf_file(stddeb, "DeleteFile: '%s'\n", path );
@@ -1106,8 +1097,8 @@
         return FALSE;
     }
 
-    if (!(unixName = DOSFS_GetUnixFileName( path, TRUE ))) return FALSE;
-    if (unlink( unixName ) == -1)
+    if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
+    if (unlink( full_name.long_name ) == -1)
     {
         FILE_SetDosError();
         return FALSE;
@@ -1153,6 +1144,99 @@
 }
 
 
+/**************************************************************************
+ *           MoveFile32A   (KERNEL32.387)
+ */
+BOOL32 MoveFile32A( LPCSTR fn1, LPCSTR fn2 )
+{
+    DOS_FULL_NAME full_name1, full_name2;
+
+    dprintf_file( stddeb, "MoveFile32A(%s,%s)\n", fn1, fn2 );
+
+    if (!DOSFS_GetFullName( fn1, TRUE, &full_name1 )) return FALSE;
+    if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 )) return FALSE;
+    /* FIXME: should not replace an existing file */
+    /* FIXME: should handle renaming across devices */
+    if (rename( full_name1.long_name, full_name2.long_name ) == -1)
+    {
+        FILE_SetDosError();
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
+/**************************************************************************
+ *           MoveFile32W   (KERNEL32.390)
+ */
+BOOL32 MoveFile32W( LPCWSTR fn1, LPCWSTR fn2 )
+{
+    LPSTR afn1 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn1 );
+    LPSTR afn2 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn2 );
+    BOOL32 res = MoveFile32A( afn1, afn2 );
+    HeapFree( GetProcessHeap(), 0, afn1 );
+    HeapFree( GetProcessHeap(), 0, afn2 );
+    return res;
+}
+
+
+/**************************************************************************
+ *           CopyFile32A   (KERNEL32.36)
+ */
+BOOL32 CopyFile32A( LPCSTR source, LPCSTR dest, BOOL32 fail_if_exists )
+{
+    HFILE32 h1, h2;
+    BY_HANDLE_FILE_INFORMATION info;
+    UINT32 count;
+    BOOL32 ret = FALSE;
+    int mode;
+    char buffer[2048];
+
+    if ((h1 = _lopen32( source, OF_READ )) == HFILE_ERROR32) return FALSE;
+    if (!GetFileInformationByHandle( h1, &info ))
+    {
+        CloseHandle( h1 );
+        return FALSE;
+    }
+    mode = (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666;
+    if ((h2 = FILE_Create( dest, mode, fail_if_exists )) == HFILE_ERROR32)
+    {
+        CloseHandle( h1 );
+        return FALSE;
+    }
+    while ((count = _lread32( h2, buffer, sizeof(buffer) )) > 0)
+    {
+        char *p = buffer;
+        while (count > 0)
+        {
+            INT32 res = _lwrite32( h2, p, count );
+            if (res <= 0) goto done;
+            p += res;
+            count -= res;
+        }
+    }
+    ret =  TRUE;
+done:
+    CloseHandle( h1 );
+    CloseHandle( h2 );
+    return ret;
+}
+
+
+/**************************************************************************
+ *           CopyFile32W   (KERNEL32.37)
+ */
+BOOL32 CopyFile32W( LPCWSTR source, LPCWSTR dest, BOOL32 fail_if_exists )
+{
+    LPSTR sourceA = HEAP_strdupWtoA( GetProcessHeap(), 0, source );
+    LPSTR destA   = HEAP_strdupWtoA( GetProcessHeap(), 0, dest );
+    BOOL32 ret = CopyFile32A( sourceA, destA, fail_if_exists );
+    HeapFree( GetProcessHeap(), 0, sourceA );
+    HeapFree( GetProcessHeap(), 0, destA );
+    return ret;
+}
+
+
 /***********************************************************************
  *              SetFileTime   (KERNEL32.493)
  */
@@ -1172,11 +1256,11 @@
 	lpLastWriteTime
     );
     if (lpLastAccessTime)
-	utimbuf.actime	= DOSFS_FileTimeToUnixTime(lpLastAccessTime);
+	utimbuf.actime	= DOSFS_FileTimeToUnixTime(lpLastAccessTime, NULL);
     else
 	utimbuf.actime	= 0; /* FIXME */
     if (lpLastWriteTime)
-	utimbuf.modtime	= DOSFS_FileTimeToUnixTime(lpLastWriteTime);
+	utimbuf.modtime	= DOSFS_FileTimeToUnixTime(lpLastWriteTime, NULL);
     else
 	utimbuf.modtime	= 0; /* FIXME */
     if (-1==utime(file->unix_name,&utimbuf))
diff --git a/files/profile.c b/files/profile.c
index 393fe48..06b8b70 100644
--- a/files/profile.c
+++ b/files/profile.c
@@ -11,7 +11,7 @@
 #include <string.h>
 
 #include "windows.h"
-#include "dos_fs.h"
+#include "file.h"
 #include "heap.h"
 #include "stddebug.h"
 #include "debug.h"
@@ -36,6 +36,8 @@
     BOOL32           changed;
     PROFILESECTION  *section;
     char            *dos_name;
+    char            *unix_name;
+    char            *filename;
 } PROFILE;
 
 
@@ -332,8 +334,7 @@
     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" )))
+    if (!(unix_name = CurProfile.unix_name) || !(file = fopen(unix_name, "w")))
     {
         /* Try to create it in $HOME/.wine */
         /* FIXME: this will need a more general solution */
@@ -343,7 +344,7 @@
             strcat( buffer, "/.wine/" );
             p = buffer + strlen(buffer);
             strcpy( p, strrchr( CurProfile.dos_name, '\\' ) + 1 );
-            AnsiLower( p );
+            CharLower32A( p );
             file = fopen( buffer, "w" );
             unix_name = buffer;
         }
@@ -372,24 +373,32 @@
  */
 static BOOL32 PROFILE_Open( LPCSTR filename )
 {
+    DOS_FULL_NAME full_name;
     char buffer[MAX_PATHNAME_LEN];
-    const char *dos_name, *unix_name;
     char *newdos_name, *p;
     FILE *file = NULL;
 
+    if (CurProfile.filename && !strcmp( filename, CurProfile.filename ))
+    {
+        dprintf_profile( stddeb, "PROFILE_Open(%s): already opened\n",
+                         filename );
+        return TRUE;
+    }
+
     if (strchr( filename, '/' ) || strchr( filename, '\\' ) || 
         strchr( filename, ':' ))
     {
-        if (!(dos_name = DOSFS_GetDosTrueName( filename, FALSE))) return FALSE;
+        if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
     }
     else
     {
         GetWindowsDirectory32A( buffer, sizeof(buffer) );
         strcat( buffer, "\\" );
         strcat( buffer, filename );
-        if (!(dos_name = DOSFS_GetDosTrueName( buffer, FALSE ))) return FALSE;
+        if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
     }
-    if (CurProfile.dos_name && !strcmp( dos_name, CurProfile.dos_name ))
+    if (CurProfile.dos_name &&
+        !strcmp( full_name.short_name, CurProfile.dos_name ))
     {
         dprintf_profile( stddeb, "PROFILE_Open(%s): already opened\n",
                          filename );
@@ -398,12 +407,15 @@
 
     /* Flush the previous profile */
 
-    newdos_name = HEAP_strdupA( SystemHeap, 0, dos_name );
+    newdos_name = HEAP_strdupA( SystemHeap, 0, full_name.short_name );
     PROFILE_FlushFile();
     PROFILE_Free( CurProfile.section );
     if (CurProfile.dos_name) HeapFree( SystemHeap, 0, CurProfile.dos_name );
+    if (CurProfile.unix_name) HeapFree( SystemHeap, 0, CurProfile.unix_name );
+    if (CurProfile.filename) HeapFree( SystemHeap, 0, CurProfile.filename );
     CurProfile.section   = NULL;
     CurProfile.dos_name  = newdos_name;
+    CurProfile.filename  = HEAP_strdupA( SystemHeap, 0, filename );
 
     /* Try to open the profile file, first in $HOME/.wine */
 
@@ -414,15 +426,22 @@
         strcat( buffer, "/.wine/" );
         p = buffer + strlen(buffer);
         strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
-        AnsiLower( p );
+        CharLower32A( p );
         if ((file = fopen( buffer, "r" )))
-            dprintf_profile( stddeb, "Found it in %s\n", buffer );
+        {
+            dprintf_profile( stddeb, "PROFILE_Open(%s): found it in %s\n",
+                             filename, buffer );
+            CurProfile.unix_name = HEAP_strdupA( SystemHeap, 0, buffer );
+        }
     }
 
-    if (!file && ((unix_name = DOSFS_GetUnixFileName( dos_name, TRUE ))))
+    if (!file)
     {
-        if ((file = fopen( unix_name, "r" )))
-            dprintf_profile( stddeb, "Found it in %s\n", unix_name );
+        CurProfile.unix_name = HEAP_strdupA( SystemHeap, 0,
+                                             full_name.long_name );
+        if ((file = fopen( full_name.long_name, "r" )))
+            dprintf_profile( stddeb, "PROFILE_Open(%s): found it in %s\n",
+                             filename, full_name.long_name );
     }
 
     if (file)
@@ -435,7 +454,6 @@
         /* Does not exist yet, we will create it in PROFILE_FlushFile */
         fprintf( stderr, "Warning: profile file %s not found\n", newdos_name );
     }
-    dprintf_profile( stddeb, "PROFILE_Open(%s): successful\n", filename );
     return TRUE;
 }