- reimplements Get{Full|Short|Long}PathName using only ntdll or kernel32 APIs
- fixes a couple of bugs in previous implementation
- removed the (now useless) todo_wine:s in the regression tests
- added invalid character testing in RtlIsNameLegalDOS8Dot3
- moved these APIs to kernel32/path.c (new file)
A+ -- Eric Pouech
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel38/Makefile.in dlls/kernel/Makefile.in --- dlls/kernel38/Makefile.in 2003-12-06 16:09:08.000000000 +0100 +++ dlls/kernel/Makefile.in 2003-12-12 21:33:31.000000000 +0100 @@ -51,6 +51,7 @@ module.c \ ne_module.c \ ne_segment.c \ + path.c \ powermgnt.c \ process.c \ profile.c \ diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel38/path.c dlls/kernel/path.c --- dlls/kernel38/path.c 1970-01-01 01:00:00.000000000 +0100 +++ dlls/kernel/path.c 2003-12-13 10:30:11.000000000 +0100 @@ -0,0 +1,440 @@ +/* + * File handling functions + * + * Copyright 1993 Erik Bos + * Copyright 1996 Alexandre Julliard + * Copyright 2003 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "wine/port.h" + +#include <stdarg.h> + +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "winerror.h" +#include "ntstatus.h" +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "wincon.h" +#include "wine/winbase16.h" +#include "kernel_private.h" + +#include "wine/unicode.h" +#include "wine/debug.h" +#include "async.h" + +WINE_DEFAULT_DEBUG_CHANNEL(file); + +#define MAX_PATHNAME_LEN 1024 + + +/*********************************************************************** + * GetFullPathNameW (KERNEL32.@) + * NOTES + * if the path closed with '\', *lastpart is 0 + */ +DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer, + LPWSTR *lastpart ) +{ + LPWSTR dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(buffer)); + DWORD ret; + + if (!dst) return 0; + ret = RtlGetFullPathName_U(name, len * sizeof(WCHAR), dst, lastpart) / sizeof(WCHAR); + if (ret < len) + { + strcpyW(buffer, dst); + if (lastpart && *lastpart) *lastpart = buffer + (*lastpart - dst); + } + HeapFree(GetProcessHeap(), 0, dst); + return ret; +} + +/*********************************************************************** + * GetFullPathNameA (KERNEL32.@) + * NOTES + * if the path closed with '\', *lastpart is 0 + */ +DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer, + LPSTR *lastpart ) +{ + UNICODE_STRING nameW; + WCHAR bufferW[MAX_PATH]; + DWORD ret, retW; + + if (!name) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + + retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL); + + if (!retW) + ret = 0; + else if (retW > MAX_PATH) + { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + ret = 0; + } + else + { + ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); + if (ret && ret <= len) + { + WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL); + ret--; /* length without 0 */ + + if (lastpart) + { + LPSTR p = buffer + strlen(buffer) - 1; + + if (*p != '\\') + { + while ((p > buffer + 2) && (*p != '\\')) p--; + *lastpart = p + 1; + } + else *lastpart = NULL; + } + } + } + + RtlFreeUnicodeString(&nameW); + return ret; +} + + +/*********************************************************************** + * GetLongPathNameW (KERNEL32.@) + * + * NOTES + * observed (Win2000): + * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0 + * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0 + */ +DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen ) +{ + WCHAR tmplongpath[MAX_PATHNAME_LEN]; + LPCWSTR p; + DWORD sp = 0, lp = 0; + DWORD tmplen; + BOOL unixabsolute = (shortpath[0] == '/'); + WIN32_FIND_DATAW wfd; + HANDLE goit; + + if (!shortpath) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if (!shortpath[0]) + { + SetLastError(ERROR_PATH_NOT_FOUND); + return 0; + } + + TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen); + + if (shortpath[0] == '\\' && shortpath[1] == '\\') + { + ERR("UNC pathname %s\n", debugstr_w(shortpath)); + lstrcpynW( longpath, shortpath, longlen ); + return strlenW(longpath); + } + + /* check for drive letter */ + if (!unixabsolute && shortpath[1] == ':' ) + { + tmplongpath[0] = shortpath[0]; + tmplongpath[1] = ':'; + lp = sp = 2; + } + + while (shortpath[sp]) + { + /* check for path delimiters and reproduce them */ + if (shortpath[sp] == '\\' || shortpath[sp] == '/') + { + if (!lp || tmplongpath[lp-1] != '\\') + { + /* strip double "\\" */ + tmplongpath[lp++] = '\\'; + } + tmplongpath[lp] = 0; /* terminate string */ + sp++; + continue; + } + + p = shortpath + sp; + if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\')) + { + tmplongpath[lp++] = *p++; + tmplongpath[lp++] = *p++; + } + for (; *p && *p != '/' && *p != '\\'; p++); + tmplen = p - (shortpath + sp); + lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1); + /* Check if the file exists and use the existing file name */ + goit = FindFirstFileW(tmplongpath, &wfd); + if (goit == INVALID_HANDLE_VALUE) + { + TRACE("not found %s!\n", debugstr_w(tmplongpath)); + SetLastError ( ERROR_FILE_NOT_FOUND ); + return 0; + } + FindClose(goit); + strcpyW(tmplongpath + lp, wfd.cFileName); + lp += strlenW(tmplongpath + lp); + sp += tmplen; + } + tmplen = strlenW(shortpath) - 1; + if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') && + (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\')) + tmplongpath[lp++] = shortpath[tmplen]; + tmplongpath[lp] = 0; + + tmplen = strlenW(tmplongpath) + 1; + if (tmplen <= longlen) + { + strcpyW(longpath, tmplongpath); + TRACE("returning %s\n", debugstr_w(longpath)); + tmplen--; /* length without 0 */ + } + + return tmplen; +} + +/*********************************************************************** + * GetLongPathNameA (KERNEL32.@) + */ +DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen ) +{ + UNICODE_STRING shortpathW; + WCHAR longpathW[MAX_PATH]; + DWORD ret, retW; + + if (!shortpath) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + TRACE("%s\n", debugstr_a(shortpath)); + + if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + + retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH); + + if (!retW) + ret = 0; + else if (retW > MAX_PATH) + { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + ret = 0; + } + else + { + ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL); + if (ret <= longlen) + { + WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL); + ret--; /* length without 0 */ + } + } + + RtlFreeUnicodeString(&shortpathW); + return ret; +} + + +/*********************************************************************** + * GetShortPathNameW (KERNEL32.@) + * + * NOTES + * observed: + * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0 + * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0 + * + * more observations ( with NT 3.51 (WinDD) ): + * longpath <= 8.3 -> just copy longpath to shortpath + * longpath > 8.3 -> + * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND + * b) file does exist -> set the short filename. + * - trailing slashes are reproduced in the short name, even if the + * file is not a directory + * - the absolute/relative path of the short name is reproduced like found + * in the long name + * - longpath and shortpath may have the same address + * Peter Ganten, 1999 + */ +DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen ) +{ + WCHAR tmpshortpath[MAX_PATHNAME_LEN]; + LPCWSTR p; + DWORD sp = 0, lp = 0; + DWORD tmplen; + BOOL unixabsolute = (longpath[0] == '/'); + WIN32_FIND_DATAW wfd; + HANDLE goit; + UNICODE_STRING ustr; + WCHAR ustr_buf[8+1+3+1]; + + TRACE("%s\n", debugstr_w(longpath)); + + if (!longpath) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if (!longpath[0]) + { + SetLastError(ERROR_BAD_PATHNAME); + return 0; + } + + /* check for drive letter */ + if (!unixabsolute && longpath[1] == ':' ) + { + tmpshortpath[0] = longpath[0]; + tmpshortpath[1] = ':'; + sp = lp = 2; + } + + ustr.Buffer = ustr_buf; + ustr.Length = 0; + ustr.MaximumLength = sizeof(ustr_buf); + + while (longpath[lp]) + { + /* check for path delimiters and reproduce them */ + if (longpath[lp] == '\\' || longpath[lp] == '/') + { + if (!sp || tmpshortpath[sp-1] != '\\') + { + /* strip double "\\" */ + tmpshortpath[sp] = '\\'; + sp++; + } + tmpshortpath[sp] = 0; /* terminate string */ + lp++; + continue; + } + + for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++); + tmplen = p - (longpath + lp); + lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1); + if (tmplen <= 8+1+3+1) + { + memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR)); + ustr_buf[tmplen] = '\0'; + ustr.Length = tmplen * sizeof(WCHAR); + /* Check, if the current element is a valid dos name */ + if (!RtlIsNameLegalDOS8Dot3(&ustr, NULL, NULL)) + goto notfound; + sp += tmplen; + lp += tmplen; + continue; + } + + /* Check if the file exists and use the existing file name */ + goit = FindFirstFileW(tmpshortpath, &wfd); + if (goit == INVALID_HANDLE_VALUE) goto notfound; + FindClose(goit); + strcpyW(tmpshortpath + sp, wfd.cAlternateFileName); + sp += strlenW(tmpshortpath + sp); + lp += tmplen; + } + tmpshortpath[sp] = 0; + + tmplen = strlenW(tmpshortpath) + 1; + if (tmplen <= shortlen) + { + strcpyW(shortpath, tmpshortpath); + TRACE("returning %s\n", debugstr_w(shortpath)); + tmplen--; /* length without 0 */ + } + + return tmplen; + + notfound: + TRACE("not found!\n" ); + SetLastError ( ERROR_FILE_NOT_FOUND ); + return 0; +} + +/*********************************************************************** + * GetShortPathNameA (KERNEL32.@) + */ +DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen ) +{ + UNICODE_STRING longpathW; + WCHAR shortpathW[MAX_PATH]; + DWORD ret, retW; + + if (!longpath) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + TRACE("%s\n", debugstr_a(longpath)); + + if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + + retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH); + + if (!retW) + ret = 0; + else if (retW > MAX_PATH) + { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + ret = 0; + } + else + { + ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL); + if (ret <= shortlen) + { + WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL); + ret--; /* length without 0 */ + } + } + + RtlFreeUnicodeString(&longpathW); + return ret; +} + + diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel38/tests/path.c dlls/kernel/tests/path.c --- dlls/kernel38/tests/path.c 2003-12-06 16:08:32.000000000 +0100 +++ dlls/kernel/tests/path.c 2003-12-13 10:53:55.000000000 +0100 @@ -112,14 +112,11 @@ len=GetFullPathNameA(subpath,MAX_PATH,tmpstr,&strptr); ok(len, "GetFullPathNameA failed for: '%s'",subpath); if(HAS_TRAIL_SLASH_A(subpath)) { -/* Wine strips off the trailing '\\'. Neither Win98 nor Win2k do this. */ - todo_wine { - ok(strptr==NULL, - "%s: GetFullPathNameA should not return a filename ptr",errstr); - ok(lstrcmpiA(fullpath,tmpstr)==0, - "%s: GetFullPathNameA returned '%s' instead of '%s'", - errstr,tmpstr,fullpath); - } + ok(strptr==NULL, + "%s: GetFullPathNameA should not return a filename ptr %s=>%s",subpath,strptr,errstr); + ok(lstrcmpiA(fullpath,tmpstr)==0, + "%s: GetFullPathNameA returned '%s' instead of '%s'", + errstr,tmpstr,fullpath); } else { ok(lstrcmpiA(strptr,filename)==0, "%s: GetFullPathNameA returned '%s' instead of '%s'", @@ -141,7 +138,7 @@ We test both conversion from GetFullPathNameA and from GetShortPathNameA */ if(pGetLongPathNameA) { - if(len==0) { + if(len!=0) { SetLastError(0); len=pGetLongPathNameA(shortstr,tmpstr,MAX_PATH); if(passfail==NULL) { @@ -160,12 +157,9 @@ if(passfail==NULL) { ok(len, "%s: GetLongPathNameA failed",errstr); if(HAS_TRAIL_SLASH_A(fullpath)) { -/* Wine strips off the trailing '\\' Neither Win98 nor Win2k do this */ - todo_wine { - ok(lstrcmpiA(fullpathlong,tmpstr)==0, + ok(lstrcmpiA(fullpathlong,tmpstr)==0, "%s: GetLongPathNameA returned '%s' instead of '%s'", errstr,tmpstr,fullpathlong); - } } else { ok(lstrcmpiA(fullpathlong,tmpstr)==0, "%s: GetLongPathNameA returned '%s' instead of '%s'", @@ -229,7 +223,7 @@ /* Test that Get(Short|Long|Full)PathNameA work correctly with interesting characters in the filename. 'valid' indicates whether this would be an allowed filename - 'todo' indictaes that wine doesn't get this right yet. + 'todo' indicates that wine doesn't get this right yet. NOTE: We always call this routine with a non-existent filename, so Get(Short|Long)PathNameA should never pass, but GetFullPathNameA should. @@ -518,13 +512,11 @@ "GetLongPathNameA: wrong return code, %ld instead of %d", rc1, strlen(tmpstr)+1); - todo_wine { - sprintf(dir,"%c:",curDrive); - rc1=(*pGetLongPathNameA)(dir,tmpstr,sizeof(tmpstr)); - ok(strcmp(dir,tmpstr)==0, - "GetLongPathNameA: returned '%s' instead of '%s' (rc=%ld)", - tmpstr,dir,rc1); - } + sprintf(dir,"%c:",curDrive); + rc1=(*pGetLongPathNameA)(dir,tmpstr,sizeof(tmpstr)); + ok(strcmp(dir,tmpstr)==0, + "GetLongPathNameA: returned '%s' instead of '%s' (rc=%ld)", + tmpstr,dir,rc1); } /* Check the cases where both file and directory exist first */ @@ -773,11 +765,9 @@ ok(GetShortPathNameA(LONGDIR,tmpstr,MAX_PATH),"GetShortPathNameA failed"); test_SplitShortPathA(tmpstr,dir,eight,three); if(pGetLongPathNameA) { - ok(pGetLongPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetShortPathNameA failed"); - todo_wine { - ok(lstrcmpiA(tmpstr1,LONGDIR)==0, - "GetLongPathNameA returned '%s' instead of '%s'",tmpstr1,LONGDIR); - } + ok(pGetLongPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetLongPathNameA failed"); + ok(lstrcmpiA(tmpstr1,LONGDIR)==0, + "GetLongPathNameA returned '%s' instead of '%s'",tmpstr1,LONGDIR); } sprintf(tmpstr,".\\%s",LONGDIR); ok(GetShortPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetShortPathNameA failed"); @@ -785,11 +775,10 @@ ok(lstrcmpiA(dir,".")==0 || dir[0]=='\0', "GetShortPathNameA did not keep relative directory [%s]",tmpstr1); if(pGetLongPathNameA) { - ok(pGetLongPathNameA(tmpstr1,tmpstr1,MAX_PATH),"GetShortPathNameA failed"); - todo_wine { - ok(lstrcmpiA(tmpstr1,tmpstr)==0, - "GetLongPathNameA returned '%s' instead of '%s'",tmpstr1,tmpstr); - } + ok(pGetLongPathNameA(tmpstr1,tmpstr1,MAX_PATH),"GetLongPathNameA failed %s", + tmpstr); + ok(lstrcmpiA(tmpstr1,tmpstr)==0, + "GetLongPathNameA returned '%s' instead of '%s'",tmpstr1,tmpstr); } /* Check out Get*PathNameA on some funny characters */ for(i=0;i<lstrlenA(funny_chars);i++) { diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll38/path.c dlls/ntdll/path.c --- dlls/ntdll38/path.c 2003-12-06 16:08:36.000000000 +0100 +++ dlls/ntdll/path.c 2003-12-13 10:52:49.000000000 +0100 @@ -614,6 +614,7 @@ BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode, OEM_STRING *oem, BOOLEAN *spaces ) { + static const char* illegal = "*?<>|\"+=,;[]:/\\\345"; int dot = -1; unsigned int i; char buffer[12]; @@ -654,7 +655,7 @@ dot = i; break; default: - /* FIXME: check for invalid chars */ + if (strchr(illegal, oem->Buffer[i])) return FALSE; break; } } diff -u -N -r -x '*~' -x '.#*' -x CVS files38/dos_fs.c files/dos_fs.c --- files38/dos_fs.c 2003-12-07 11:24:07.000000000 +0100 +++ files/dos_fs.c 2003-12-13 10:23:45.000000000 +0100 @@ -1173,527 +1173,6 @@ /*********************************************************************** - * GetShortPathNameW (KERNEL32.@) - * - * NOTES - * observed: - * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0 - * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0 - * - * more observations ( with NT 3.51 (WinDD) ): - * longpath <= 8.3 -> just copy longpath to shortpath - * longpath > 8.3 -> - * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND - * b) file does exist -> set the short filename. - * - trailing slashes are reproduced in the short name, even if the - * file is not a directory - * - the absolute/relative path of the short name is reproduced like found - * in the long name - * - longpath and shortpath may have the same address - * Peter Ganten, 1999 - */ -DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen ) -{ - DOS_FULL_NAME full_name; - WCHAR tmpshortpath[MAX_PATHNAME_LEN]; - const WCHAR *p; - DWORD sp = 0, lp = 0; - int drive; - DWORD tmplen; - UINT flags; - BOOL unixabsolute = *longpath == '/'; - - TRACE("%s\n", debugstr_w(longpath)); - - if (!longpath) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - if (!longpath[0]) { - SetLastError(ERROR_BAD_PATHNAME); - return 0; - } - - /* check for drive letter */ - if (!unixabsolute && longpath[1] == ':' ) { - tmpshortpath[0] = longpath[0]; - tmpshortpath[1] = ':'; - sp = 2; - } - - if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0; - flags = DRIVE_GetFlags ( drive ); - - if (unixabsolute && drive != DRIVE_GetCurrentDrive()) { - tmpshortpath[0] = drive + 'A'; - tmpshortpath[1] = ':'; - sp = 2; - } - - while ( longpath[lp] ) { - - /* check for path delimiters and reproduce them */ - if ( longpath[lp] == '\\' || longpath[lp] == '/' ) { - if (!sp || tmpshortpath[sp-1]!= '\\') - { - /* strip double "\\" */ - tmpshortpath[sp] = '\\'; - sp++; - } - tmpshortpath[sp]=0;/*terminate string*/ - lp++; - continue; - } - - tmplen = 0; - for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++) - tmplen++; - lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1); - - /* Check, if the current element is a valid dos name */ - if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) { - sp += tmplen; - lp += tmplen; - continue; - } - - /* Check if the file exists and use the existing file name */ - if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) { - strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1); - sp += strlenW(tmpshortpath + sp); - lp += tmplen; - continue; - } - - TRACE("not found!\n" ); - SetLastError ( ERROR_FILE_NOT_FOUND ); - return 0; - } - tmpshortpath[sp] = 0; - - tmplen = strlenW(tmpshortpath) + 1; - if (tmplen <= shortlen) - { - strcpyW(shortpath, tmpshortpath); - TRACE("returning %s\n", debugstr_w(shortpath)); - tmplen--; /* length without 0 */ - } - - return tmplen; -} - - -/*********************************************************************** - * GetShortPathNameA (KERNEL32.@) - */ -DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen ) -{ - UNICODE_STRING longpathW; - WCHAR shortpathW[MAX_PATH]; - DWORD ret, retW; - - if (!longpath) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - TRACE("%s\n", debugstr_a(longpath)); - - if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath)) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH); - - if (!retW) - ret = 0; - else if (retW > MAX_PATH) - { - SetLastError(ERROR_FILENAME_EXCED_RANGE); - ret = 0; - } - else - { - ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL); - if (ret <= shortlen) - { - WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL); - ret--; /* length without 0 */ - } - } - - RtlFreeUnicodeString(&longpathW); - return ret; -} - - -/*********************************************************************** - * GetLongPathNameW (KERNEL32.@) - * - * NOTES - * observed (Win2000): - * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0 - * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0 - */ -DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen ) -{ - DOS_FULL_NAME full_name; - const char *root; - LPWSTR p; - int drive; - DWORD ret, len = 0; - - if (!shortpath) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - if (!shortpath[0]) { - SetLastError(ERROR_PATH_NOT_FOUND); - return 0; - } - - TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen); - - if(shortpath[0]=='\\' && shortpath[1]=='\\') - { - ERR("UNC pathname %s\n",debugstr_w(shortpath)); - lstrcpynW( longpath, full_name.short_name, longlen ); - return strlenW(longpath); - } - - if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0; - - root = full_name.long_name; - drive = DRIVE_FindDriveRoot(&root); - - ret = MultiByteToWideChar(CP_UNIXCP, 0, root, -1, NULL, 0); - ret += 3; /* A:\ */ - /* reproduce terminating slash */ - if (ret > 4) /* if not drive root */ - { - len = strlenW(shortpath); - if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/') - len = 1; - } - ret += len; - if (ret <= longlen) - { - longpath[0] = 'A' + drive; - longpath[1] = ':'; - MultiByteToWideChar(CP_UNIXCP, 0, root, -1, longpath + 2, longlen - 2); - for (p = longpath; *p; p++) if (*p == '/') *p = '\\'; - if (len) - { - longpath[ret - 2] = '\\'; - longpath[ret - 1] = 0; - } - TRACE("returning %s\n", debugstr_w(longpath)); - ret--; /* length without 0 */ - } - return ret; -} - - -/*********************************************************************** - * GetLongPathNameA (KERNEL32.@) - */ -DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen ) -{ - UNICODE_STRING shortpathW; - WCHAR longpathW[MAX_PATH]; - DWORD ret, retW; - - if (!shortpath) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - TRACE("%s\n", debugstr_a(shortpath)); - - if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath)) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH); - - if (!retW) - ret = 0; - else if (retW > MAX_PATH) - { - SetLastError(ERROR_FILENAME_EXCED_RANGE); - ret = 0; - } - else - { - ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL); - if (ret <= longlen) - { - WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL); - ret--; /* length without 0 */ - } - } - - RtlFreeUnicodeString(&shortpathW); - return ret; -} - - -/*********************************************************************** - * DOSFS_DoGetFullPathName - * - * Implementation of GetFullPathNameA/W. - * - * bon@elektron 000331: - * A test for GetFullPathName with many pathological cases - * now gives identical output for Wine and OSR2 - */ -static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result ) -{ - DWORD ret; - DOS_FULL_NAME full_name; - LPWSTR p, q; - char *p_l; - const char * root; - WCHAR drivecur[] = {'C',':','.',0}; - WCHAR driveletter=0; - int namelen,drive=0; - static const WCHAR bkslashW[] = {'\\',0}; - static const WCHAR dotW[] = {'.',0}; - static const WCHAR updir_slashW[] = {'\\','.','.','\\',0}; - static const WCHAR curdirW[] = {'\\','.','\\',0}; - static const WCHAR updirW[] = {'\\','.','.',0}; - - if (!name[0]) - { - SetLastError(ERROR_BAD_PATHNAME); - return 0; - } - - TRACE("passed %s\n", debugstr_w(name)); - - if (name[1]==':') - /*drive letter given */ - { - driveletter = name[0]; - } - if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/'))) - /*absolute path given */ - { - strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN); - full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */ - drive = toupperW(name[0]) - 'A'; - } - else - { - if (driveletter) - drivecur[0]=driveletter; - else if ((name[0]=='\\') || (name[0]=='/')) - strcpyW(drivecur, bkslashW); - else - strcpyW(drivecur, dotW); - - if (!DOSFS_GetFullName( drivecur, FALSE, &full_name )) - { - FIXME("internal: error getting drive/path\n"); - return 0; - } - /* find path that drive letter substitutes*/ - drive = toupperW(full_name.short_name[0]) - 'A'; - root= DRIVE_GetRoot(drive); - if (!root) - { - FIXME("internal: error getting DOS Drive Root\n"); - return 0; - } - if (!strcmp(root,"/")) - { - /* we have just the last / and we need it. */ - p_l = full_name.long_name; - } - else - { - p_l = full_name.long_name + strlen(root); - } - /* append long name (= unix name) to drive */ - MultiByteToWideChar(CP_UNIXCP, 0, p_l, -1, full_name.short_name + 2, MAX_PATHNAME_LEN - 3); - /* append name to treat */ - namelen= strlenW(full_name.short_name); - p = (LPWSTR)name; - if (driveletter) - p += 2; /* skip drive name when appending */ - if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN) - { - FIXME("internal error: buffer too small\n"); - return 0; - } - full_name.short_name[namelen++] ='\\'; - full_name.short_name[namelen] = 0; - strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen); - full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */ - } - /* reverse all slashes */ - for (p=full_name.short_name; - p < full_name.short_name + strlenW(full_name.short_name); - p++) - { - if ( *p == '/' ) - *p = '\\'; - } - /* Use memmove, as areas overlap */ - /* Delete .. */ - while ((p = strstrW(full_name.short_name, updir_slashW))) - { - if (p > full_name.short_name+2) - { - *p = 0; - q = strrchrW(full_name.short_name, '\\'); - memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR)); - } - else - { - memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR)); - } - } - if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.')) - { - /* This case istn't treated yet : c:..\test */ - memmove(full_name.short_name+2,full_name.short_name+4, - (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR)); - } - /* Delete . */ - while ((p = strstrW(full_name.short_name, curdirW))) - { - *(p+1) = 0; - memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR)); - } - if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING)) - for (p = full_name.short_name; *p; p++) *p = toupperW(*p); - namelen = strlenW(full_name.short_name); - if (!strcmpW(full_name.short_name+namelen-3, updirW)) - { - /* one more strange case: "c:\test\test1\.." - return "c:\test" */ - *(full_name.short_name+namelen-3)=0; - q = strrchrW(full_name.short_name, '\\'); - *q =0; - } - if (full_name.short_name[namelen-1]=='.') - full_name.short_name[(namelen--)-1] =0; - if (!driveletter) - if (full_name.short_name[namelen-1]=='\\') - full_name.short_name[(namelen--)-1] =0; - TRACE("got %s\n", debugstr_w(full_name.short_name)); - - /* If the lpBuffer buffer is too small, the return value is the - size of the buffer, in characters, required to hold the path - plus the terminating \0 (tested against win95osr2, bon 001118) - . */ - ret = strlenW(full_name.short_name); - if (ret >= len ) - { - /* don't touch anything when the buffer is not large enough */ - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return ret+1; - } - if (result) - { - strncpyW( result, full_name.short_name, len ); - result[len - 1] = 0; /* ensure 0 termination */ - } - - TRACE("returning %s\n", debugstr_w(full_name.short_name) ); - return ret; -} - - -/*********************************************************************** - * GetFullPathNameA (KERNEL32.@) - * NOTES - * if the path closed with '\', *lastpart is 0 - */ -DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer, - LPSTR *lastpart ) -{ - UNICODE_STRING nameW; - WCHAR bufferW[MAX_PATH]; - DWORD ret, retW; - - if (!name) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name)) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL); - - if (!retW) - ret = 0; - else if (retW > MAX_PATH) - { - SetLastError(ERROR_FILENAME_EXCED_RANGE); - ret = 0; - } - else - { - ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); - if (ret <= len) - { - WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL); - ret--; /* length without 0 */ - - if (lastpart) - { - LPSTR p = buffer + strlen(buffer); - - if (*p != '\\') - { - while ((p > buffer + 2) && (*p != '\\')) p--; - *lastpart = p + 1; - } - else *lastpart = NULL; - } - } - } - - RtlFreeUnicodeString(&nameW); - return ret; -} - - -/*********************************************************************** - * GetFullPathNameW (KERNEL32.@) - */ -DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer, - LPWSTR *lastpart ) -{ - DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer ); - if (ret && (ret<=len) && buffer && lastpart) - { - LPWSTR p = buffer + strlenW(buffer); - if (*p != (WCHAR)'\\') - { - while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--; - *lastpart = p + 1; - } - else *lastpart = NULL; - } - return ret; -} - - -/*********************************************************************** * wine_get_unix_file_name (KERNEL32.@) Not a Windows API * * Return the full Unix file name for a given path.