|  | /* | 
|  | * Path Functions | 
|  | * | 
|  | * Copyright 1999, 2000 Juergen Schmied | 
|  | * Copyright 2001, 2002 Jon Griffiths | 
|  | * | 
|  | * 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> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "wine/unicode.h" | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  | #include "winuser.h" | 
|  | #include "winreg.h" | 
|  | #include "winternl.h" | 
|  | #define NO_SHLWAPI_STREAM | 
|  | #include "shlwapi.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(shell); | 
|  |  | 
|  | /* Get a function pointer from a DLL handle */ | 
|  | #define GET_FUNC(func, module, name, fail) \ | 
|  | do { \ | 
|  | if (!func) { \ | 
|  | if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \ | 
|  | func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \ | 
|  | if (!func) return fail; \ | 
|  | } \ | 
|  | } while (0) | 
|  |  | 
|  | /* DLL handles for late bound calls */ | 
|  | extern HMODULE SHLWAPI_hshell32; | 
|  |  | 
|  | /* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */ | 
|  | typedef BOOL (WINAPI *fnpIsNetDrive)(int); | 
|  | static  fnpIsNetDrive pIsNetDrive; | 
|  |  | 
|  | HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD); | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathAppendA    [SHLWAPI.@] | 
|  | * | 
|  | * Append one path to another. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath   [I/O] Initial part of path, and destination for output | 
|  | *  lpszAppend [I]   Path to append | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE. lpszPath contains the newly created path. | 
|  | *  Failure: FALSE, if either path is NULL, or PathCombineA() fails. | 
|  | * | 
|  | * NOTES | 
|  | *  lpszAppend must contain at least one backslash ('\') if not NULL. | 
|  | *  Because PathCombineA() is used to join the paths, the resulting | 
|  | *  path is also canonicalized. | 
|  | */ | 
|  | BOOL WINAPI PathAppendA (LPSTR lpszPath, LPCSTR lpszAppend) | 
|  | { | 
|  | TRACE("(%s,%s)\n",debugstr_a(lpszPath), debugstr_a(lpszAppend)); | 
|  |  | 
|  | if (lpszPath && lpszAppend) | 
|  | { | 
|  | if (!PathIsUNCA(lpszAppend)) | 
|  | while (*lpszAppend == '\\') | 
|  | lpszAppend++; | 
|  | if (PathCombineA(lpszPath, lpszPath, lpszAppend)) | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathAppendW    [SHLWAPI.@] | 
|  | * | 
|  | * See PathAppendA. | 
|  | */ | 
|  | BOOL WINAPI PathAppendW(LPWSTR lpszPath, LPCWSTR lpszAppend) | 
|  | { | 
|  | TRACE("(%s,%s)\n",debugstr_w(lpszPath), debugstr_w(lpszAppend)); | 
|  |  | 
|  | if (lpszPath && lpszAppend) | 
|  | { | 
|  | if (!PathIsUNCW(lpszAppend)) | 
|  | while (*lpszAppend == '\\') | 
|  | lpszAppend++; | 
|  | if (PathCombineW(lpszPath, lpszPath, lpszAppend)) | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCombineA		[SHLWAPI.@] | 
|  | * | 
|  | * Combine two paths together. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszDest [O] Destination for combined path | 
|  | *  lpszDir  [I] Directory path | 
|  | *  lpszFile [I] File path | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: The output path | 
|  | *  Failure: NULL, if inputs are invalid. | 
|  | * | 
|  | * NOTES | 
|  | *  lpszDest should be at least MAX_PATH in size, and may point to the same | 
|  | *  memory location as lpszDir. The combined path is canonicalised. | 
|  | */ | 
|  | LPSTR WINAPI PathCombineA(LPSTR lpszDest, LPCSTR lpszDir, LPCSTR lpszFile) | 
|  | { | 
|  | TRACE("(%p,%s,%s)\n", lpszDest, debugstr_a(lpszDir), debugstr_a(lpszFile)); | 
|  |  | 
|  | if (!lpszDest || (!lpszDir && !lpszFile)) | 
|  | return NULL; /* Invalid parameters */ | 
|  | else | 
|  | { | 
|  | WCHAR szDest[MAX_PATH]; | 
|  | WCHAR szDir[MAX_PATH]; | 
|  | WCHAR szFile[MAX_PATH]; | 
|  | if (lpszDir) | 
|  | MultiByteToWideChar(CP_ACP,0,lpszDir,-1,szDir,MAX_PATH); | 
|  | if (lpszFile) | 
|  | MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH); | 
|  | PathCombineW(szDest, lpszDir ? szDir : NULL, lpszFile ? szFile : NULL); | 
|  | WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0); | 
|  | } | 
|  | return lpszDest; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCombineW		 [SHLWAPI.@] | 
|  | * | 
|  | * See PathCombineA. | 
|  | */ | 
|  | LPWSTR WINAPI PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile) | 
|  | { | 
|  | WCHAR szTemp[MAX_PATH]; | 
|  | BOOL bUseBoth = FALSE, bStrip = FALSE; | 
|  |  | 
|  | TRACE("(%p,%s,%s)\n", lpszDest, debugstr_w(lpszDir), debugstr_w(lpszFile)); | 
|  |  | 
|  | if (!lpszDest || (!lpszDir && !lpszFile)) | 
|  | return lpszDest; /* Invalid parameters */ | 
|  |  | 
|  | if (!lpszFile || !*lpszFile) | 
|  | { | 
|  | /* Use dir only */ | 
|  | strncpyW(szTemp, lpszDir, MAX_PATH); | 
|  | } | 
|  | else if (!lpszDir || !*lpszDir || !PathIsRelativeW(lpszFile)) | 
|  | { | 
|  | if (!lpszDir || !*lpszDir || *lpszFile != '\\' || PathIsUNCW(lpszFile)) | 
|  | { | 
|  | /* Use file only */ | 
|  | strncpyW(szTemp, lpszFile, MAX_PATH); | 
|  | } | 
|  | else | 
|  | { | 
|  | bUseBoth = TRUE; | 
|  | bStrip = TRUE; | 
|  | } | 
|  | } | 
|  | else | 
|  | bUseBoth = TRUE; | 
|  |  | 
|  | if (bUseBoth) | 
|  | { | 
|  | strncpyW(szTemp, lpszDir, MAX_PATH); | 
|  | if (bStrip) | 
|  | { | 
|  | PathStripToRootW(szTemp); | 
|  | lpszFile++; /* Skip '\' */ | 
|  | } | 
|  | if (!PathAddBackslashW(szTemp)) | 
|  | return NULL; | 
|  | if (strlenW(szTemp) + strlenW(lpszFile) >= MAX_PATH) | 
|  | return NULL; | 
|  | strcatW(szTemp, lpszFile); | 
|  | } | 
|  |  | 
|  | PathCanonicalizeW(lpszDest, szTemp); | 
|  | return lpszDest; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathAddBackslashA	[SHLWAPI.@] | 
|  | * | 
|  | * Append a backslash ('\') to a path if one doesn't exist. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] The path to append a backslash to. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: The position of the last backslash in the path. | 
|  | *  Failure: NULL, if lpszPath is NULL or the path is too large. | 
|  | */ | 
|  | LPSTR WINAPI PathAddBackslashA(LPSTR lpszPath) | 
|  | { | 
|  | size_t iLen; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || (iLen = strlen(lpszPath)) >= MAX_PATH) | 
|  | return NULL; | 
|  |  | 
|  | if (iLen) | 
|  | { | 
|  | lpszPath += iLen; | 
|  | if (lpszPath[-1] != '\\') | 
|  | { | 
|  | *lpszPath++ = '\\'; | 
|  | *lpszPath = '\0'; | 
|  | } | 
|  | } | 
|  | return lpszPath; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathAddBackslashW  [SHLWAPI.@] | 
|  | * | 
|  | * See PathAddBackslashA. | 
|  | */ | 
|  | LPWSTR WINAPI PathAddBackslashW( LPWSTR lpszPath ) | 
|  | { | 
|  | size_t iLen; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || (iLen = strlenW(lpszPath)) >= MAX_PATH) | 
|  | return NULL; | 
|  |  | 
|  | if (iLen) | 
|  | { | 
|  | lpszPath += iLen; | 
|  | if (lpszPath[-1] != '\\') | 
|  | { | 
|  | *lpszPath++ = '\\'; | 
|  | *lpszPath = '\0'; | 
|  | } | 
|  | } | 
|  | return lpszPath; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathBuildRootA    [SHLWAPI.@] | 
|  | * | 
|  | * Create a root drive string (e.g. "A:\") from a drive number. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [O] Destination for the drive string | 
|  | * | 
|  | * RETURNS | 
|  | *  lpszPath | 
|  | * | 
|  | * NOTES | 
|  | *  If lpszPath is NULL or drive is invalid, nothing is written to lpszPath. | 
|  | */ | 
|  | LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive) | 
|  | { | 
|  | TRACE("(%p,%d)\n", lpszPath, drive); | 
|  |  | 
|  | if (lpszPath && drive >= 0 && drive < 26) | 
|  | { | 
|  | lpszPath[0] = 'A' + drive; | 
|  | lpszPath[1] = ':'; | 
|  | lpszPath[2] = '\\'; | 
|  | lpszPath[3] = '\0'; | 
|  | } | 
|  | return lpszPath; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathBuildRootW    [SHLWAPI.@] | 
|  | * | 
|  | * See PathBuildRootA. | 
|  | */ | 
|  | LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive) | 
|  | { | 
|  | TRACE("(%p,%d)\n", lpszPath, drive); | 
|  |  | 
|  | if (lpszPath && drive >= 0 && drive < 26) | 
|  | { | 
|  | lpszPath[0] = 'A' + drive; | 
|  | lpszPath[1] = ':'; | 
|  | lpszPath[2] = '\\'; | 
|  | lpszPath[3] = '\0'; | 
|  | } | 
|  | return lpszPath; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindFileNameA  [SHLWAPI.@] | 
|  | * | 
|  | * Locate the start of the file name in a path | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to search | 
|  | * | 
|  | * RETURNS | 
|  | *  A pointer to the first character of the file name | 
|  | */ | 
|  | LPSTR WINAPI PathFindFileNameA(LPCSTR lpszPath) | 
|  | { | 
|  | LPCSTR lastSlash = lpszPath; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | while (lpszPath && *lpszPath) | 
|  | { | 
|  | if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && | 
|  | lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') | 
|  | lastSlash = lpszPath + 1; | 
|  | lpszPath = CharNextA(lpszPath); | 
|  | } | 
|  | return (LPSTR)lastSlash; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindFileNameW  [SHLWAPI.@] | 
|  | * | 
|  | * See PathFindFileNameA. | 
|  | */ | 
|  | LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath) | 
|  | { | 
|  | LPCWSTR lastSlash = lpszPath; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | while (lpszPath && *lpszPath) | 
|  | { | 
|  | if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && | 
|  | lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') | 
|  | lastSlash = lpszPath + 1; | 
|  | lpszPath = CharNextW(lpszPath); | 
|  | } | 
|  | return (LPWSTR)lastSlash; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindExtensionA  [SHLWAPI.@] | 
|  | * | 
|  | * Locate the start of the file extension in a path | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] The path to search | 
|  | * | 
|  | * RETURNS | 
|  | *  A pointer to the first character of the extension, the end of | 
|  | *  the string if the path has no extension, or NULL If lpszPath is NULL | 
|  | */ | 
|  | LPSTR WINAPI PathFindExtensionA( LPCSTR lpszPath ) | 
|  | { | 
|  | LPCSTR lastpoint = NULL; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\' || *lpszPath==' ') | 
|  | lastpoint = NULL; | 
|  | else if (*lpszPath == '.') | 
|  | lastpoint = lpszPath; | 
|  | lpszPath = CharNextA(lpszPath); | 
|  | } | 
|  | } | 
|  | return (LPSTR)(lastpoint ? lastpoint : lpszPath); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindExtensionW  [SHLWAPI.@] | 
|  | * | 
|  | * See PathFindExtensionA. | 
|  | */ | 
|  | LPWSTR WINAPI PathFindExtensionW( LPCWSTR lpszPath ) | 
|  | { | 
|  | LPCWSTR lastpoint = NULL; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\' || *lpszPath==' ') | 
|  | lastpoint = NULL; | 
|  | else if (*lpszPath == '.') | 
|  | lastpoint = lpszPath; | 
|  | lpszPath = CharNextW(lpszPath); | 
|  | } | 
|  | } | 
|  | return (LPWSTR)(lastpoint ? lastpoint : lpszPath); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathGetArgsA    [SHLWAPI.@] | 
|  | * | 
|  | * Find the next argument in a string delimited by spaces. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] The string to search for arguments in | 
|  | * | 
|  | * RETURNS | 
|  | *  The start of the next argument in lpszPath, or NULL if lpszPath is NULL | 
|  | * | 
|  | * NOTES | 
|  | *  Spaces in quoted strings are ignored as delimiters. | 
|  | */ | 
|  | LPSTR WINAPI PathGetArgsA(LPCSTR lpszPath) | 
|  | { | 
|  | BOOL bSeenQuote = FALSE; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | while (*lpszPath) | 
|  | { | 
|  | if ((*lpszPath==' ') && !bSeenQuote) | 
|  | return (LPSTR)lpszPath + 1; | 
|  | if (*lpszPath == '"') | 
|  | bSeenQuote = !bSeenQuote; | 
|  | lpszPath = CharNextA(lpszPath); | 
|  | } | 
|  | } | 
|  | return (LPSTR)lpszPath; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathGetArgsW    [SHLWAPI.@] | 
|  | * | 
|  | * See PathGetArgsA. | 
|  | */ | 
|  | LPWSTR WINAPI PathGetArgsW(LPCWSTR lpszPath) | 
|  | { | 
|  | BOOL bSeenQuote = FALSE; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | while (*lpszPath) | 
|  | { | 
|  | if ((*lpszPath==' ') && !bSeenQuote) | 
|  | return (LPWSTR)lpszPath + 1; | 
|  | if (*lpszPath == '"') | 
|  | bSeenQuote = !bSeenQuote; | 
|  | lpszPath = CharNextW(lpszPath); | 
|  | } | 
|  | } | 
|  | return (LPWSTR)lpszPath; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathGetDriveNumberA	[SHLWAPI.@] | 
|  | * | 
|  | * Return the drive number from a path | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to get the drive number from | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: The drive number corresponding to the drive in the path | 
|  | *  Failure: -1, if lpszPath contains no valid drive | 
|  | */ | 
|  | int WINAPI PathGetDriveNumberA(LPCSTR lpszPath) | 
|  | { | 
|  | TRACE ("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath && !IsDBCSLeadByte(*lpszPath) && lpszPath[1] == ':' && | 
|  | tolower(*lpszPath) >= 'a' && tolower(*lpszPath) <= 'z') | 
|  | return tolower(*lpszPath) - 'a'; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathGetDriveNumberW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathGetDriveNumberA. | 
|  | */ | 
|  | int WINAPI PathGetDriveNumberW(LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE ("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath && lpszPath[1] == ':' && | 
|  | tolowerW(*lpszPath) >= 'a' && tolowerW(*lpszPath) <= 'z') | 
|  | return tolowerW(*lpszPath) - 'a'; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveFileSpecA	[SHLWAPI.@] | 
|  | * | 
|  | * Remove the file specification from a path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to remove the file spec from | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the path was valid and modified | 
|  | *  FALSE Otherwise | 
|  | */ | 
|  | BOOL WINAPI PathRemoveFileSpecA(LPSTR lpszPath) | 
|  | { | 
|  | LPSTR lpszFileSpec = lpszPath; | 
|  | BOOL bModified = FALSE; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if(lpszPath) | 
|  | { | 
|  | /* Skip directory or UNC path */ | 
|  | if (*lpszPath == '\\') | 
|  | lpszFileSpec = ++lpszPath; | 
|  | if (*lpszPath == '\\') | 
|  | lpszFileSpec = ++lpszPath; | 
|  |  | 
|  | while (*lpszPath) | 
|  | { | 
|  | if(*lpszPath == '\\') | 
|  | lpszFileSpec = lpszPath; /* Skip dir */ | 
|  | else if(*lpszPath == ':') | 
|  | { | 
|  | lpszFileSpec = ++lpszPath; /* Skip drive */ | 
|  | if (*lpszPath == '\\') | 
|  | lpszFileSpec++; | 
|  | } | 
|  | if (!(lpszPath = CharNextA(lpszPath))) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (*lpszFileSpec) | 
|  | { | 
|  | *lpszFileSpec = '\0'; | 
|  | bModified = TRUE; | 
|  | } | 
|  | } | 
|  | return bModified; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveFileSpecW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathRemoveFileSpecA. | 
|  | */ | 
|  | BOOL WINAPI PathRemoveFileSpecW(LPWSTR lpszPath) | 
|  | { | 
|  | LPWSTR lpszFileSpec = lpszPath; | 
|  | BOOL bModified = FALSE; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if(lpszPath) | 
|  | { | 
|  | /* Skip directory or UNC path */ | 
|  | if (*lpszPath == '\\') | 
|  | lpszFileSpec = ++lpszPath; | 
|  | if (*lpszPath == '\\') | 
|  | lpszFileSpec = ++lpszPath; | 
|  |  | 
|  | while (*lpszPath) | 
|  | { | 
|  | if(*lpszPath == '\\') | 
|  | lpszFileSpec = lpszPath; /* Skip dir */ | 
|  | else if(*lpszPath == ':') | 
|  | { | 
|  | lpszFileSpec = ++lpszPath; /* Skip drive */ | 
|  | if (*lpszPath == '\\') | 
|  | lpszFileSpec++; | 
|  | } | 
|  | if (!(lpszPath = CharNextW(lpszPath))) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (*lpszFileSpec) | 
|  | { | 
|  | *lpszFileSpec = '\0'; | 
|  | bModified = TRUE; | 
|  | } | 
|  | } | 
|  | return bModified; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathStripPathA	[SHLWAPI.@] | 
|  | * | 
|  | * Remove the initial path from the beginning of a filename | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to remove the initial path from | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing. | 
|  | */ | 
|  | void WINAPI PathStripPathA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | LPSTR lpszFileName = PathFindFileNameA(lpszPath); | 
|  | if(lpszFileName) | 
|  | RtlMoveMemory(lpszPath, lpszFileName, strlen(lpszFileName)+1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathStripPathW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathStripPathA. | 
|  | */ | 
|  | void WINAPI PathStripPathW(LPWSTR lpszPath) | 
|  | { | 
|  | LPWSTR lpszFileName; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  | lpszFileName = PathFindFileNameW(lpszPath); | 
|  | if(lpszFileName) | 
|  | RtlMoveMemory(lpszPath, lpszFileName, (strlenW(lpszFileName)+1)*sizeof(WCHAR)); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathStripToRootA	[SHLWAPI.@] | 
|  | * | 
|  | * Reduce a path to its root. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] the path to reduce | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE if the stripped path is a root path | 
|  | *  Failure: FALSE if the path cannot be stripped or is NULL | 
|  | */ | 
|  | BOOL WINAPI PathStripToRootA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  | while(!PathIsRootA(lpszPath)) | 
|  | if (!PathRemoveFileSpecA(lpszPath)) | 
|  | return FALSE; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathStripToRootW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathStripToRootA. | 
|  | */ | 
|  | BOOL WINAPI PathStripToRootW(LPWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  | while(!PathIsRootW(lpszPath)) | 
|  | if (!PathRemoveFileSpecW(lpszPath)) | 
|  | return FALSE; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveArgsA	[SHLWAPI.@] | 
|  | * | 
|  | * Strip space separated arguments from a path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to remove arguments from | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing. | 
|  | */ | 
|  | void WINAPI PathRemoveArgsA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if(lpszPath) | 
|  | { | 
|  | LPSTR lpszArgs = PathGetArgsA(lpszPath); | 
|  | if (*lpszArgs) | 
|  | lpszArgs[-1] = '\0'; | 
|  | else | 
|  | { | 
|  | LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs); | 
|  | if(*lpszLastChar == ' ') | 
|  | *lpszLastChar = '\0'; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveArgsW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathRemoveArgsA. | 
|  | */ | 
|  | void WINAPI PathRemoveArgsW(LPWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if(lpszPath) | 
|  | { | 
|  | LPWSTR lpszArgs = PathGetArgsW(lpszPath); | 
|  | if (*lpszArgs) | 
|  | lpszArgs[-1] = '\0'; | 
|  | else | 
|  | { | 
|  | LPWSTR lpszLastChar = CharPrevW(lpszPath, lpszArgs); | 
|  | if(*lpszLastChar == ' ') | 
|  | *lpszLastChar = '\0'; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveExtensionA		[SHLWAPI.@] | 
|  | * | 
|  | * Remove the file extension from a path | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to remove the extension from | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing. | 
|  | */ | 
|  | void WINAPI PathRemoveExtensionA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | lpszPath = PathFindExtensionA(lpszPath); | 
|  | *lpszPath = '\0'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveExtensionW		[SHLWAPI.@] | 
|  | * | 
|  | * See PathRemoveExtensionA. | 
|  | */ | 
|  | void WINAPI PathRemoveExtensionW(LPWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | lpszPath = PathFindExtensionW(lpszPath); | 
|  | *lpszPath = '\0'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveBackslashA	[SHLWAPI.@] | 
|  | * | 
|  | * Remove a trailing backslash from a path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to remove backslash from | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to the end of the path | 
|  | *  Failure: NULL, if lpszPath is NULL | 
|  | */ | 
|  | LPSTR WINAPI PathRemoveBackslashA( LPSTR lpszPath ) | 
|  | { | 
|  | LPSTR szTemp = NULL; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if(lpszPath) | 
|  | { | 
|  | szTemp = CharPrevA(lpszPath, lpszPath + strlen(lpszPath)); | 
|  | if (!PathIsRootA(lpszPath) && *szTemp == '\\') | 
|  | *szTemp = '\0'; | 
|  | } | 
|  | return szTemp; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveBackslashW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathRemoveBackslashA. | 
|  | */ | 
|  | LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpszPath ) | 
|  | { | 
|  | LPWSTR szTemp = NULL; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if(lpszPath) | 
|  | { | 
|  | szTemp = CharPrevW(lpszPath, lpszPath + strlenW(lpszPath)); | 
|  | if (!PathIsRootW(lpszPath) && *szTemp == '\\') | 
|  | *szTemp = '\0'; | 
|  | } | 
|  | return szTemp; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveBlanksA [SHLWAPI.@] | 
|  | * | 
|  | * Remove Spaces from the start and end of a path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to strip blanks from | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing. | 
|  | */ | 
|  | VOID WINAPI PathRemoveBlanksA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if(lpszPath && *lpszPath) | 
|  | { | 
|  | LPSTR start = lpszPath; | 
|  |  | 
|  | while (*lpszPath == ' ') | 
|  | lpszPath = CharNextA(lpszPath); | 
|  |  | 
|  | while(*lpszPath) | 
|  | *start++ = *lpszPath++; | 
|  |  | 
|  | if (start != lpszPath) | 
|  | while (start[-1] == ' ') | 
|  | start--; | 
|  | *start = '\0'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRemoveBlanksW [SHLWAPI.@] | 
|  | * | 
|  | * See PathRemoveBlanksA. | 
|  | */ | 
|  | VOID WINAPI PathRemoveBlanksW(LPWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if(lpszPath && *lpszPath) | 
|  | { | 
|  | LPWSTR start = lpszPath; | 
|  |  | 
|  | while (*lpszPath == ' ') | 
|  | lpszPath++; | 
|  |  | 
|  | while(*lpszPath) | 
|  | *start++ = *lpszPath++; | 
|  |  | 
|  | if (start != lpszPath) | 
|  | while (start[-1] == ' ') | 
|  | start--; | 
|  | *start = '\0'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathQuoteSpacesA [SHLWAPI.@] | 
|  | * | 
|  | * Surround a path containg spaces in quotes. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to quote | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing. | 
|  | * | 
|  | * NOTES | 
|  | *  The path is not changed if it is invalid or has no spaces. | 
|  | */ | 
|  | VOID WINAPI PathQuoteSpacesA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if(lpszPath && StrChrA(lpszPath,' ')) | 
|  | { | 
|  | size_t iLen = strlen(lpszPath) + 1; | 
|  |  | 
|  | if (iLen + 2 < MAX_PATH) | 
|  | { | 
|  | memmove(lpszPath + 1, lpszPath, iLen); | 
|  | lpszPath[0] = '"'; | 
|  | lpszPath[iLen] = '"'; | 
|  | lpszPath[iLen + 1] = '\0'; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathQuoteSpacesW [SHLWAPI.@] | 
|  | * | 
|  | * See PathQuoteSpacesA. | 
|  | */ | 
|  | VOID WINAPI PathQuoteSpacesW(LPWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if(lpszPath && StrChrW(lpszPath,' ')) | 
|  | { | 
|  | int iLen = strlenW(lpszPath) + 1; | 
|  |  | 
|  | if (iLen + 2 < MAX_PATH) | 
|  | { | 
|  | memmove(lpszPath + 1, lpszPath, iLen * sizeof(WCHAR)); | 
|  | lpszPath[0] = '"'; | 
|  | lpszPath[iLen] = '"'; | 
|  | lpszPath[iLen + 1] = '\0'; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathUnquoteSpacesA [SHLWAPI.@] | 
|  | * | 
|  | * Remove quotes ("") from around a path, if present. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to strip quotes from | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing | 
|  | * | 
|  | * NOTES | 
|  | *  If the path contains a single quote only, an empty string will result. | 
|  | *  Otherwise quotes are only removed if they appear at the start and end | 
|  | *  of the path. | 
|  | */ | 
|  | VOID WINAPI PathUnquoteSpacesA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath == '"') | 
|  | { | 
|  | DWORD dwLen = strlen(lpszPath) - 1; | 
|  |  | 
|  | if (lpszPath[dwLen] == '"') | 
|  | { | 
|  | lpszPath[dwLen] = '\0'; | 
|  | for (; *lpszPath; lpszPath++) | 
|  | *lpszPath = lpszPath[1]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathUnquoteSpacesW [SHLWAPI.@] | 
|  | * | 
|  | * See PathUnquoteSpacesA. | 
|  | */ | 
|  | VOID WINAPI PathUnquoteSpacesW(LPWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath == '"') | 
|  | { | 
|  | DWORD dwLen = strlenW(lpszPath) - 1; | 
|  |  | 
|  | if (lpszPath[dwLen] == '"') | 
|  | { | 
|  | lpszPath[dwLen] = '\0'; | 
|  | for (; *lpszPath; lpszPath++) | 
|  | *lpszPath = lpszPath[1]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathParseIconLocationA  [SHLWAPI.@] | 
|  | * | 
|  | * Parse the location of an icon from a path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] The path to parse the icon location from. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: The number of the icon | 
|  | *  Failure: 0 if the path does not contain an icon location or is NULL | 
|  | * | 
|  | * NOTES | 
|  | *  The path has surrounding quotes and spaces removed regardless | 
|  | *  of whether the call succeeds or not. | 
|  | */ | 
|  | int WINAPI PathParseIconLocationA(LPSTR lpszPath) | 
|  | { | 
|  | int iRet = 0; | 
|  | LPSTR lpszComma; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | if ((lpszComma = strchr(lpszPath, ','))) | 
|  | { | 
|  | *lpszComma++ = '\0'; | 
|  | iRet = StrToIntA(lpszComma); | 
|  | } | 
|  | PathUnquoteSpacesA(lpszPath); | 
|  | PathRemoveBlanksA(lpszPath); | 
|  | } | 
|  | return iRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathParseIconLocationW  [SHLWAPI.@] | 
|  | * | 
|  | * See PathParseIconLocationA. | 
|  | */ | 
|  | int WINAPI PathParseIconLocationW(LPWSTR lpszPath) | 
|  | { | 
|  | int iRet = 0; | 
|  | LPWSTR lpszComma; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | if ((lpszComma = StrChrW(lpszPath, ','))) | 
|  | { | 
|  | *lpszComma++ = '\0'; | 
|  | iRet = StrToIntW(lpszComma); | 
|  | } | 
|  | PathUnquoteSpacesW(lpszPath); | 
|  | PathRemoveBlanksW(lpszPath); | 
|  | } | 
|  | return iRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * @	[SHLWAPI.4] | 
|  | * | 
|  | * Unicode version of PathFileExistsDefExtA. | 
|  | */ | 
|  | BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich) | 
|  | { | 
|  | static const WCHAR pszExts[7][5] = { { '.', 'p', 'i', 'f', 0}, | 
|  | { '.', 'c', 'o', 'm', 0}, | 
|  | { '.', 'e', 'x', 'e', 0}, | 
|  | { '.', 'b', 'a', 't', 0}, | 
|  | { '.', 'l', 'n', 'k', 0}, | 
|  | { '.', 'c', 'm', 'd', 0}, | 
|  | { 0, 0, 0, 0, 0} }; | 
|  |  | 
|  | TRACE("(%s,%ld)\n", debugstr_w(lpszPath), dwWhich); | 
|  |  | 
|  | if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath)) | 
|  | return FALSE; | 
|  |  | 
|  | if (dwWhich) | 
|  | { | 
|  | LPCWSTR szExt = PathFindExtensionW(lpszPath); | 
|  | if (!*szExt || dwWhich & 0x40) | 
|  | { | 
|  | size_t iChoose = 0; | 
|  | int iLen = lstrlenW(lpszPath); | 
|  | if (iLen > (MAX_PATH - 5)) | 
|  | return FALSE; | 
|  | while ( (dwWhich & 0x1) && pszExts[iChoose][0] ) | 
|  | { | 
|  | lstrcpyW(lpszPath + iLen, pszExts[iChoose]); | 
|  | if (PathFileExistsW(lpszPath)) | 
|  | return TRUE; | 
|  | iChoose++; | 
|  | dwWhich >>= 1; | 
|  | } | 
|  | *(lpszPath + iLen) = (WCHAR)'\0'; | 
|  | return FALSE; | 
|  | } | 
|  | } | 
|  | return PathFileExistsW(lpszPath); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * @	[SHLWAPI.3] | 
|  | * | 
|  | * Determine if a file exists locally and is of an executable type. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath       [I/O] File to search for | 
|  | *  dwWhich        [I]   Type of executable to search for | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the file was found. lpszPath contains the file name. | 
|  | *  FALSE Otherwise. | 
|  | * | 
|  | * NOTES | 
|  | *  lpszPath is modified in place and must be at least MAX_PATH in length. | 
|  | *  If the function returns FALSE, the path is modified to its orginal state. | 
|  | *  If the given path contains an extension or dwWhich is 0, executable | 
|  | *  extensions are not checked. | 
|  | * | 
|  | *  Ordinals 3-6 are a classic case of MS exposing limited functionality to | 
|  | *  users (here through PathFindOnPathA()) and keeping advanced functionality for | 
|  | *  their own developers exclusive use. Monopoly, anyone? | 
|  | */ | 
|  | BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich) | 
|  | { | 
|  | BOOL bRet = FALSE; | 
|  |  | 
|  | TRACE("(%s,%ld)\n", debugstr_a(lpszPath), dwWhich); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  | MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); | 
|  | bRet = PathFileExistsDefExtW(szPath, dwWhich); | 
|  | if (bRet) | 
|  | WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * SHLWAPI_PathFindInOtherDirs | 
|  | * | 
|  | * Internal helper for SHLWAPI_PathFindOnPathExA/W. | 
|  | */ | 
|  | static BOOL WINAPI SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich) | 
|  | { | 
|  | static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'}; | 
|  | static const WCHAR szPath[] = { 'P','A','T','H','\0'}; | 
|  | DWORD dwLenPATH; | 
|  | LPCWSTR lpszCurr; | 
|  | WCHAR *lpszPATH; | 
|  | WCHAR buff[MAX_PATH]; | 
|  |  | 
|  | TRACE("(%s,%08lx)\n", debugstr_w(lpszFile), dwWhich); | 
|  |  | 
|  | /* Try system directories */ | 
|  | GetSystemDirectoryW(buff, MAX_PATH); | 
|  | if (!PathAppendW(buff, lpszFile)) | 
|  | return FALSE; | 
|  | if (PathFileExistsDefExtW(buff, dwWhich)) | 
|  | { | 
|  | strcpyW(lpszFile, buff); | 
|  | return TRUE; | 
|  | } | 
|  | GetWindowsDirectoryW(buff, MAX_PATH); | 
|  | if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile)) | 
|  | return FALSE; | 
|  | if (PathFileExistsDefExtW(buff, dwWhich)) | 
|  | { | 
|  | strcpyW(lpszFile, buff); | 
|  | return TRUE; | 
|  | } | 
|  | GetWindowsDirectoryW(buff, MAX_PATH); | 
|  | if (!PathAppendW(buff, lpszFile)) | 
|  | return FALSE; | 
|  | if (PathFileExistsDefExtW(buff, dwWhich)) | 
|  | { | 
|  | strcpyW(lpszFile, buff); | 
|  | return TRUE; | 
|  | } | 
|  | /* Try dirs listed in %PATH% */ | 
|  | dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH); | 
|  |  | 
|  | if (!dwLenPATH || !(lpszPATH = malloc((dwLenPATH + 1) * sizeof (WCHAR)))) | 
|  | return FALSE; | 
|  |  | 
|  | GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1); | 
|  | lpszCurr = lpszPATH; | 
|  | while (lpszCurr) | 
|  | { | 
|  | LPCWSTR lpszEnd = lpszCurr; | 
|  | LPWSTR pBuff = buff; | 
|  |  | 
|  | while (*lpszEnd == ' ') | 
|  | lpszEnd++; | 
|  | while (*lpszEnd && *lpszEnd != ';') | 
|  | *pBuff++ = *lpszEnd++; | 
|  | *pBuff = '\0'; | 
|  |  | 
|  | if (*lpszEnd) | 
|  | lpszCurr = lpszEnd + 1; | 
|  | else | 
|  | lpszCurr = NULL; /* Last Path, terminate after this */ | 
|  |  | 
|  | if (!PathAppendW(buff, lpszFile)) | 
|  | { | 
|  | free(lpszPATH); | 
|  | return FALSE; | 
|  | } | 
|  | if (PathFileExistsDefExtW(buff, dwWhich)) | 
|  | { | 
|  | strcpyW(lpszFile, buff); | 
|  | free(lpszPATH); | 
|  | return TRUE; | 
|  | } | 
|  | } | 
|  | free(lpszPATH); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * @	[SHLWAPI.5] | 
|  | * | 
|  | * Search a range of paths for a specific type of executable. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszFile       [I/O] File to search for | 
|  | *  lppszOtherDirs [I]   Other directories to look in | 
|  | *  dwWhich        [I]   Type of executable to search for | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE. The path to the executable is stored in lpszFile. | 
|  | *  Failure: FALSE. The path to the executable is unchanged. | 
|  | */ | 
|  | BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich) | 
|  | { | 
|  | WCHAR szFile[MAX_PATH]; | 
|  | WCHAR buff[MAX_PATH]; | 
|  |  | 
|  | TRACE("(%s,%p,%08lx)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich); | 
|  |  | 
|  | if (!lpszFile || !PathIsFileSpecA(lpszFile)) | 
|  | return FALSE; | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH); | 
|  |  | 
|  | /* Search provided directories first */ | 
|  | if (lppszOtherDirs && *lppszOtherDirs) | 
|  | { | 
|  | WCHAR szOther[MAX_PATH]; | 
|  | LPCSTR *lpszOtherPath = lppszOtherDirs; | 
|  |  | 
|  | while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) | 
|  | { | 
|  | MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH); | 
|  | PathCombineW(buff, szOther, szFile); | 
|  | if (PathFileExistsDefExtW(buff, dwWhich)) | 
|  | { | 
|  | WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0); | 
|  | return TRUE; | 
|  | } | 
|  | lpszOtherPath++; | 
|  | } | 
|  | } | 
|  | /* Not found, try system and path dirs */ | 
|  | if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich)) | 
|  | { | 
|  | WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0); | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * @	[SHLWAPI.6] | 
|  | * | 
|  | * Unicode version of PathFindOnPathExA. | 
|  | */ | 
|  | BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich) | 
|  | { | 
|  | WCHAR buff[MAX_PATH]; | 
|  |  | 
|  | TRACE("(%s,%p,%08lx)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich); | 
|  |  | 
|  | if (!lpszFile || !PathIsFileSpecW(lpszFile)) | 
|  | return FALSE; | 
|  |  | 
|  | /* Search provided directories first */ | 
|  | if (lppszOtherDirs && *lppszOtherDirs) | 
|  | { | 
|  | LPCWSTR *lpszOtherPath = lppszOtherDirs; | 
|  | while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) | 
|  | { | 
|  | PathCombineW(buff, *lpszOtherPath, lpszFile); | 
|  | if (PathFileExistsDefExtW(buff, dwWhich)) | 
|  | { | 
|  | strcpyW(lpszFile, buff); | 
|  | return TRUE; | 
|  | } | 
|  | lpszOtherPath++; | 
|  | } | 
|  | } | 
|  | /* Not found, try system and path dirs */ | 
|  | return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindOnPathA	[SHLWAPI.@] | 
|  | * | 
|  | * Search a range of paths for an executable. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszFile       [I/O] File to search for | 
|  | *  lppszOtherDirs [I]   Other directories to look in | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE. The path to the executable is stored in lpszFile. | 
|  | *  Failure: FALSE. The path to the executable is unchanged. | 
|  | */ | 
|  | BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs) | 
|  | { | 
|  | TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs); | 
|  | return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindOnPathW      [SHLWAPI.@] | 
|  | * | 
|  | * See PathFindOnPathA. | 
|  | */ | 
|  | BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs) | 
|  | { | 
|  | TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs); | 
|  | return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCompactPathExA   [SHLWAPI.@] | 
|  | * | 
|  | * Compact a path into a given number of characters. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszDest [O] Destination for compacted path | 
|  | *  lpszPath [I] Source path | 
|  | *  cchMax   [I] Maximum size of compacted path | 
|  | *  dwFlags  [I] Reserved | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE. The compacted path is written to lpszDest. | 
|  | *  Failure: FALSE. lpszPath is undefined. | 
|  | * | 
|  | * NOTES | 
|  | *  If cchMax is given as 0, lpszDest will still be NUL terminated. | 
|  | * | 
|  | *  The Win32 version of this function contains a bug: When cchMax == 7, | 
|  | *  8 bytes will be written to lpszDest. This bug is fixed in the Wine | 
|  | *  implementation. | 
|  | * | 
|  | *  Some relative paths will be different when cchMax == 5 or 6. This occurs | 
|  | *  because Win32 will insert a "\" in lpszDest, even if one is | 
|  | *  not present in the original path. | 
|  | */ | 
|  | BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath, | 
|  | UINT cchMax, DWORD dwFlags) | 
|  | { | 
|  | BOOL bRet = FALSE; | 
|  |  | 
|  | TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags); | 
|  |  | 
|  | if (lpszPath && lpszDest) | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  | WCHAR szDest[MAX_PATH]; | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); | 
|  | szDest[0] = '\0'; | 
|  | bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags); | 
|  | WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCompactPathExW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathCompactPathExA. | 
|  | */ | 
|  | BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath, | 
|  | UINT cchMax, DWORD dwFlags) | 
|  | { | 
|  | static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; | 
|  | LPCWSTR lpszFile; | 
|  | DWORD dwLen, dwFileLen = 0; | 
|  |  | 
|  | TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | if (!lpszDest) | 
|  | { | 
|  | WARN("Invalid lpszDest would crash under Win32!\n"); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | *lpszDest = '\0'; | 
|  |  | 
|  | if (cchMax < 2) | 
|  | return TRUE; | 
|  |  | 
|  | dwLen = strlenW(lpszPath) + 1; | 
|  |  | 
|  | if (dwLen < cchMax) | 
|  | { | 
|  | /* Don't need to compact */ | 
|  | memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* Path must be compacted to fit into lpszDest */ | 
|  | lpszFile = PathFindFileNameW(lpszPath); | 
|  | dwFileLen = lpszPath + dwLen - lpszFile; | 
|  |  | 
|  | if (dwFileLen == dwLen) | 
|  | { | 
|  | /* No root in psth */ | 
|  | if (cchMax <= 4) | 
|  | { | 
|  | while (--cchMax > 0) /* No room left for anything but ellipses */ | 
|  | *lpszDest++ = '.'; | 
|  | *lpszDest = '\0'; | 
|  | return TRUE; | 
|  | } | 
|  | /* Compact the file name with ellipses at the end */ | 
|  | cchMax -= 4; | 
|  | memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); | 
|  | strcpyW(lpszDest + cchMax, szEllipses); | 
|  | return TRUE; | 
|  | } | 
|  | /* We have a root in the path */ | 
|  | lpszFile--; /* Start compacted filename with the path separator */ | 
|  | dwFileLen++; | 
|  |  | 
|  | if (dwFileLen + 3 > cchMax) | 
|  | { | 
|  | /* Compact the file name */ | 
|  | if (cchMax <= 4) | 
|  | { | 
|  | while (--cchMax > 0) /* No room left for anything but ellipses */ | 
|  | *lpszDest++ = '.'; | 
|  | *lpszDest = '\0'; | 
|  | return TRUE; | 
|  | } | 
|  | strcpyW(lpszDest, szEllipses); | 
|  | lpszDest += 3; | 
|  | cchMax -= 4; | 
|  | *lpszDest++ = *lpszFile++; | 
|  | if (cchMax <= 4) | 
|  | { | 
|  | while (--cchMax > 0) /* No room left for anything but ellipses */ | 
|  | *lpszDest++ = '.'; | 
|  | *lpszDest = '\0'; | 
|  | return TRUE; | 
|  | } | 
|  | cchMax -= 4; | 
|  | memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); | 
|  | strcpyW(lpszDest + cchMax, szEllipses); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* Only the root needs to be Compacted */ | 
|  | dwLen = cchMax - dwFileLen - 3; | 
|  | memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); | 
|  | strcpyW(lpszDest + dwLen, szEllipses); | 
|  | strcpyW(lpszDest + dwLen + 3, lpszFile); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsRelativeA	[SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is a relative path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE:  The path is relative, or is invalid. | 
|  | *  FALSE: The path is not relative. | 
|  | */ | 
|  | BOOL WINAPI PathIsRelativeA (LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !*lpszPath || IsDBCSLeadByte(*lpszPath)) | 
|  | return TRUE; | 
|  | if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) | 
|  | return FALSE; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *  PathIsRelativeW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathIsRelativeA. | 
|  | */ | 
|  | BOOL WINAPI PathIsRelativeW (LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !*lpszPath) | 
|  | return TRUE; | 
|  | if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) | 
|  | return FALSE; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsRootA		[SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is a root path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If lpszPath is valid and a root path, | 
|  | *  FALSE Otherwise | 
|  | */ | 
|  | BOOL WINAPI PathIsRootA(LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | { | 
|  | if (!lpszPath[1]) | 
|  | return TRUE; /* \ */ | 
|  | else if (lpszPath[1]=='\\') | 
|  | { | 
|  | BOOL bSeenSlash = FALSE; | 
|  | lpszPath += 2; | 
|  |  | 
|  | /* Check for UNC root path */ | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | { | 
|  | if (bSeenSlash) | 
|  | return FALSE; | 
|  | bSeenSlash = TRUE; | 
|  | } | 
|  | lpszPath = CharNextA(lpszPath); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | } | 
|  | else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') | 
|  | return TRUE; /* X:\ */ | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsRootW		[SHLWAPI.@] | 
|  | * | 
|  | * See PathIsRootA. | 
|  | */ | 
|  | BOOL WINAPI PathIsRootW(LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | { | 
|  | if (!lpszPath[1]) | 
|  | return TRUE; /* \ */ | 
|  | else if (lpszPath[1]=='\\') | 
|  | { | 
|  | BOOL bSeenSlash = FALSE; | 
|  | lpszPath += 2; | 
|  |  | 
|  | /* Check for UNC root path */ | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | { | 
|  | if (bSeenSlash) | 
|  | return FALSE; | 
|  | bSeenSlash = TRUE; | 
|  | } | 
|  | lpszPath = CharNextW(lpszPath); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | } | 
|  | else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') | 
|  | return TRUE; /* X:\ */ | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsDirectoryA	[SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is a valid directory | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check. | 
|  | * | 
|  | * RETURNS | 
|  | *  FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes) | 
|  | *  FALSE if lpszPath is invalid or not a directory. | 
|  | * | 
|  | * NOTES | 
|  | *  Although this function is prototyped as returning a BOOL, it returns | 
|  | *  FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as: | 
|  | * | 
|  | *|  if (PathIsDirectoryA("c:\\windows\\") == TRUE) | 
|  | *|    ... | 
|  | * | 
|  | *  will always fail. | 
|  | */ | 
|  | BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath) | 
|  | { | 
|  | DWORD dwAttr; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || PathIsUNCServerA(lpszPath)) | 
|  | return FALSE; | 
|  |  | 
|  | if (PathIsUNCServerShareA(lpszPath)) | 
|  | { | 
|  | FIXME("UNC Server Share not yet supported - FAILING\n"); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES) | 
|  | return FALSE; | 
|  | return dwAttr & FILE_ATTRIBUTE_DIRECTORY; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsDirectoryW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathIsDirectoryA. | 
|  | */ | 
|  | BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath) | 
|  | { | 
|  | DWORD dwAttr; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || PathIsUNCServerW(lpszPath)) | 
|  | return FALSE; | 
|  |  | 
|  | if (PathIsUNCServerShareW(lpszPath)) | 
|  | { | 
|  | FIXME("UNC Server Share not yet supported - FAILING\n"); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) | 
|  | return FALSE; | 
|  | return dwAttr & FILE_ATTRIBUTE_DIRECTORY; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFileExistsA	[SHLWAPI.@] | 
|  | * | 
|  | * Determine if a file exists. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the file exists and is readable | 
|  | *  FALSE Otherwise | 
|  | */ | 
|  | BOOL WINAPI PathFileExistsA(LPCSTR lpszPath) | 
|  | { | 
|  | UINT iPrevErrMode; | 
|  | DWORD dwAttr; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | iPrevErrMode = SetErrorMode(1); | 
|  | dwAttr = GetFileAttributesA(lpszPath); | 
|  | SetErrorMode(iPrevErrMode); | 
|  | return dwAttr == INVALID_FILE_ATTRIBUTES ? FALSE : TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFileExistsW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathFileExistsA. | 
|  | */ | 
|  | BOOL WINAPI PathFileExistsW(LPCWSTR lpszPath) | 
|  | { | 
|  | UINT iPrevErrMode; | 
|  | DWORD dwAttr; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | iPrevErrMode = SetErrorMode(1); | 
|  | dwAttr = GetFileAttributesW(lpszPath); | 
|  | SetErrorMode(iPrevErrMode); | 
|  | return dwAttr == INVALID_FILE_ATTRIBUTES ? FALSE : TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFileExistsAndAttributesA	[SHLWAPI.445] | 
|  | * | 
|  | * Determine if a file exists. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | *  dwAttr   [O] attributes of file | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the file exists and is readable | 
|  | *  FALSE Otherwise | 
|  | */ | 
|  | BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr) | 
|  | { | 
|  | UINT iPrevErrMode; | 
|  | DWORD dwVal = 0; | 
|  |  | 
|  | TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr); | 
|  |  | 
|  | if (dwAttr) | 
|  | *dwAttr = INVALID_FILE_ATTRIBUTES; | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); | 
|  | dwVal = GetFileAttributesA(lpszPath); | 
|  | SetErrorMode(iPrevErrMode); | 
|  | if (dwAttr) | 
|  | *dwAttr = dwVal; | 
|  | return (dwVal != INVALID_FILE_ATTRIBUTES); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFileExistsAndAttributesW	[SHLWAPI.446] | 
|  | * | 
|  | * See PathFileExistsA. | 
|  | */ | 
|  | BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr) | 
|  | { | 
|  | UINT iPrevErrMode; | 
|  | DWORD dwVal; | 
|  |  | 
|  | TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); | 
|  | dwVal = GetFileAttributesW(lpszPath); | 
|  | SetErrorMode(iPrevErrMode); | 
|  | if (dwAttr) | 
|  | *dwAttr = dwVal; | 
|  | return (dwVal != INVALID_FILE_ATTRIBUTES); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMatchSingleMaskA	[internal] | 
|  | */ | 
|  | static BOOL WINAPI PathMatchSingleMaskA(LPCSTR name, LPCSTR mask) | 
|  | { | 
|  | while (*name && *mask && *mask!=';') | 
|  | { | 
|  | if (*mask == '*') | 
|  | { | 
|  | do | 
|  | { | 
|  | if (PathMatchSingleMaskA(name,mask+1)) | 
|  | return TRUE;  /* try substrings */ | 
|  | } while (*name++); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if (toupper(*mask) != toupper(*name) && *mask != '?') | 
|  | return FALSE; | 
|  |  | 
|  | name = CharNextA(name); | 
|  | mask = CharNextA(mask); | 
|  | } | 
|  |  | 
|  | if (!*name) | 
|  | { | 
|  | while (*mask == '*') | 
|  | mask++; | 
|  | if (!*mask || *mask == ';') | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMatchSingleMaskW	[internal] | 
|  | */ | 
|  | static BOOL WINAPI PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask) | 
|  | { | 
|  | while (*name && *mask && *mask != ';') | 
|  | { | 
|  | if (*mask == '*') | 
|  | { | 
|  | do | 
|  | { | 
|  | if (PathMatchSingleMaskW(name,mask+1)) | 
|  | return TRUE;  /* try substrings */ | 
|  | } while (*name++); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if (toupperW(*mask) != toupperW(*name) && *mask != '?') | 
|  | return FALSE; | 
|  |  | 
|  | name = CharNextW(name); | 
|  | mask = CharNextW(mask); | 
|  | } | 
|  | if (!*name) | 
|  | { | 
|  | while (*mask == '*') | 
|  | mask++; | 
|  | if (!*mask || *mask == ';') | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMatchSpecA	[SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path matches one or more search masks. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | *  lpszMask [I] Search mask(s) | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If lpszPath is valid and is matched | 
|  | *  FALSE Otherwise | 
|  | * | 
|  | * NOTES | 
|  | *  Multiple search masks may be given if they are separated by ";". The | 
|  | *  pattern "*.*" is treated specially in that it matches all paths (for | 
|  | *  backwards compatibility with DOS). | 
|  | */ | 
|  | BOOL WINAPI PathMatchSpecA(LPCSTR lpszPath, LPCSTR lpszMask) | 
|  | { | 
|  | TRACE("(%s,%s)\n", lpszPath, lpszMask); | 
|  |  | 
|  | if (!lstrcmpA(lpszMask, "*.*")) | 
|  | return TRUE; /* Matches every path */ | 
|  |  | 
|  | while (*lpszMask) | 
|  | { | 
|  | if (PathMatchSingleMaskA(lpszPath, lpszMask)) | 
|  | return TRUE; /* Matches the current mask */ | 
|  |  | 
|  | while (*lpszMask && *lpszMask != ';') | 
|  | lpszMask = CharNextA(lpszMask); | 
|  |  | 
|  | if (*lpszMask == ';') | 
|  | { | 
|  | lpszMask++; | 
|  | while (*lpszMask == ' ') | 
|  | lpszMask++; /*  masks may be separated by "; " */ | 
|  | } | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMatchSpecW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathMatchSpecA. | 
|  | */ | 
|  | BOOL WINAPI PathMatchSpecW(LPCWSTR lpszPath, LPCWSTR lpszMask) | 
|  | { | 
|  | static const WCHAR szStarDotStar[] = { '*', '.', '*', '\0' }; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszMask)); | 
|  |  | 
|  | if (!lstrcmpW(lpszMask, szStarDotStar)) | 
|  | return TRUE; /* Matches every path */ | 
|  |  | 
|  | while (*lpszMask) | 
|  | { | 
|  | if (PathMatchSingleMaskW(lpszPath, lpszMask)) | 
|  | return TRUE; /* Matches the current path */ | 
|  |  | 
|  | while (*lpszMask && *lpszMask != ';') | 
|  | lpszMask++; | 
|  |  | 
|  | if (*lpszMask == ';') | 
|  | { | 
|  | lpszMask++; | 
|  | while (*lpszMask == ' ') | 
|  | lpszMask++; /* Masks may be separated by "; " */ | 
|  | } | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsSameRootA	[SHLWAPI.@] | 
|  | * | 
|  | * Determine if two paths share the same root. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath1 [I] Source path | 
|  | *  lpszPath2 [I] Path to compare with | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If both paths are valid and share the same root. | 
|  | *  FALSE If either path is invalid or the paths do not share the same root. | 
|  | */ | 
|  | BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2) | 
|  | { | 
|  | LPCSTR lpszStart; | 
|  | int dwLen; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2)); | 
|  |  | 
|  | if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1))) | 
|  | return FALSE; | 
|  |  | 
|  | dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1; | 
|  | if (lpszStart - lpszPath1 > dwLen) | 
|  | return FALSE; /* Paths not common up to length of the root */ | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsSameRootW	[SHLWAPI.@] | 
|  | * | 
|  | * See PathIsSameRootA. | 
|  | */ | 
|  | BOOL WINAPI PathIsSameRootW(LPCWSTR lpszPath1, LPCWSTR lpszPath2) | 
|  | { | 
|  | LPCWSTR lpszStart; | 
|  | int dwLen; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszPath1), debugstr_w(lpszPath2)); | 
|  |  | 
|  | if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootW(lpszPath1))) | 
|  | return FALSE; | 
|  |  | 
|  | dwLen = PathCommonPrefixW(lpszPath1, lpszPath2, NULL) + 1; | 
|  | if (lpszStart - lpszPath1 > dwLen) | 
|  | return FALSE; /* Paths not common up to length of the root */ | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsContentTypeA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a file is of a given registered content type. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath        [I] File to check | 
|  | *  lpszContentType [I] Content type to check for | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If lpszPath is a given registered content type, | 
|  | *  FALSE Otherwise. | 
|  | * | 
|  | * NOTES | 
|  | *  This function looks up the registered content type for lpszPath. If | 
|  | *  a content type is registered, it is compared (case insensitively) to | 
|  | *  lpszContentType. Only if this matches does the function succeed. | 
|  | */ | 
|  | BOOL WINAPI PathIsContentTypeA(LPCSTR lpszPath, LPCSTR lpszContentType) | 
|  | { | 
|  | LPCSTR szExt; | 
|  | DWORD dwDummy; | 
|  | char szBuff[MAX_PATH]; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszContentType)); | 
|  |  | 
|  | if (lpszPath && (szExt = PathFindExtensionA(lpszPath)) && *szExt && | 
|  | !SHGetValueA(HKEY_CLASSES_ROOT, szExt, "Content Type", | 
|  | REG_NONE, szBuff, &dwDummy) && | 
|  | !strcasecmp(lpszContentType, szBuff)) | 
|  | { | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsContentTypeW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsContentTypeA. | 
|  | */ | 
|  | BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType) | 
|  | { | 
|  | static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' }; | 
|  | LPCWSTR szExt; | 
|  | DWORD dwDummy; | 
|  | WCHAR szBuff[MAX_PATH]; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType)); | 
|  |  | 
|  | if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt && | 
|  | !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType, | 
|  | REG_NONE, szBuff, &dwDummy) && | 
|  | !strcmpiW(lpszContentType, szBuff)) | 
|  | { | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsFileSpecA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is a file specification. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to chack | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If lpszPath is a file specification (i.e. Contains no directories). | 
|  | *  FALSE Otherwise. | 
|  | */ | 
|  | BOOL WINAPI PathIsFileSpecA(LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\' || *lpszPath == ':') | 
|  | return FALSE; | 
|  | lpszPath = CharNextA(lpszPath); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsFileSpecW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsFileSpecA. | 
|  | */ | 
|  | BOOL WINAPI PathIsFileSpecW(LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\' || *lpszPath == ':') | 
|  | return FALSE; | 
|  | lpszPath = CharNextW(lpszPath); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsPrefixA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is a prefix of another. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPrefix [I] Prefix | 
|  | *  lpszPath   [I] Path to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If lpszPath has lpszPrefix as its prefix, | 
|  | *  FALSE If either path is NULL or lpszPrefix is not a prefix | 
|  | */ | 
|  | BOOL WINAPI PathIsPrefixA (LPCSTR lpszPrefix, LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszPrefix), debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPrefix && lpszPath && | 
|  | PathCommonPrefixA(lpszPath, lpszPrefix, NULL) == (int)strlen(lpszPrefix)) | 
|  | return TRUE; | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | *  PathIsPrefixW   [SHLWAPI.@] | 
|  | * | 
|  | *  See PathIsPrefixA. | 
|  | */ | 
|  | BOOL WINAPI PathIsPrefixW(LPCWSTR lpszPrefix, LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszPrefix), debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPrefix && lpszPath && | 
|  | PathCommonPrefixW(lpszPath, lpszPrefix, NULL) == (int)strlenW(lpszPrefix)) | 
|  | return TRUE; | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsSystemFolderA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path or file attributes are a system folder. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath  [I] Path to check. | 
|  | *  dwAttrib  [I] Attributes to check, if lpszPath is NULL. | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE   If lpszPath or dwAttrib are a system folder. | 
|  | *  FALSE  If GetFileAttributesA() fails or neither parameter is a system folder. | 
|  | */ | 
|  | BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib) | 
|  | { | 
|  | TRACE("(%s,0x%08lx)\n", debugstr_a(lpszPath), dwAttrib); | 
|  |  | 
|  | if (lpszPath && *lpszPath) | 
|  | dwAttrib = GetFileAttributesA(lpszPath); | 
|  |  | 
|  | if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || | 
|  | !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) | 
|  | return FALSE; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsSystemFolderW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsSystemFolderA. | 
|  | */ | 
|  | BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib) | 
|  | { | 
|  | TRACE("(%s,0x%08lx)\n", debugstr_w(lpszPath), dwAttrib); | 
|  |  | 
|  | if (lpszPath && *lpszPath) | 
|  | dwAttrib = GetFileAttributesW(lpszPath); | 
|  |  | 
|  | if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || | 
|  | !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) | 
|  | return FALSE; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsUNCA		[SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is in UNC format. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE: The path is UNC. | 
|  | *  FALSE: The path is not UNC or is NULL. | 
|  | */ | 
|  | BOOL WINAPI PathIsUNCA(LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\')) | 
|  | return TRUE; | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsUNCW		[SHLWAPI.@] | 
|  | * | 
|  | * See PathIsUNCA. | 
|  | */ | 
|  | BOOL WINAPI PathIsUNCW(LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\')) | 
|  | return TRUE; | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsUNCServerA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is a UNC server name ("\\SHARENAME"). | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath  [I] Path to check. | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE   If lpszPath is a valid UNC server name. | 
|  | *  FALSE  Otherwise. | 
|  | * | 
|  | * NOTES | 
|  | *  This routine is bug compatible with Win32: Server names with a | 
|  | *  trailing backslash (e.g. "\\FOO\"), return FALSE incorrectly. | 
|  | *  Fixing this bug may break other shlwapi functions! | 
|  | */ | 
|  | BOOL WINAPI PathIsUNCServerA(LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') | 
|  | { | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | return FALSE; | 
|  | lpszPath = CharNextA(lpszPath); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsUNCServerW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsUNCServerA. | 
|  | */ | 
|  | BOOL WINAPI PathIsUNCServerW(LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') | 
|  | { | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | return FALSE; | 
|  | lpszPath = CharNextW(lpszPath); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsUNCServerShareA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a path is a UNC server share ("\\SHARENAME\SHARE"). | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath  [I] Path to check. | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE   If lpszPath is a valid UNC server share. | 
|  | *  FALSE  Otherwise. | 
|  | * | 
|  | * NOTES | 
|  | *  This routine is bug compatible with Win32: Server shares with a | 
|  | *  trailing backslash (e.g. "\\FOO\BAR\"), return FALSE incorrectly. | 
|  | *  Fixing this bug may break other shlwapi functions! | 
|  | */ | 
|  | BOOL WINAPI PathIsUNCServerShareA(LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') | 
|  | { | 
|  | BOOL bSeenSlash = FALSE; | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | { | 
|  | if (bSeenSlash) | 
|  | return FALSE; | 
|  | bSeenSlash = TRUE; | 
|  | } | 
|  | lpszPath = CharNextA(lpszPath); | 
|  | } | 
|  | return bSeenSlash; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsUNCServerShareW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsUNCServerShareA. | 
|  | */ | 
|  | BOOL WINAPI PathIsUNCServerShareW(LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') | 
|  | { | 
|  | BOOL bSeenSlash = FALSE; | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == '\\') | 
|  | { | 
|  | if (bSeenSlash) | 
|  | return FALSE; | 
|  | bSeenSlash = TRUE; | 
|  | } | 
|  | lpszPath = CharNextW(lpszPath); | 
|  | } | 
|  | return bSeenSlash; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCanonicalizeA   [SHLWAPI.@] | 
|  | * | 
|  | * Convert a path to its canonical form. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszBuf  [O] Output path | 
|  | *  lpszPath [I] Path to cnonicalize | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE.  lpszBuf contains the output path, | 
|  | *  Failure: FALSE, If input path is invalid. lpszBuf is undefined | 
|  | */ | 
|  | BOOL WINAPI PathCanonicalizeA(LPSTR lpszBuf, LPCSTR lpszPath) | 
|  | { | 
|  | BOOL bRet = FALSE; | 
|  |  | 
|  | TRACE("(%p,%s)\n", lpszBuf, debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszBuf) | 
|  | *lpszBuf = '\0'; | 
|  |  | 
|  | if (!lpszBuf || !lpszPath) | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | else | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  | WCHAR szBuff[MAX_PATH]; | 
|  | MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); | 
|  | bRet = PathCanonicalizeW(szBuff, szPath); | 
|  | WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszBuf,MAX_PATH,0,0); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCanonicalizeW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathCanonicalizeA. | 
|  | */ | 
|  | BOOL WINAPI PathCanonicalizeW(LPWSTR lpszBuf, LPCWSTR lpszPath) | 
|  | { | 
|  | LPWSTR lpszDst = lpszBuf; | 
|  | LPCWSTR lpszSrc = lpszPath; | 
|  |  | 
|  | TRACE("(%p,%s)\n", lpszBuf, debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszBuf) | 
|  | *lpszDst = '\0'; | 
|  |  | 
|  | if (!lpszBuf || !lpszPath) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if (!*lpszPath) | 
|  | { | 
|  | *lpszBuf++ = '\\'; | 
|  | *lpszBuf = '\0'; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* Copy path root */ | 
|  | if (*lpszSrc == '\\') | 
|  | { | 
|  | *lpszDst++ = *lpszSrc++; | 
|  | } | 
|  | else if (*lpszSrc && lpszSrc[1] == ':') | 
|  | { | 
|  | /* X:\ */ | 
|  | *lpszDst++ = *lpszSrc++; | 
|  | *lpszDst++ = *lpszSrc++; | 
|  | if (*lpszSrc == '\\') | 
|  | *lpszDst++ = *lpszSrc++; | 
|  | } | 
|  |  | 
|  | /* Canonicalize the rest of the path */ | 
|  | while (*lpszSrc) | 
|  | { | 
|  | if (*lpszSrc == '.') | 
|  | { | 
|  | if (lpszSrc[1] == '\\' && (lpszSrc == lpszPath || lpszSrc[-1] == '\\' || lpszSrc[-1] == ':')) | 
|  | { | 
|  | lpszSrc += 2; /* Skip .\ */ | 
|  | } | 
|  | else if (lpszSrc[1] == '.' && (lpszDst == lpszBuf || lpszDst[-1] == '\\')) | 
|  | { | 
|  | /* \.. backs up a directory, over the root if it has no \ following X:. | 
|  | * .. is ignored if it would remove a UNC server name or inital \\ | 
|  | */ | 
|  | if (lpszDst != lpszBuf) | 
|  | { | 
|  | *lpszDst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */ | 
|  | if (lpszDst > lpszBuf+1 && lpszDst[-1] == '\\' && | 
|  | (lpszDst[-2] != '\\' || lpszDst > lpszBuf+2)) | 
|  | { | 
|  | if (lpszDst[-2] == ':' && (lpszDst > lpszBuf+3 || lpszDst[-3] == ':')) | 
|  | { | 
|  | lpszDst -= 2; | 
|  | while (lpszDst > lpszBuf && *lpszDst != '\\') | 
|  | lpszDst--; | 
|  | if (*lpszDst == '\\') | 
|  | lpszDst++; /* Reset to last '\' */ | 
|  | else | 
|  | lpszDst = lpszBuf; /* Start path again from new root */ | 
|  | } | 
|  | else if (lpszDst[-2] != ':' && !PathIsUNCServerShareW(lpszBuf)) | 
|  | lpszDst -= 2; | 
|  | } | 
|  | while (lpszDst > lpszBuf && *lpszDst != '\\') | 
|  | lpszDst--; | 
|  | if (lpszDst == lpszBuf) | 
|  | { | 
|  | *lpszDst++ = '\\'; | 
|  | lpszSrc++; | 
|  | } | 
|  | } | 
|  | lpszSrc += 2; /* Skip .. in src path */ | 
|  | } | 
|  | else | 
|  | *lpszDst++ = *lpszSrc++; | 
|  | } | 
|  | else | 
|  | *lpszDst++ = *lpszSrc++; | 
|  | } | 
|  | /* Append \ to naked drive specs */ | 
|  | if (lpszDst - lpszBuf == 2 && lpszDst[-1] == ':') | 
|  | *lpszDst++ = '\\'; | 
|  | *lpszDst++ = '\0'; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindNextComponentA   [SHLWAPI.@] | 
|  | * | 
|  | * Find the next component in a path. | 
|  | * | 
|  | * PARAMS | 
|  | *   lpszPath [I] Path to find next component in | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to the next component, or the end of the string. | 
|  | *  Failure: NULL, If lpszPath is invalid | 
|  | * | 
|  | * NOTES | 
|  | *  A 'component' is either a backslash character (\) or UNC marker (\\). | 
|  | *  Because of this, relative paths (e.g "c:foo") are regarded as having | 
|  | *  only one component. | 
|  | */ | 
|  | LPSTR WINAPI PathFindNextComponentA(LPCSTR lpszPath) | 
|  | { | 
|  | LPSTR lpszSlash; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if(!lpszPath || !*lpszPath) | 
|  | return NULL; | 
|  |  | 
|  | if ((lpszSlash = StrChrA(lpszPath, '\\'))) | 
|  | { | 
|  | if (lpszSlash[1] == '\\') | 
|  | lpszSlash++; | 
|  | return lpszSlash + 1; | 
|  | } | 
|  | return (LPSTR)lpszPath + strlen(lpszPath); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindNextComponentW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathFindNextComponentA. | 
|  | */ | 
|  | LPWSTR WINAPI PathFindNextComponentW(LPCWSTR lpszPath) | 
|  | { | 
|  | LPWSTR lpszSlash; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if(!lpszPath || !*lpszPath) | 
|  | return NULL; | 
|  |  | 
|  | if ((lpszSlash = StrChrW(lpszPath, '\\'))) | 
|  | { | 
|  | if (lpszSlash[1] == '\\') | 
|  | lpszSlash++; | 
|  | return lpszSlash + 1; | 
|  | } | 
|  | return (LPWSTR)lpszPath + strlenW(lpszPath); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathAddExtensionA   [SHLWAPI.@] | 
|  | * | 
|  | * Add a file extension to a path | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath      [I/O] Path to add extension to | 
|  | *  lpszExtension [I]   Extension to add to lpszPath | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the path was modified, | 
|  | *  FALSE If lpszPath or lpszExtension are invalid, lpszPath has an | 
|  | *        extension allready, or the new path length is too big. | 
|  | * | 
|  | * FIXME | 
|  | *  What version of shlwapi.dll adds "exe" if lpszExtension is NULL? Win2k | 
|  | *  does not do this, so the behaviour was removed. | 
|  | */ | 
|  | BOOL WINAPI PathAddExtensionA(LPSTR lpszPath, LPCSTR lpszExtension) | 
|  | { | 
|  | size_t dwLen; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExtension)); | 
|  |  | 
|  | if (!lpszPath || !lpszExtension || *(PathFindExtensionA(lpszPath))) | 
|  | return FALSE; | 
|  |  | 
|  | dwLen = strlen(lpszPath); | 
|  |  | 
|  | if (dwLen + strlen(lpszExtension) >= MAX_PATH) | 
|  | return FALSE; | 
|  |  | 
|  | strcpy(lpszPath + dwLen, lpszExtension); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathAddExtensionW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathAddExtensionA. | 
|  | */ | 
|  | BOOL WINAPI PathAddExtensionW(LPWSTR lpszPath, LPCWSTR lpszExtension) | 
|  | { | 
|  | size_t dwLen; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExtension)); | 
|  |  | 
|  | if (!lpszPath || !lpszExtension || *(PathFindExtensionW(lpszPath))) | 
|  | return FALSE; | 
|  |  | 
|  | dwLen = strlenW(lpszPath); | 
|  |  | 
|  | if (dwLen + strlenW(lpszExtension) >= MAX_PATH) | 
|  | return FALSE; | 
|  |  | 
|  | strcpyW(lpszPath + dwLen, lpszExtension); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMakePrettyA   [SHLWAPI.@] | 
|  | * | 
|  | * Convert an uppercase DOS filename into lowercase. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to convert. | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the path was an uppercase DOS path and was converted, | 
|  | *  FALSE Otherwise. | 
|  | */ | 
|  | BOOL WINAPI PathMakePrettyA(LPSTR lpszPath) | 
|  | { | 
|  | LPSTR pszIter = lpszPath; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!pszIter || !*pszIter) | 
|  | return FALSE; | 
|  |  | 
|  | while (*pszIter) | 
|  | { | 
|  | if (islower(*pszIter) || IsDBCSLeadByte(*pszIter)) | 
|  | return FALSE; /* Not DOS path */ | 
|  | pszIter++; | 
|  | } | 
|  | pszIter = lpszPath + 1; | 
|  | while (*pszIter) | 
|  | { | 
|  | *pszIter = tolower(*pszIter); | 
|  | pszIter++; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMakePrettyW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathMakePrettyA. | 
|  | */ | 
|  | BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath) | 
|  | { | 
|  | LPWSTR pszIter = lpszPath; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!pszIter || !*pszIter) | 
|  | return FALSE; | 
|  |  | 
|  | while (*pszIter) | 
|  | { | 
|  | if (islowerW(*pszIter)) | 
|  | return FALSE; /* Not DOS path */ | 
|  | pszIter++; | 
|  | } | 
|  | pszIter = lpszPath + 1; | 
|  | while (*pszIter) | 
|  | { | 
|  | *pszIter = tolowerW(*pszIter); | 
|  | pszIter++; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCommonPrefixA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine the length of the common prefix between two paths. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszFile1 [I] First path for comparison | 
|  | *  lpszFile2 [I] Second path for comparison | 
|  | *  achPath   [O] Destination for common prefix string | 
|  | * | 
|  | * RETURNS | 
|  | *  The length of the common prefix. This is 0 if there is no common | 
|  | *  prefix between the paths or if any parameters are invalid. If the prefix | 
|  | *  is non-zero and achPath is not NULL, achPath is filled with the common | 
|  | *  part of the prefix and NUL terminated. | 
|  | * | 
|  | * NOTES | 
|  | *  A common prefix of 2 is always returned as 3. It is thus possible for | 
|  | *  the length returned to be invalid (i.e. Longer than one or both of the | 
|  | *  strings given as parameters). This Win32 behaviour has been implemented | 
|  | *  here, and cannot be changed (fixed?) without breaking other SHLWAPI calls. | 
|  | *  To work around this when using this function, always check that the byte | 
|  | *  at [common_prefix_len-1] is not a NUL. If it is, deduct 1 from the prefix. | 
|  | */ | 
|  | int WINAPI PathCommonPrefixA(LPCSTR lpszFile1, LPCSTR lpszFile2, LPSTR achPath) | 
|  | { | 
|  | size_t iLen = 0; | 
|  | LPCSTR lpszIter1 = lpszFile1; | 
|  | LPCSTR lpszIter2 = lpszFile2; | 
|  |  | 
|  | TRACE("(%s,%s,%p)\n", debugstr_a(lpszFile1), debugstr_a(lpszFile2), achPath); | 
|  |  | 
|  | if (achPath) | 
|  | *achPath = '\0'; | 
|  |  | 
|  | if (!lpszFile1 || !lpszFile2) | 
|  | return 0; | 
|  |  | 
|  | /* Handle roots first */ | 
|  | if (PathIsUNCA(lpszFile1)) | 
|  | { | 
|  | if (!PathIsUNCA(lpszFile2)) | 
|  | return 0; | 
|  | lpszIter1 += 2; | 
|  | lpszIter2 += 2; | 
|  | } | 
|  | else if (PathIsUNCA(lpszFile2)) | 
|  | return 0; /* Know already lpszFile1 is not UNC */ | 
|  |  | 
|  | do | 
|  | { | 
|  | /* Update len */ | 
|  | if ((!*lpszIter1 || *lpszIter1 == '\\') && | 
|  | (!*lpszIter2 || *lpszIter2 == '\\')) | 
|  | iLen = lpszIter1 - lpszFile1; /* Common to this point */ | 
|  |  | 
|  | if (!*lpszIter1 || (tolower(*lpszIter1) != tolower(*lpszIter2))) | 
|  | break; /* Strings differ at this point */ | 
|  |  | 
|  | lpszIter1++; | 
|  | lpszIter2++; | 
|  | } while (1); | 
|  |  | 
|  | if (iLen == 2) | 
|  | iLen++; /* Feature/Bug compatible with Win32 */ | 
|  |  | 
|  | if (iLen && achPath) | 
|  | { | 
|  | memcpy(achPath,lpszFile1,iLen); | 
|  | achPath[iLen] = '\0'; | 
|  | } | 
|  | return iLen; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCommonPrefixW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathCommonPrefixA. | 
|  | */ | 
|  | int WINAPI PathCommonPrefixW(LPCWSTR lpszFile1, LPCWSTR lpszFile2, LPWSTR achPath) | 
|  | { | 
|  | size_t iLen = 0; | 
|  | LPCWSTR lpszIter1 = lpszFile1; | 
|  | LPCWSTR lpszIter2 = lpszFile2; | 
|  |  | 
|  | TRACE("(%s,%s,%p)\n", debugstr_w(lpszFile1), debugstr_w(lpszFile2), achPath); | 
|  |  | 
|  | if (achPath) | 
|  | *achPath = '\0'; | 
|  |  | 
|  | if (!lpszFile1 || !lpszFile2) | 
|  | return 0; | 
|  |  | 
|  | /* Handle roots first */ | 
|  | if (PathIsUNCW(lpszFile1)) | 
|  | { | 
|  | if (!PathIsUNCW(lpszFile2)) | 
|  | return 0; | 
|  | lpszIter1 += 2; | 
|  | lpszIter2 += 2; | 
|  | } | 
|  | else if (PathIsUNCW(lpszFile2)) | 
|  | return 0; /* Know already lpszFile1 is not UNC */ | 
|  |  | 
|  | do | 
|  | { | 
|  | /* Update len */ | 
|  | if ((!*lpszIter1 || *lpszIter1 == '\\') && | 
|  | (!*lpszIter2 || *lpszIter2 == '\\')) | 
|  | iLen = lpszIter1 - lpszFile1; /* Common to this point */ | 
|  |  | 
|  | if (!*lpszIter1 || (tolowerW(*lpszIter1) != tolowerW(*lpszIter2))) | 
|  | break; /* Strings differ at this point */ | 
|  |  | 
|  | lpszIter1++; | 
|  | lpszIter2++; | 
|  | } while (1); | 
|  |  | 
|  | if (iLen == 2) | 
|  | iLen++; /* Feature/Bug compatible with Win32 */ | 
|  |  | 
|  | if (iLen && achPath) | 
|  | { | 
|  | memcpy(achPath,lpszFile1,iLen * sizeof(WCHAR)); | 
|  | achPath[iLen] = '\0'; | 
|  | } | 
|  | return iLen; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCompactPathA   [SHLWAPI.@] | 
|  | * | 
|  | * Make a path fit into a given width when printed to a DC. | 
|  | * | 
|  | * PARAMS | 
|  | *  hDc      [I]   Destination DC | 
|  | *  lpszPath [I/O] Path to be printed to hDc | 
|  | *  dx       [I]   Desired width | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the path was modified. | 
|  | *  FALSE Otherwise. | 
|  | */ | 
|  | BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx) | 
|  | { | 
|  | BOOL bRet = FALSE; | 
|  |  | 
|  | TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  | MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); | 
|  | bRet = PathCompactPathW(hDC, szPath, dx); | 
|  | WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCompactPathW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathCompactPathA. | 
|  | */ | 
|  | BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx) | 
|  | { | 
|  | static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; | 
|  | BOOL bRet = TRUE; | 
|  | HDC hdc = 0; | 
|  | WCHAR buff[MAX_PATH]; | 
|  | SIZE size; | 
|  | DWORD dwLen; | 
|  |  | 
|  | TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return bRet; | 
|  |  | 
|  | if (!hDC) | 
|  | hdc = hDC = GetDC(0); | 
|  |  | 
|  | /* Get the length of the whole path */ | 
|  | dwLen = strlenW(lpszPath); | 
|  | GetTextExtentPointW(hDC, lpszPath, dwLen, &size); | 
|  |  | 
|  | if ((UINT)size.cx > dx) | 
|  | { | 
|  | /* Path too big, must reduce it */ | 
|  | LPWSTR sFile; | 
|  | DWORD dwEllipsesLen = 0, dwPathLen = 0; | 
|  |  | 
|  | sFile = PathFindFileNameW(lpszPath); | 
|  | if (sFile != lpszPath) | 
|  | sFile = CharPrevW(lpszPath, sFile); | 
|  |  | 
|  | /* Get the size of ellipses */ | 
|  | GetTextExtentPointW(hDC, szEllipses, 3, &size); | 
|  | dwEllipsesLen = size.cx; | 
|  | /* Get the size of the file name */ | 
|  | GetTextExtentPointW(hDC, sFile, strlenW(sFile), &size); | 
|  | dwPathLen = size.cx; | 
|  |  | 
|  | if (sFile != lpszPath) | 
|  | { | 
|  | LPWSTR sPath = sFile; | 
|  | BOOL bEllipses = FALSE; | 
|  |  | 
|  | /* The path includes a file name. Include as much of the path prior to | 
|  | * the file name as possible, allowing for the ellipses, e.g: | 
|  | * c:\some very long path\filename ==> c:\some v...\filename | 
|  | */ | 
|  | strncpyW(buff, sFile, MAX_PATH); | 
|  |  | 
|  | do | 
|  | { | 
|  | DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen; | 
|  |  | 
|  | GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size); | 
|  | dwTotalLen += size.cx; | 
|  | if (dwTotalLen <= dx) | 
|  | break; | 
|  | sPath = CharPrevW(lpszPath, sPath); | 
|  | if (!bEllipses) | 
|  | { | 
|  | bEllipses = TRUE; | 
|  | sPath = CharPrevW(lpszPath, sPath); | 
|  | sPath = CharPrevW(lpszPath, sPath); | 
|  | } | 
|  | } while (sPath > lpszPath); | 
|  |  | 
|  | if (sPath > lpszPath) | 
|  | { | 
|  | if (bEllipses) | 
|  | { | 
|  | strcpyW(sPath, szEllipses); | 
|  | strcpyW(sPath+3, buff); | 
|  | } | 
|  | bRet = TRUE; | 
|  | goto end; | 
|  | } | 
|  | strcpyW(lpszPath, szEllipses); | 
|  | strcpyW(lpszPath+3, buff); | 
|  | bRet = FALSE; | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | /* Trim the path by adding ellipses to the end, e.g: | 
|  | * A very long file name.txt ==> A very... | 
|  | */ | 
|  | dwLen = strlenW(lpszPath); | 
|  |  | 
|  | if (dwLen > MAX_PATH - 3) | 
|  | dwLen =  MAX_PATH - 3; | 
|  | strncpyW(buff, sFile, dwLen); | 
|  |  | 
|  | do { | 
|  | dwLen--; | 
|  | GetTextExtentPointW(hDC, buff, dwLen, &size); | 
|  | } while (dwLen && size.cx + dwEllipsesLen > dx); | 
|  |  | 
|  | if (!dwLen) | 
|  | { | 
|  | DWORD dwWritten = 0; | 
|  |  | 
|  | dwEllipsesLen /= 3; /* Size of a single '.' */ | 
|  |  | 
|  | /* Write as much of the Ellipses string as possible */ | 
|  | while (dwWritten + dwEllipsesLen < dx && dwLen < 3) | 
|  | { | 
|  | *lpszPath++ = '.'; | 
|  | dwWritten += dwEllipsesLen; | 
|  | dwLen++; | 
|  | } | 
|  | *lpszPath = '\0'; | 
|  | bRet = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | strcpyW(buff + dwLen, szEllipses); | 
|  | strcpyW(lpszPath, buff); | 
|  | } | 
|  | } | 
|  |  | 
|  | end: | 
|  | if (hdc) | 
|  | ReleaseDC(0, hdc); | 
|  |  | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathGetCharTypeA   [SHLWAPI.@] | 
|  | * | 
|  | * Categorise a character from a file path. | 
|  | * | 
|  | * PARAMS | 
|  | *  ch [I] Character to get the type of | 
|  | * | 
|  | * RETURNS | 
|  | *  A set of GCT_ bit flags (from "shlwapi.h") indicating the character type. | 
|  | */ | 
|  | UINT WINAPI PathGetCharTypeA(UCHAR ch) | 
|  | { | 
|  | return PathGetCharTypeW(ch); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathGetCharTypeW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathGetCharTypeA. | 
|  | */ | 
|  | UINT WINAPI PathGetCharTypeW(WCHAR ch) | 
|  | { | 
|  | UINT flags = 0; | 
|  |  | 
|  | TRACE("(%d)\n", ch); | 
|  |  | 
|  | if (!ch || ch < ' ' || ch == '<' || ch == '>' || | 
|  | ch == '"' || ch == '|' || ch == '/') | 
|  | flags = GCT_INVALID; /* Invalid */ | 
|  | else if (ch == '*' || ch=='?') | 
|  | flags = GCT_WILD; /* Wildchars */ | 
|  | else if ((ch == '\\') || (ch == ':')) | 
|  | return GCT_SEPARATOR; /* Path separators */ | 
|  | else | 
|  | { | 
|  | if (ch < 126) | 
|  | { | 
|  | if ((ch & 0x1 && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' || | 
|  | ch == '.' || ch == '@' || ch == '^' || | 
|  | ch == '\'' || ch == 130 || ch == '`') | 
|  | flags |= GCT_SHORTCHAR; /* All these are valid for DOS */ | 
|  | } | 
|  | else | 
|  | flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */ | 
|  | flags |= GCT_LFNCHAR; /* Valid for long file names */ | 
|  | } | 
|  | return flags; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * SHLWAPI_UseSystemForSystemFolders | 
|  | * | 
|  | * Internal helper for PathMakeSystemFolderW. | 
|  | */ | 
|  | static BOOL WINAPI SHLWAPI_UseSystemForSystemFolders() | 
|  | { | 
|  | static BOOL bCheckedReg = FALSE; | 
|  | static BOOL bUseSystemForSystemFolders = FALSE; | 
|  |  | 
|  | if (!bCheckedReg) | 
|  | { | 
|  | bCheckedReg = TRUE; | 
|  |  | 
|  | /* Key tells Win what file attributes to use on system folders */ | 
|  | if (SHGetValueA(HKEY_LOCAL_MACHINE, | 
|  | "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", | 
|  | "UseSystemForSystemFolders", 0, 0, 0)) | 
|  | bUseSystemForSystemFolders = TRUE; | 
|  | } | 
|  | return bUseSystemForSystemFolders; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMakeSystemFolderA   [SHLWAPI.@] | 
|  | * | 
|  | * Set system folder attribute for a path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] The path to turn into a system folder | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the path was changed to/already was a system folder | 
|  | *  FALSE If the path is invalid or SetFileAttributesA() fails | 
|  | */ | 
|  | BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath) | 
|  | { | 
|  | BOOL bRet = FALSE; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath && *lpszPath) | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  | MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); | 
|  | bRet = PathMakeSystemFolderW(szPath); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathMakeSystemFolderW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathMakeSystemFolderA. | 
|  | */ | 
|  | BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath) | 
|  | { | 
|  | DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr; | 
|  | WCHAR buff[MAX_PATH]; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !*lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | /* If the directory is already a system directory, don't do anything */ | 
|  | GetSystemDirectoryW(buff, MAX_PATH); | 
|  | if (!strcmpW(buff, lpszPath)) | 
|  | return TRUE; | 
|  |  | 
|  | GetWindowsDirectoryW(buff, MAX_PATH); | 
|  | if (!strcmpW(buff, lpszPath)) | 
|  | return TRUE; | 
|  |  | 
|  | /* "UseSystemForSystemFolders" Tells Win what attributes to use */ | 
|  | if (SHLWAPI_UseSystemForSystemFolders()) | 
|  | dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM; | 
|  |  | 
|  | if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) | 
|  | return FALSE; | 
|  |  | 
|  | /* Change file attributes to system attributes */ | 
|  | dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY); | 
|  | return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRenameExtensionA   [SHLWAPI.@] | 
|  | * | 
|  | * Swap the file extension in a path with another extension. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to swap the extension in | 
|  | *  lpszExt  [I]   The new extension | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  if lpszPath was modified, | 
|  | *  FALSE if lpszPath or lpszExt is NULL, or the new path is too long | 
|  | */ | 
|  | BOOL WINAPI PathRenameExtensionA(LPSTR lpszPath, LPCSTR lpszExt) | 
|  | { | 
|  | LPSTR lpszExtension; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExt)); | 
|  |  | 
|  | lpszExtension = PathFindExtensionA(lpszPath); | 
|  |  | 
|  | if (!lpszExtension || (lpszExtension - lpszPath + strlen(lpszExt) >= MAX_PATH)) | 
|  | return FALSE; | 
|  |  | 
|  | strcpy(lpszExtension, lpszExt); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRenameExtensionW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathRenameExtensionA. | 
|  | */ | 
|  | BOOL WINAPI PathRenameExtensionW(LPWSTR lpszPath, LPCWSTR lpszExt) | 
|  | { | 
|  | LPWSTR lpszExtension; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExt)); | 
|  |  | 
|  | lpszExtension = PathFindExtensionW(lpszPath); | 
|  |  | 
|  | if (!lpszExtension || (lpszExtension - lpszPath + strlenW(lpszExt) >= MAX_PATH)) | 
|  | return FALSE; | 
|  |  | 
|  | strcpyW(lpszExtension, lpszExt); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathSearchAndQualifyA   [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a given path is correct and fully qualified. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | *  lpszBuf  [O] Output for correct path | 
|  | *  cchBuf   [I] Size of lpszBuf | 
|  | * | 
|  | * RETURNS | 
|  | *  Unknown. | 
|  | */ | 
|  | BOOL WINAPI PathSearchAndQualifyA(LPCSTR lpszPath, LPSTR lpszBuf, UINT cchBuf) | 
|  | { | 
|  | TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszPath), lpszBuf, cchBuf); | 
|  |  | 
|  | if(SearchPathA(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL)) | 
|  | return TRUE; | 
|  | return !!GetFullPathNameA(lpszPath, cchBuf, lpszBuf, NULL); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathSearchAndQualifyW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathSearchAndQualifyA. | 
|  | */ | 
|  | BOOL WINAPI PathSearchAndQualifyW(LPCWSTR lpszPath, LPWSTR lpszBuf, UINT cchBuf) | 
|  | { | 
|  | TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszPath), lpszBuf, cchBuf); | 
|  |  | 
|  | if(SearchPathW(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL)) | 
|  | return TRUE; | 
|  | return !!GetFullPathNameW(lpszPath, cchBuf, lpszBuf, NULL); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathSkipRootA   [SHLWAPI.@] | 
|  | * | 
|  | * Return the portion of a path following the drive letter or mount point. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] The path to skip on | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to the next character after the root. | 
|  | *  Failure: NULL, if lpszPath is invalid, has no root or is a multibyte string. | 
|  | */ | 
|  | LPSTR WINAPI PathSkipRootA(LPCSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !*lpszPath) | 
|  | return NULL; | 
|  |  | 
|  | if (*lpszPath == '\\' && lpszPath[1] == '\\') | 
|  | { | 
|  | /* Network share: skip share server and mount point */ | 
|  | lpszPath += 2; | 
|  | if ((lpszPath = StrChrA(lpszPath, '\\')) && | 
|  | (lpszPath = StrChrA(lpszPath + 1, '\\'))) | 
|  | lpszPath++; | 
|  | return (LPSTR)lpszPath; | 
|  | } | 
|  |  | 
|  | if (IsDBCSLeadByte(*lpszPath)) | 
|  | return NULL; | 
|  |  | 
|  | /* Check x:\ */ | 
|  | if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') | 
|  | return (LPSTR)lpszPath + 3; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathSkipRootW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathSkipRootA. | 
|  | */ | 
|  | LPWSTR WINAPI PathSkipRootW(LPCWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !*lpszPath) | 
|  | return NULL; | 
|  |  | 
|  | if (*lpszPath == '\\' && lpszPath[1] == '\\') | 
|  | { | 
|  | /* Network share: skip share server and mount point */ | 
|  | lpszPath += 2; | 
|  | if ((lpszPath = StrChrW(lpszPath, '\\')) && | 
|  | (lpszPath = StrChrW(lpszPath + 1, '\\'))) | 
|  | lpszPath++; | 
|  | return (LPWSTR)lpszPath; | 
|  | } | 
|  |  | 
|  | /* Check x:\ */ | 
|  | if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') | 
|  | return (LPWSTR)lpszPath + 3; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCreateFromUrlA   [SHLWAPI.@] | 
|  | * | 
|  | * See PathCreateFromUrlW | 
|  | */ | 
|  | HRESULT WINAPI PathCreateFromUrlA(LPCSTR pszUrl, LPSTR pszPath, | 
|  | LPDWORD pcchPath, DWORD dwReserved) | 
|  | { | 
|  | WCHAR bufW[MAX_PATH]; | 
|  | WCHAR *pathW = bufW; | 
|  | UNICODE_STRING urlW; | 
|  | HRESULT ret; | 
|  | DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA; | 
|  |  | 
|  | if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl)) | 
|  | return E_INVALIDARG; | 
|  | if((ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved)) == E_POINTER) { | 
|  | pathW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); | 
|  | ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved); | 
|  | } | 
|  | if(ret == S_OK) { | 
|  | RtlUnicodeToMultiByteSize(&lenA, pathW, lenW * sizeof(WCHAR)); | 
|  | if(*pcchPath > lenA) { | 
|  | RtlUnicodeToMultiByteN(pszPath, *pcchPath - 1, &lenA, pathW, lenW * sizeof(WCHAR)); | 
|  | pszPath[lenA] = 0; | 
|  | *pcchPath = lenA; | 
|  | } else { | 
|  | *pcchPath = lenA + 1; | 
|  | ret = E_POINTER; | 
|  | } | 
|  | } | 
|  | if(pathW != bufW) HeapFree(GetProcessHeap(), 0, pathW); | 
|  | RtlFreeUnicodeString(&urlW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathCreateFromUrlW   [SHLWAPI.@] | 
|  | * | 
|  | * Create a path from a URL | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszUrl  [I] URL to convert into a path | 
|  | *  lpszPath [O] Output buffer for the resulting Path | 
|  | *  pcchPath [I] Length of lpszPath | 
|  | *  dwFlags  [I] Flags controlling the conversion | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. lpszPath contains the URL in path format, | 
|  | *  Failure: An HRESULT error code such as E_INVALIDARG. | 
|  | */ | 
|  | HRESULT WINAPI PathCreateFromUrlW(LPCWSTR pszUrl, LPWSTR pszPath, | 
|  | LPDWORD pcchPath, DWORD dwReserved) | 
|  | { | 
|  | static const WCHAR file_colon[] = { 'f','i','l','e',':',0 }; | 
|  | HRESULT hr; | 
|  | DWORD nslashes = 0; | 
|  | WCHAR *ptr; | 
|  |  | 
|  | TRACE("(%s,%p,%p,0x%08lx)\n", debugstr_w(pszUrl), pszPath, pcchPath, dwReserved); | 
|  |  | 
|  | if (!pszUrl || !pszPath || !pcchPath || !*pcchPath) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  |  | 
|  | if (strncmpW(pszUrl, file_colon, 5)) | 
|  | return E_INVALIDARG; | 
|  | pszUrl += 5; | 
|  |  | 
|  | while(*pszUrl == '/' || *pszUrl == '\\') { | 
|  | nslashes++; | 
|  | pszUrl++; | 
|  | } | 
|  |  | 
|  | if(isalphaW(*pszUrl) && (pszUrl[1] == ':' || pszUrl[1] == '|') && (pszUrl[2] == '/' || pszUrl[2] == '\\')) | 
|  | nslashes = 0; | 
|  |  | 
|  | switch(nslashes) { | 
|  | case 2: | 
|  | pszUrl -= 2; | 
|  | break; | 
|  | case 0: | 
|  | break; | 
|  | default: | 
|  | pszUrl -= 1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | hr = UrlUnescapeW((LPWSTR)pszUrl, pszPath, pcchPath, 0); | 
|  | if(hr != S_OK) return hr; | 
|  |  | 
|  | for(ptr = pszPath; *ptr; ptr++) | 
|  | if(*ptr == '/') *ptr = '\\'; | 
|  |  | 
|  | while(*pszPath == '\\') | 
|  | pszPath++; | 
|  |  | 
|  | if(isalphaW(*pszPath) && pszPath[1] == '|' && pszPath[2] == '\\') /* c|\ -> c:\ */ | 
|  | pszPath[1] = ':'; | 
|  |  | 
|  | if(nslashes == 2 && (ptr = strchrW(pszPath, '\\'))) { /* \\host\c:\ -> \\hostc:\ */ | 
|  | ptr++; | 
|  | if(isalphaW(*ptr) && (ptr[1] == ':' || ptr[1] == '|') && ptr[2] == '\\') { | 
|  | memmove(ptr - 1, ptr, (strlenW(ptr) + 1) * sizeof(WCHAR)); | 
|  | (*pcchPath)--; | 
|  | } | 
|  | } | 
|  |  | 
|  | TRACE("Returning %s\n",debugstr_w(pszPath)); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRelativePathToA   [SHLWAPI.@] | 
|  | * | 
|  | * Create a relative path from one path to another. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath   [O] Destination for relative path | 
|  | *  lpszFrom   [I] Source path | 
|  | *  dwAttrFrom [I] File attribute of source path | 
|  | *  lpszTo     [I] Destination path | 
|  | *  dwAttrTo   [I] File attributes of destination path | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If a relative path can be formed. lpszPath contains the new path | 
|  | *  FALSE If the paths are not relavtive or any parameters are invalid | 
|  | * | 
|  | * NOTES | 
|  | *  lpszTo should be at least MAX_PATH in length. | 
|  | * | 
|  | *  Calling this function with relative paths for lpszFrom or lpszTo may | 
|  | *  give erroneous results. | 
|  | * | 
|  | *  The Win32 version of this function contains a bug where the lpszTo string | 
|  | *  may be referenced 1 byte beyond the end of the string. As a result random | 
|  | *  garbage may be written to the output path, depending on what lies beyond | 
|  | *  the last byte of the string. This bug occurs because of the behaviour of | 
|  | *  PathCommonPrefix() (see notes for that function), and no workaround seems | 
|  | *  possible with Win32. | 
|  | * | 
|  | *  This bug has been fixed here, so for example the relative path from "\\" | 
|  | *  to "\\" is correctly determined as "." in this implementation. | 
|  | */ | 
|  | BOOL WINAPI PathRelativePathToA(LPSTR lpszPath, LPCSTR lpszFrom, DWORD dwAttrFrom, | 
|  | LPCSTR lpszTo, DWORD dwAttrTo) | 
|  | { | 
|  | BOOL bRet = FALSE; | 
|  |  | 
|  | TRACE("(%p,%s,0x%08lx,%s,0x%08lx)\n", lpszPath, debugstr_a(lpszFrom), | 
|  | dwAttrFrom, debugstr_a(lpszTo), dwAttrTo); | 
|  |  | 
|  | if(lpszPath && lpszFrom && lpszTo) | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  | WCHAR szFrom[MAX_PATH]; | 
|  | WCHAR szTo[MAX_PATH]; | 
|  | MultiByteToWideChar(CP_ACP,0,lpszFrom,-1,szFrom,MAX_PATH); | 
|  | MultiByteToWideChar(CP_ACP,0,lpszTo,-1,szTo,MAX_PATH); | 
|  | bRet = PathRelativePathToW(szPath,szFrom,dwAttrFrom,szTo,dwAttrTo); | 
|  | WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathRelativePathToW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathRelativePathToA. | 
|  | */ | 
|  | BOOL WINAPI PathRelativePathToW(LPWSTR lpszPath, LPCWSTR lpszFrom, DWORD dwAttrFrom, | 
|  | LPCWSTR lpszTo, DWORD dwAttrTo) | 
|  | { | 
|  | static const WCHAR szPrevDirSlash[] = { '.', '.', '\\', '\0' }; | 
|  | static const WCHAR szPrevDir[] = { '.', '.', '\0' }; | 
|  | WCHAR szFrom[MAX_PATH]; | 
|  | WCHAR szTo[MAX_PATH]; | 
|  | DWORD dwLen; | 
|  |  | 
|  | TRACE("(%p,%s,0x%08lx,%s,0x%08lx)\n", lpszPath, debugstr_w(lpszFrom), | 
|  | dwAttrFrom, debugstr_w(lpszTo), dwAttrTo); | 
|  |  | 
|  | if(!lpszPath || !lpszFrom || !lpszTo) | 
|  | return FALSE; | 
|  |  | 
|  | *lpszPath = '\0'; | 
|  | strncpyW(szFrom, lpszFrom, MAX_PATH); | 
|  | strncpyW(szTo, lpszTo, MAX_PATH); | 
|  |  | 
|  | if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY)) | 
|  | PathRemoveFileSpecW(szFrom); | 
|  | if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY)) | 
|  | PathRemoveFileSpecW(szTo); | 
|  |  | 
|  | /* Paths can only be relative if they have a common root */ | 
|  | if(!(dwLen = PathCommonPrefixW(szFrom, szTo, 0))) | 
|  | return FALSE; | 
|  |  | 
|  | /* Strip off lpszFrom components to the root, by adding "..\" */ | 
|  | lpszFrom = szFrom + dwLen; | 
|  | if (!*lpszFrom) | 
|  | { | 
|  | lpszPath[0] = '.'; | 
|  | lpszPath[1] = '\0'; | 
|  | } | 
|  | if (*lpszFrom == '\\') | 
|  | lpszFrom++; | 
|  |  | 
|  | while (*lpszFrom) | 
|  | { | 
|  | lpszFrom = PathFindNextComponentW(lpszFrom); | 
|  | strcatW(lpszPath, *lpszFrom ? szPrevDirSlash : szPrevDir); | 
|  | } | 
|  |  | 
|  | /* From the root add the components of lpszTo */ | 
|  | lpszTo += dwLen; | 
|  | /* We check lpszTo[-1] to avoid skipping end of string. See the notes for | 
|  | * this function. | 
|  | */ | 
|  | if (*lpszTo && lpszTo[-1]) | 
|  | { | 
|  | if (*lpszTo != '\\') | 
|  | lpszTo--; | 
|  | dwLen = strlenW(lpszPath); | 
|  | if (dwLen + strlenW(lpszTo) >= MAX_PATH) | 
|  | { | 
|  | *lpszPath = '\0'; | 
|  | return FALSE; | 
|  | } | 
|  | strcpyW(lpszPath + dwLen, lpszTo); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathUnmakeSystemFolderA   [SHLWAPI.@] | 
|  | * | 
|  | * Remove the system folder attributes from a path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] The path to remove attributes from | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE. | 
|  | *  Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling | 
|  | *           SetFileAttributesA() fails. | 
|  | */ | 
|  | BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath) | 
|  | { | 
|  | DWORD dwAttr; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES || | 
|  | !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) | 
|  | return FALSE; | 
|  |  | 
|  | dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); | 
|  | return SetFileAttributesA(lpszPath, dwAttr); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathUnmakeSystemFolderW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathUnmakeSystemFolderA. | 
|  | */ | 
|  | BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath) | 
|  | { | 
|  | DWORD dwAttr; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES || | 
|  | !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) | 
|  | return FALSE; | 
|  |  | 
|  | dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); | 
|  | return SetFileAttributesW(lpszPath, dwAttr); | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathSetDlgItemPathA   [SHLWAPI.@] | 
|  | * | 
|  | * Set the text of a dialog item to a path, shrinking the path to fit | 
|  | * if it is too big for the item. | 
|  | * | 
|  | * PARAMS | 
|  | *  hDlg     [I] Dialog handle | 
|  | *  id       [I] ID of item in the dialog | 
|  | *  lpszPath [I] Path to set as the items text | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing. | 
|  | * | 
|  | * NOTES | 
|  | *  If lpszPath is NULL, a blank string ("") is set (i.e. The previous | 
|  | *  window text is erased). | 
|  | */ | 
|  | VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath) | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  |  | 
|  | TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); | 
|  | else | 
|  | szPath[0] = '\0'; | 
|  | PathSetDlgItemPathW(hDlg, id, szPath); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathSetDlgItemPathW   [SHLWAPI.@] | 
|  | * | 
|  | * See PathSetDlgItemPathA. | 
|  | */ | 
|  | VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath) | 
|  | { | 
|  | WCHAR path[MAX_PATH + 1]; | 
|  | HWND hwItem; | 
|  | RECT rect; | 
|  | HDC hdc; | 
|  | HGDIOBJ hPrevObj; | 
|  |  | 
|  | TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!(hwItem = GetDlgItem(hDlg, id))) | 
|  | return; | 
|  |  | 
|  | if (lpszPath) | 
|  | lstrcpynW(path, lpszPath, sizeof(path) / sizeof(WCHAR)); | 
|  | else | 
|  | path[0] = '\0'; | 
|  |  | 
|  | GetClientRect(hwItem, &rect); | 
|  | hdc = GetDC(hDlg); | 
|  | hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0)); | 
|  |  | 
|  | if (hPrevObj) | 
|  | { | 
|  | PathCompactPathW(hdc, path, rect.right); | 
|  | SelectObject(hdc, hPrevObj); | 
|  | } | 
|  |  | 
|  | ReleaseDC(hDlg, hdc); | 
|  | SetWindowTextW(hwItem, path); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsNetworkPathA [SHLWAPI.@] | 
|  | * | 
|  | * Determine if the given path is a network path. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If lpszPath is a UNC share or mapped network drive, or | 
|  | *  FALSE If lpszPath is a local drive or cannot be determined | 
|  | */ | 
|  | BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath) | 
|  | { | 
|  | int dwDriveNum; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  | if (*lpszPath == '\\' && lpszPath[1] == '\\') | 
|  | return TRUE; | 
|  | dwDriveNum = PathGetDriveNumberA(lpszPath); | 
|  | if (dwDriveNum == -1) | 
|  | return FALSE; | 
|  | GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ | 
|  | return pIsNetDrive(dwDriveNum); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsNetworkPathW [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsNetworkPathA. | 
|  | */ | 
|  | BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath) | 
|  | { | 
|  | int dwDriveNum; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  | if (*lpszPath == '\\' && lpszPath[1] == '\\') | 
|  | return TRUE; | 
|  | dwDriveNum = PathGetDriveNumberW(lpszPath); | 
|  | if (dwDriveNum == -1) | 
|  | return FALSE; | 
|  | GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ | 
|  | return pIsNetDrive(dwDriveNum); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsLFNFileSpecA [SHLWAPI.@] | 
|  | * | 
|  | * Determine if the given path is a long file name | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Path to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If path is a long file name, | 
|  | *  FALSE If path is a valid DOS short file name | 
|  | */ | 
|  | BOOL WINAPI PathIsLFNFileSpecA(LPCSTR lpszPath) | 
|  | { | 
|  | DWORD dwNameLen = 0, dwExtLen = 0; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == ' ') | 
|  | return TRUE; /* DOS names cannot have spaces */ | 
|  | if (*lpszPath == '.') | 
|  | { | 
|  | if (dwExtLen) | 
|  | return TRUE; /* DOS names have only one dot */ | 
|  | dwExtLen = 1; | 
|  | } | 
|  | else if (dwExtLen) | 
|  | { | 
|  | dwExtLen++; | 
|  | if (dwExtLen > 4) | 
|  | return TRUE; /* DOS extensions are <= 3 chars*/ | 
|  | } | 
|  | else | 
|  | { | 
|  | dwNameLen++; | 
|  | if (dwNameLen > 8) | 
|  | return TRUE; /* DOS names are <= 8 chars */ | 
|  | } | 
|  | lpszPath += IsDBCSLeadByte(*lpszPath) ? 2 : 1; | 
|  | } | 
|  | return FALSE; /* Valid DOS path */ | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsLFNFileSpecW [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsLFNFileSpecA. | 
|  | */ | 
|  | BOOL WINAPI PathIsLFNFileSpecW(LPCWSTR lpszPath) | 
|  | { | 
|  | DWORD dwNameLen = 0, dwExtLen = 0; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath) | 
|  | return FALSE; | 
|  |  | 
|  | while (*lpszPath) | 
|  | { | 
|  | if (*lpszPath == ' ') | 
|  | return TRUE; /* DOS names cannot have spaces */ | 
|  | if (*lpszPath == '.') | 
|  | { | 
|  | if (dwExtLen) | 
|  | return TRUE; /* DOS names have only one dot */ | 
|  | dwExtLen = 1; | 
|  | } | 
|  | else if (dwExtLen) | 
|  | { | 
|  | dwExtLen++; | 
|  | if (dwExtLen > 4) | 
|  | return TRUE; /* DOS extensions are <= 3 chars*/ | 
|  | } | 
|  | else | 
|  | { | 
|  | dwNameLen++; | 
|  | if (dwNameLen > 8) | 
|  | return TRUE; /* DOS names are <= 8 chars */ | 
|  | } | 
|  | lpszPath++; | 
|  | } | 
|  | return FALSE; /* Valid DOS path */ | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsDirectoryEmptyA [SHLWAPI.@] | 
|  | * | 
|  | * Determine if a given directory is empty. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I] Directory to check | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the directory exists and contains no files, | 
|  | *  FALSE Otherwise | 
|  | */ | 
|  | BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath) | 
|  | { | 
|  | BOOL bRet = FALSE; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | WCHAR szPath[MAX_PATH]; | 
|  | MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); | 
|  | bRet = PathIsDirectoryEmptyW(szPath); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathIsDirectoryEmptyW [SHLWAPI.@] | 
|  | * | 
|  | * See PathIsDirectoryEmptyA. | 
|  | */ | 
|  | BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath) | 
|  | { | 
|  | static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' }; | 
|  | WCHAR szSearch[MAX_PATH]; | 
|  | DWORD dwLen; | 
|  | HANDLE hfind; | 
|  | BOOL retVal = FALSE; | 
|  | WIN32_FIND_DATAW find_data; | 
|  |  | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (!lpszPath || !PathIsDirectoryW(lpszPath)) | 
|  | return FALSE; | 
|  |  | 
|  | strncpyW(szSearch, lpszPath, MAX_PATH); | 
|  | PathAddBackslashW(szSearch); | 
|  | dwLen = strlenW(szSearch); | 
|  | if (dwLen > MAX_PATH - 4) | 
|  | return FALSE; | 
|  |  | 
|  | strcpyW(szSearch + dwLen, szAllFiles); | 
|  | hfind = FindFirstFileW(szSearch, &find_data); | 
|  |  | 
|  | if (hfind != INVALID_HANDLE_VALUE && | 
|  | find_data.cFileName[0] == '.' && | 
|  | find_data.cFileName[1] == '.') | 
|  | { | 
|  | /* The only directory entry should be the parent */ | 
|  | if (!FindNextFileW(hfind, &find_data)) | 
|  | retVal = TRUE; | 
|  | FindClose(hfind); | 
|  | } | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindSuffixArrayA [SHLWAPI.@] | 
|  | * | 
|  | * Find a suffix string in an array of suffix strings | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszSuffix [I] Suffix string to search for | 
|  | *  lppszArray [I] Array of suffix strings to search | 
|  | *  dwCount    [I] Number of elements in lppszArray | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: The index of the position of lpszSuffix in lppszArray | 
|  | *  Failure: 0, if any parameters are invalid or lpszSuffix is not found | 
|  | * | 
|  | * NOTES | 
|  | *  The search is case sensitive. | 
|  | *  The match is made against the end of the suffix string, so for example: | 
|  | *  lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not. | 
|  | */ | 
|  | LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount) | 
|  | { | 
|  | size_t dwLen; | 
|  | int dwRet = 0; | 
|  |  | 
|  | TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount); | 
|  |  | 
|  | if (lpszSuffix && lppszArray && dwCount > 0) | 
|  | { | 
|  | dwLen = strlen(lpszSuffix); | 
|  |  | 
|  | while (dwRet < dwCount) | 
|  | { | 
|  | size_t dwCompareLen = strlen(*lppszArray); | 
|  | if (dwCompareLen < dwLen) | 
|  | { | 
|  | if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) | 
|  | return *lppszArray; /* Found */ | 
|  | } | 
|  | dwRet++; | 
|  | lppszArray++; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathFindSuffixArrayW [SHLWAPI.@] | 
|  | * | 
|  | * See PathFindSuffixArrayA. | 
|  | */ | 
|  | LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount) | 
|  | { | 
|  | size_t dwLen; | 
|  | int dwRet = 0; | 
|  |  | 
|  | TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount); | 
|  |  | 
|  | if (lpszSuffix && lppszArray && dwCount > 0) | 
|  | { | 
|  | dwLen = strlenW(lpszSuffix); | 
|  |  | 
|  | while (dwRet < dwCount) | 
|  | { | 
|  | size_t dwCompareLen = strlenW(*lppszArray); | 
|  | if (dwCompareLen < dwLen) | 
|  | { | 
|  | if (!strcmpW(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) | 
|  | return *lppszArray; /* Found */ | 
|  | } | 
|  | dwRet++; | 
|  | lppszArray++; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathUndecorateA [SHLWAPI.@] | 
|  | * | 
|  | * Undecorate a file path | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszPath [I/O] Path to remove any decoration from | 
|  | * | 
|  | * RETURNS | 
|  | *  Nothing | 
|  | * | 
|  | * NOTES | 
|  | *  A decorations form is "path[n].ext" where "n" is an optional decimal number. | 
|  | */ | 
|  | VOID WINAPI PathUndecorateA(LPSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_a(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | LPSTR lpszExt = PathFindExtensionA(lpszPath); | 
|  | if (lpszExt > lpszPath && lpszExt[-1] == ']') | 
|  | { | 
|  | LPSTR lpszSkip = lpszExt - 2; | 
|  | if (*lpszSkip == '[') | 
|  | lpszSkip++;  /* [] (no number) */ | 
|  | else | 
|  | while (lpszSkip > lpszPath && isdigit(lpszSkip[-1])) | 
|  | lpszSkip--; | 
|  | if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') | 
|  | { | 
|  | /* remove the [n] */ | 
|  | lpszSkip--; | 
|  | while (*lpszExt) | 
|  | *lpszSkip++ = *lpszExt++; | 
|  | *lpszSkip = '\0'; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * PathUndecorateW [SHLWAPI.@] | 
|  | * | 
|  | * See PathUndecorateA. | 
|  | */ | 
|  | VOID WINAPI PathUndecorateW(LPWSTR lpszPath) | 
|  | { | 
|  | TRACE("(%s)\n",debugstr_w(lpszPath)); | 
|  |  | 
|  | if (lpszPath) | 
|  | { | 
|  | LPWSTR lpszExt = PathFindExtensionW(lpszPath); | 
|  | if (lpszExt > lpszPath && lpszExt[-1] == ']') | 
|  | { | 
|  | LPWSTR lpszSkip = lpszExt - 2; | 
|  | if (*lpszSkip == '[') | 
|  | lpszSkip++; /* [] (no number) */ | 
|  | else | 
|  | while (lpszSkip > lpszPath && isdigitW(lpszSkip[-1])) | 
|  | lpszSkip--; | 
|  | if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') | 
|  | { | 
|  | /* remove the [n] */ | 
|  | lpszSkip--; | 
|  | while (*lpszExt) | 
|  | *lpszSkip++ = *lpszExt++; | 
|  | *lpszSkip = '\0'; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * @     [SHLWAPI.440] | 
|  | * | 
|  | * Find localised or default web content in "%WINDOWS%\web\". | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszFile  [I] File name containing content to look for | 
|  | *  lpszPath  [O] Buffer to contain the full path to the file | 
|  | *  dwPathLen [I] Length of lpszPath | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: S_OK. lpszPath contains the full path to the content. | 
|  | *  Failure: E_FAIL. The content does not exist or lpszPath is too short. | 
|  | */ | 
|  | HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen) | 
|  | { | 
|  | WCHAR szFile[MAX_PATH], szPath[MAX_PATH]; | 
|  | HRESULT hRet; | 
|  |  | 
|  | TRACE("(%s,%p,%ld)\n", lpszFile, lpszPath, dwPathLen); | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH); | 
|  | szPath[0] = '\0'; | 
|  | hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen); | 
|  | WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0); | 
|  | return hRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * @     [SHLWAPI.441] | 
|  | * | 
|  | * Unicode version of SHGetWebFolderFilePathA. | 
|  | */ | 
|  | HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen) | 
|  | { | 
|  | static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'}; | 
|  | static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'}; | 
|  | #define szWebLen (sizeof(szWeb)/sizeof(WCHAR)) | 
|  | #define szWebMuiLen ((sizeof(szWebMui)+1)/sizeof(WCHAR)) | 
|  | DWORD dwLen, dwFileLen; | 
|  | LANGID lidSystem, lidUser; | 
|  |  | 
|  | TRACE("(%s,%p,%ld)\n", debugstr_w(lpszFile), lpszPath, dwPathLen); | 
|  |  | 
|  | /* Get base directory for web content */ | 
|  | dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen); | 
|  | if (dwLen > 0 && lpszPath[dwLen-1] == '\\') | 
|  | dwLen--; | 
|  |  | 
|  | dwFileLen = strlenW(lpszFile); | 
|  |  | 
|  | if (dwLen + dwFileLen + szWebLen >= dwPathLen) | 
|  | return E_FAIL; /* lpszPath too short */ | 
|  |  | 
|  | strcpyW(lpszPath+dwLen, szWeb); | 
|  | dwLen += szWebLen; | 
|  | dwPathLen = dwPathLen - dwLen; /* Remaining space */ | 
|  |  | 
|  | lidSystem = GetSystemDefaultUILanguage(); | 
|  | lidUser = GetUserDefaultUILanguage(); | 
|  |  | 
|  | if (lidSystem != lidUser) | 
|  | { | 
|  | if (dwFileLen + szWebMuiLen < dwPathLen) | 
|  | { | 
|  | /* Use localised content in the users UI language if present */ | 
|  | wsprintfW(lpszPath + dwLen, szWebMui, lidUser); | 
|  | strcpyW(lpszPath + dwLen + szWebMuiLen, lpszFile); | 
|  | if (PathFileExistsW(lpszPath)) | 
|  | return S_OK; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Fall back to OS default installed content */ | 
|  | strcpyW(lpszPath + dwLen, lpszFile); | 
|  | if (PathFileExistsW(lpszPath)) | 
|  | return S_OK; | 
|  | return E_FAIL; | 
|  | } |