twinapi.appcore: Implement Windows.System.Profile.AnalyticsInfo_get_DeviceFamily.
[wine.git] / dlls / shlwapi / path.c
blob7967ef4932c987aa07c91e74195257089cecd630
1 /*
2 * Path Functions
4 * Copyright 1999, 2000 Juergen Schmied
5 * Copyright 2001, 2002 Jon Griffiths
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <string.h>
24 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winreg.h"
31 #include "winternl.h"
32 #define NO_SHLWAPI_STREAM
33 #include "shlwapi.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38 /* Get a function pointer from a DLL handle */
39 #define GET_FUNC(func, module, name, fail) \
40 do { \
41 if (!func) { \
42 if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
43 func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
44 if (!func) return fail; \
45 } \
46 } while (0)
48 /* DLL handles for late bound calls */
49 static HMODULE SHLWAPI_hshell32;
51 /* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
52 typedef BOOL (WINAPI *fnpIsNetDrive)(int);
53 static fnpIsNetDrive pIsNetDrive;
55 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD);
57 /*************************************************************************
58 * PathBuildRootA [SHLWAPI.@]
60 * Create a root drive string (e.g. "A:\") from a drive number.
62 * PARAMS
63 * lpszPath [O] Destination for the drive string
65 * RETURNS
66 * lpszPath
68 * NOTES
69 * If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
71 LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive)
73 TRACE("(%p,%d)\n", lpszPath, drive);
75 if (lpszPath && drive >= 0 && drive < 26)
77 lpszPath[0] = 'A' + drive;
78 lpszPath[1] = ':';
79 lpszPath[2] = '\\';
80 lpszPath[3] = '\0';
82 return lpszPath;
85 /*************************************************************************
86 * PathBuildRootW [SHLWAPI.@]
88 * See PathBuildRootA.
90 LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive)
92 TRACE("(%p,%d)\n", lpszPath, drive);
94 if (lpszPath && drive >= 0 && drive < 26)
96 lpszPath[0] = 'A' + drive;
97 lpszPath[1] = ':';
98 lpszPath[2] = '\\';
99 lpszPath[3] = '\0';
101 return lpszPath;
104 /*************************************************************************
105 * PathRemoveArgsA [SHLWAPI.@]
107 * Strip space separated arguments from a path.
109 * PARAMS
110 * lpszPath [I/O] Path to remove arguments from
112 * RETURNS
113 * Nothing.
115 void WINAPI PathRemoveArgsA(LPSTR lpszPath)
117 TRACE("(%s)\n",debugstr_a(lpszPath));
119 if(lpszPath)
121 LPSTR lpszArgs = PathGetArgsA(lpszPath);
122 if (*lpszArgs)
123 lpszArgs[-1] = '\0';
124 else
126 LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs);
127 if(*lpszLastChar == ' ')
128 *lpszLastChar = '\0';
133 /*************************************************************************
134 * PathRemoveArgsW [SHLWAPI.@]
136 * See PathRemoveArgsA.
138 void WINAPI PathRemoveArgsW(LPWSTR lpszPath)
140 TRACE("(%s)\n",debugstr_w(lpszPath));
142 if(lpszPath)
144 LPWSTR lpszArgs = PathGetArgsW(lpszPath);
145 if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' '))
146 lpszArgs[-1] = '\0';
150 /*************************************************************************
151 * @ [SHLWAPI.4]
153 * Unicode version of PathFileExistsDefExtA.
155 BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)
157 static const WCHAR pszExts[][5] = { { '.', 'p', 'i', 'f', 0},
158 { '.', 'c', 'o', 'm', 0},
159 { '.', 'e', 'x', 'e', 0},
160 { '.', 'b', 'a', 't', 0},
161 { '.', 'l', 'n', 'k', 0},
162 { '.', 'c', 'm', 'd', 0},
163 { 0, 0, 0, 0, 0} };
165 TRACE("(%s,%ld)\n", debugstr_w(lpszPath), dwWhich);
167 if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath))
168 return FALSE;
170 if (dwWhich)
172 LPCWSTR szExt = PathFindExtensionW(lpszPath);
173 if (!*szExt || dwWhich & 0x40)
175 size_t iChoose = 0;
176 int iLen = lstrlenW(lpszPath);
177 if (iLen > (MAX_PATH - 5))
178 return FALSE;
179 while ( (dwWhich & 0x1) && pszExts[iChoose][0] )
181 lstrcpyW(lpszPath + iLen, pszExts[iChoose]);
182 if (PathFileExistsW(lpszPath))
183 return TRUE;
184 iChoose++;
185 dwWhich >>= 1;
187 *(lpszPath + iLen) = (WCHAR)'\0';
188 return FALSE;
191 return PathFileExistsW(lpszPath);
194 /*************************************************************************
195 * @ [SHLWAPI.3]
197 * Determine if a file exists locally and is of an executable type.
199 * PARAMS
200 * lpszPath [I/O] File to search for
201 * dwWhich [I] Type of executable to search for
203 * RETURNS
204 * TRUE If the file was found. lpszPath contains the file name.
205 * FALSE Otherwise.
207 * NOTES
208 * lpszPath is modified in place and must be at least MAX_PATH in length.
209 * If the function returns FALSE, the path is modified to its original state.
210 * If the given path contains an extension or dwWhich is 0, executable
211 * extensions are not checked.
213 * Ordinals 3-6 are a classic case of MS exposing limited functionality to
214 * users (here through PathFindOnPathA()) and keeping advanced functionality for
215 * their own developers exclusive use. Monopoly, anyone?
217 BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)
219 BOOL bRet = FALSE;
221 TRACE("(%s,%ld)\n", debugstr_a(lpszPath), dwWhich);
223 if (lpszPath)
225 WCHAR szPath[MAX_PATH];
226 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
227 bRet = PathFileExistsDefExtW(szPath, dwWhich);
228 if (bRet)
229 WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
231 return bRet;
234 /*************************************************************************
235 * SHLWAPI_PathFindInOtherDirs
237 * Internal helper for SHLWAPI_PathFindOnPathExA/W.
239 static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich)
241 static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'};
242 static const WCHAR szPath[] = { 'P','A','T','H','\0'};
243 DWORD dwLenPATH;
244 LPCWSTR lpszCurr;
245 WCHAR *lpszPATH;
246 WCHAR buff[MAX_PATH];
248 TRACE("(%s,%08lx)\n", debugstr_w(lpszFile), dwWhich);
250 /* Try system directories */
251 GetSystemDirectoryW(buff, MAX_PATH);
252 if (!PathAppendW(buff, lpszFile))
253 return FALSE;
254 if (PathFileExistsDefExtW(buff, dwWhich))
256 lstrcpyW(lpszFile, buff);
257 return TRUE;
259 GetWindowsDirectoryW(buff, MAX_PATH);
260 if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile))
261 return FALSE;
262 if (PathFileExistsDefExtW(buff, dwWhich))
264 lstrcpyW(lpszFile, buff);
265 return TRUE;
267 GetWindowsDirectoryW(buff, MAX_PATH);
268 if (!PathAppendW(buff, lpszFile))
269 return FALSE;
270 if (PathFileExistsDefExtW(buff, dwWhich))
272 lstrcpyW(lpszFile, buff);
273 return TRUE;
275 /* Try dirs listed in %PATH% */
276 dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH);
278 if (!dwLenPATH || !(lpszPATH = malloc((dwLenPATH + 1) * sizeof (WCHAR))))
279 return FALSE;
281 GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1);
282 lpszCurr = lpszPATH;
283 while (lpszCurr)
285 LPCWSTR lpszEnd = lpszCurr;
286 LPWSTR pBuff = buff;
288 while (*lpszEnd == ' ')
289 lpszEnd++;
290 while (*lpszEnd && *lpszEnd != ';')
291 *pBuff++ = *lpszEnd++;
292 *pBuff = '\0';
294 if (*lpszEnd)
295 lpszCurr = lpszEnd + 1;
296 else
297 lpszCurr = NULL; /* Last Path, terminate after this */
299 if (!PathAppendW(buff, lpszFile))
301 free(lpszPATH);
302 return FALSE;
304 if (PathFileExistsDefExtW(buff, dwWhich))
306 lstrcpyW(lpszFile, buff);
307 free(lpszPATH);
308 return TRUE;
311 free(lpszPATH);
312 return FALSE;
315 /*************************************************************************
316 * @ [SHLWAPI.5]
318 * Search a range of paths for a specific type of executable.
320 * PARAMS
321 * lpszFile [I/O] File to search for
322 * lppszOtherDirs [I] Other directories to look in
323 * dwWhich [I] Type of executable to search for
325 * RETURNS
326 * Success: TRUE. The path to the executable is stored in lpszFile.
327 * Failure: FALSE. The path to the executable is unchanged.
329 BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich)
331 WCHAR szFile[MAX_PATH];
332 WCHAR buff[MAX_PATH];
334 TRACE("(%s,%p,%08lx)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich);
336 if (!lpszFile || !PathIsFileSpecA(lpszFile))
337 return FALSE;
339 MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH);
341 /* Search provided directories first */
342 if (lppszOtherDirs && *lppszOtherDirs)
344 WCHAR szOther[MAX_PATH];
345 LPCSTR *lpszOtherPath = lppszOtherDirs;
347 while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
349 MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH);
350 PathCombineW(buff, szOther, szFile);
351 if (PathFileExistsDefExtW(buff, dwWhich))
353 WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0);
354 return TRUE;
356 lpszOtherPath++;
359 /* Not found, try system and path dirs */
360 if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich))
362 WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0);
363 return TRUE;
365 return FALSE;
368 /*************************************************************************
369 * @ [SHLWAPI.6]
371 * Unicode version of PathFindOnPathExA.
373 BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich)
375 WCHAR buff[MAX_PATH];
377 TRACE("(%s,%p,%08lx)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich);
379 if (!lpszFile || !PathIsFileSpecW(lpszFile))
380 return FALSE;
382 /* Search provided directories first */
383 if (lppszOtherDirs && *lppszOtherDirs)
385 LPCWSTR *lpszOtherPath = lppszOtherDirs;
386 while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
388 PathCombineW(buff, *lpszOtherPath, lpszFile);
389 if (PathFileExistsDefExtW(buff, dwWhich))
391 lstrcpyW(lpszFile, buff);
392 return TRUE;
394 lpszOtherPath++;
397 /* Not found, try system and path dirs */
398 return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich);
401 /*************************************************************************
402 * PathFindOnPathA [SHLWAPI.@]
404 * Search a range of paths for an executable.
406 * PARAMS
407 * lpszFile [I/O] File to search for
408 * lppszOtherDirs [I] Other directories to look in
410 * RETURNS
411 * Success: TRUE. The path to the executable is stored in lpszFile.
412 * Failure: FALSE. The path to the executable is unchanged.
414 BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs)
416 TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs);
417 return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0);
420 /*************************************************************************
421 * PathFindOnPathW [SHLWAPI.@]
423 * See PathFindOnPathA.
425 BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs)
427 TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs);
428 return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0);
431 /*************************************************************************
432 * PathCompactPathExA [SHLWAPI.@]
434 * Compact a path into a given number of characters.
436 * PARAMS
437 * lpszDest [O] Destination for compacted path
438 * lpszPath [I] Source path
439 * cchMax [I] Maximum size of compacted path
440 * dwFlags [I] Reserved
442 * RETURNS
443 * Success: TRUE. The compacted path is written to lpszDest.
444 * Failure: FALSE. lpszPath is undefined.
446 * NOTES
447 * If cchMax is given as 0, lpszDest will still be NUL terminated.
449 * The Win32 version of this function contains a bug: When cchMax == 7,
450 * 8 bytes will be written to lpszDest. This bug is fixed in the Wine
451 * implementation.
453 * Some relative paths will be different when cchMax == 5 or 6. This occurs
454 * because Win32 will insert a "\" in lpszDest, even if one is
455 * not present in the original path.
457 BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath,
458 UINT cchMax, DWORD dwFlags)
460 BOOL bRet = FALSE;
462 TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags);
464 if (lpszPath && lpszDest)
466 WCHAR szPath[MAX_PATH];
467 WCHAR szDest[MAX_PATH];
469 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
470 szDest[0] = '\0';
471 bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags);
472 WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0);
474 return bRet;
477 /*************************************************************************
478 * PathCompactPathExW [SHLWAPI.@]
480 * See PathCompactPathExA.
482 BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath,
483 UINT cchMax, DWORD dwFlags)
485 static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
486 LPCWSTR lpszFile;
487 DWORD dwLen, dwFileLen = 0;
489 TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags);
491 if (!lpszPath)
492 return FALSE;
494 if (!lpszDest)
496 WARN("Invalid lpszDest would crash under Win32!\n");
497 return FALSE;
500 *lpszDest = '\0';
502 if (cchMax < 2)
503 return TRUE;
505 dwLen = lstrlenW(lpszPath) + 1;
507 if (dwLen < cchMax)
509 /* Don't need to compact */
510 memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
511 return TRUE;
514 /* Path must be compacted to fit into lpszDest */
515 lpszFile = PathFindFileNameW(lpszPath);
516 dwFileLen = lpszPath + dwLen - lpszFile;
518 if (dwFileLen == dwLen)
520 /* No root in psth */
521 if (cchMax <= 4)
523 while (--cchMax > 0) /* No room left for anything but ellipses */
524 *lpszDest++ = '.';
525 *lpszDest = '\0';
526 return TRUE;
528 /* Compact the file name with ellipses at the end */
529 cchMax -= 4;
530 memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
531 lstrcpyW(lpszDest + cchMax, szEllipses);
532 return TRUE;
534 /* We have a root in the path */
535 lpszFile--; /* Start compacted filename with the path separator */
536 dwFileLen++;
538 if (dwFileLen + 3 > cchMax)
540 /* Compact the file name */
541 if (cchMax <= 4)
543 while (--cchMax > 0) /* No room left for anything but ellipses */
544 *lpszDest++ = '.';
545 *lpszDest = '\0';
546 return TRUE;
548 lstrcpyW(lpszDest, szEllipses);
549 lpszDest += 3;
550 cchMax -= 4;
551 *lpszDest++ = *lpszFile++;
552 if (cchMax <= 4)
554 while (--cchMax > 0) /* No room left for anything but ellipses */
555 *lpszDest++ = '.';
556 *lpszDest = '\0';
557 return TRUE;
559 cchMax -= 4;
560 memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
561 lstrcpyW(lpszDest + cchMax, szEllipses);
562 return TRUE;
565 /* Only the root needs to be Compacted */
566 dwLen = cchMax - dwFileLen - 3;
567 memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
568 lstrcpyW(lpszDest + dwLen, szEllipses);
569 lstrcpyW(lpszDest + dwLen + 3, lpszFile);
570 return TRUE;
573 /*************************************************************************
574 * PathIsDirectoryA [SHLWAPI.@]
576 * Determine if a path is a valid directory
578 * PARAMS
579 * lpszPath [I] Path to check.
581 * RETURNS
582 * FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
583 * FALSE if lpszPath is invalid or not a directory.
585 * NOTES
586 * Although this function is prototyped as returning a BOOL, it returns
587 * FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
589 *| if (PathIsDirectoryA("c:\\windows\\") == TRUE)
590 *| ...
592 * will always fail.
594 BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath)
596 DWORD dwAttr;
598 TRACE("(%s)\n", debugstr_a(lpszPath));
600 if (!lpszPath || PathIsUNCServerA(lpszPath))
601 return FALSE;
603 if (PathIsUNCServerShareA(lpszPath))
605 FIXME("UNC Server Share not yet supported - FAILING\n");
606 return FALSE;
609 if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES)
610 return FALSE;
611 return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
614 /*************************************************************************
615 * PathIsDirectoryW [SHLWAPI.@]
617 * See PathIsDirectoryA.
619 BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
621 DWORD dwAttr;
623 TRACE("(%s)\n", debugstr_w(lpszPath));
625 if (!lpszPath || PathIsUNCServerW(lpszPath))
626 return FALSE;
628 if (PathIsUNCServerShareW(lpszPath))
630 FIXME("UNC Server Share not yet supported - FAILING\n");
631 return FALSE;
634 if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
635 return FALSE;
636 return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
639 /*************************************************************************
640 * PathFileExistsAndAttributesA [SHLWAPI.445]
642 * Determine if a file exists.
644 * PARAMS
645 * lpszPath [I] Path to check
646 * dwAttr [O] attributes of file
648 * RETURNS
649 * TRUE If the file exists and is readable
650 * FALSE Otherwise
652 BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr)
654 UINT iPrevErrMode;
655 DWORD dwVal = 0;
657 TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr);
659 if (dwAttr)
660 *dwAttr = INVALID_FILE_ATTRIBUTES;
662 if (!lpszPath)
663 return FALSE;
665 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
666 dwVal = GetFileAttributesA(lpszPath);
667 SetErrorMode(iPrevErrMode);
668 if (dwAttr)
669 *dwAttr = dwVal;
670 return (dwVal != INVALID_FILE_ATTRIBUTES);
673 /*************************************************************************
674 * PathFileExistsAndAttributesW [SHLWAPI.446]
676 * See PathFileExistsA.
678 BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr)
680 UINT iPrevErrMode;
681 DWORD dwVal;
683 TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr);
685 if (!lpszPath)
686 return FALSE;
688 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
689 dwVal = GetFileAttributesW(lpszPath);
690 SetErrorMode(iPrevErrMode);
691 if (dwAttr)
692 *dwAttr = dwVal;
693 return (dwVal != INVALID_FILE_ATTRIBUTES);
696 /*************************************************************************
697 * PathIsContentTypeA [SHLWAPI.@]
699 * Determine if a file is of a given registered content type.
701 * PARAMS
702 * lpszPath [I] File to check
703 * lpszContentType [I] Content type to check for
705 * RETURNS
706 * TRUE If lpszPath is a given registered content type,
707 * FALSE Otherwise.
709 * NOTES
710 * This function looks up the registered content type for lpszPath. If
711 * a content type is registered, it is compared (case insensitively) to
712 * lpszContentType. Only if this matches does the function succeed.
714 BOOL WINAPI PathIsContentTypeA(LPCSTR path, LPCSTR content_type)
716 char buf[MAX_PATH];
717 DWORD size = sizeof(buf);
718 LPCSTR ext;
720 TRACE("(%s,%s)\n", debugstr_a(path), debugstr_a(content_type));
722 if(!path) return FALSE;
723 if(!(ext = PathFindExtensionA(path)) || !*ext) return FALSE;
724 if(SHGetValueA(HKEY_CLASSES_ROOT, ext, "Content Type", NULL, buf, &size)) return FALSE;
725 return !lstrcmpiA(content_type, buf);
728 /*************************************************************************
729 * PathIsContentTypeW [SHLWAPI.@]
731 * See PathIsContentTypeA.
733 BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType)
735 static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
736 LPCWSTR szExt;
737 DWORD dwDummy;
738 WCHAR szBuff[MAX_PATH];
740 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType));
742 if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt &&
743 !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType,
744 REG_NONE, szBuff, &dwDummy) &&
745 !wcsicmp(lpszContentType, szBuff))
747 return TRUE;
749 return FALSE;
752 /*************************************************************************
753 * PathIsSystemFolderA [SHLWAPI.@]
755 * Determine if a path or file attributes are a system folder.
757 * PARAMS
758 * lpszPath [I] Path to check.
759 * dwAttrib [I] Attributes to check, if lpszPath is NULL.
761 * RETURNS
762 * TRUE If lpszPath or dwAttrib are a system folder.
763 * FALSE If GetFileAttributesA() fails or neither parameter is a system folder.
765 BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib)
767 TRACE("(%s,0x%08lx)\n", debugstr_a(lpszPath), dwAttrib);
769 if (lpszPath && *lpszPath)
770 dwAttrib = GetFileAttributesA(lpszPath);
772 if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
773 !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
774 return FALSE;
775 return TRUE;
778 /*************************************************************************
779 * PathIsSystemFolderW [SHLWAPI.@]
781 * See PathIsSystemFolderA.
783 BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib)
785 TRACE("(%s,0x%08lx)\n", debugstr_w(lpszPath), dwAttrib);
787 if (lpszPath && *lpszPath)
788 dwAttrib = GetFileAttributesW(lpszPath);
790 if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
791 !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
792 return FALSE;
793 return TRUE;
796 /*************************************************************************
797 * PathMakePrettyA [SHLWAPI.@]
799 * Convert an uppercase DOS filename into lowercase.
801 * PARAMS
802 * lpszPath [I/O] Path to convert.
804 * RETURNS
805 * TRUE If the path was an uppercase DOS path and was converted,
806 * FALSE Otherwise.
808 BOOL WINAPI PathMakePrettyA(LPSTR lpszPath)
810 LPSTR pszIter = lpszPath;
812 TRACE("(%s)\n", debugstr_a(lpszPath));
814 if (!pszIter)
815 return FALSE;
817 if (*pszIter)
821 if (islower(*pszIter) || IsDBCSLeadByte(*pszIter))
822 return FALSE; /* Not DOS path */
823 pszIter++;
824 } while (*pszIter);
825 pszIter = lpszPath + 1;
826 while (*pszIter)
828 *pszIter = tolower(*pszIter);
829 pszIter++;
832 return TRUE;
835 /*************************************************************************
836 * PathMakePrettyW [SHLWAPI.@]
838 * See PathMakePrettyA.
840 BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath)
842 LPWSTR pszIter = lpszPath;
844 TRACE("(%s)\n", debugstr_w(lpszPath));
846 if (!pszIter)
847 return FALSE;
849 if (*pszIter)
853 if (iswlower(*pszIter))
854 return FALSE; /* Not DOS path */
855 pszIter++;
856 } while (*pszIter);
857 pszIter = lpszPath + 1;
858 while (*pszIter)
860 *pszIter = towlower(*pszIter);
861 pszIter++;
864 return TRUE;
867 /*************************************************************************
868 * PathCompactPathA [SHLWAPI.@]
870 * Make a path fit into a given width when printed to a DC.
872 * PARAMS
873 * hDc [I] Destination DC
874 * lpszPath [I/O] Path to be printed to hDc
875 * dx [I] Desired width
877 * RETURNS
878 * TRUE If the path was modified/went well.
879 * FALSE Otherwise.
881 BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx)
883 BOOL bRet = FALSE;
885 TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx);
887 if (lpszPath)
889 WCHAR szPath[MAX_PATH];
890 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
891 bRet = PathCompactPathW(hDC, szPath, dx);
892 WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
894 return bRet;
897 /*************************************************************************
898 * PathCompactPathW [SHLWAPI.@]
900 * See PathCompactPathA.
902 BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx)
904 static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
905 BOOL bRet = TRUE;
906 HDC hdc = 0;
907 WCHAR buff[MAX_PATH];
908 SIZE size;
909 DWORD dwLen;
911 TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx);
913 if (!lpszPath)
914 return FALSE;
916 if (!hDC)
917 hdc = hDC = GetDC(0);
919 /* Get the length of the whole path */
920 dwLen = lstrlenW(lpszPath);
921 GetTextExtentPointW(hDC, lpszPath, dwLen, &size);
923 if ((UINT)size.cx > dx)
925 /* Path too big, must reduce it */
926 LPWSTR sFile;
927 DWORD dwEllipsesLen = 0, dwPathLen = 0;
929 sFile = PathFindFileNameW(lpszPath);
930 if (sFile != lpszPath) sFile--;
932 /* Get the size of ellipses */
933 GetTextExtentPointW(hDC, szEllipses, 3, &size);
934 dwEllipsesLen = size.cx;
935 /* Get the size of the file name */
936 GetTextExtentPointW(hDC, sFile, lstrlenW(sFile), &size);
937 dwPathLen = size.cx;
939 if (sFile != lpszPath)
941 LPWSTR sPath = sFile;
942 BOOL bEllipses = FALSE;
944 /* The path includes a file name. Include as much of the path prior to
945 * the file name as possible, allowing for the ellipses, e.g:
946 * c:\some very long path\filename ==> c:\some v...\filename
948 lstrcpynW(buff, sFile, MAX_PATH);
952 DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen;
954 GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size);
955 dwTotalLen += size.cx;
956 if (dwTotalLen <= dx)
957 break;
958 sPath--;
959 if (!bEllipses)
961 bEllipses = TRUE;
962 sPath -= 2;
964 } while (sPath > lpszPath);
966 if (sPath > lpszPath)
968 if (bEllipses)
970 lstrcpyW(sPath, szEllipses);
971 lstrcpyW(sPath+3, buff);
973 bRet = TRUE;
974 goto end;
976 lstrcpyW(lpszPath, szEllipses);
977 lstrcpyW(lpszPath+3, buff);
978 bRet = FALSE;
979 goto end;
982 /* Trim the path by adding ellipses to the end, e.g:
983 * A very long file name.txt ==> A very...
985 dwLen = lstrlenW(lpszPath);
987 if (dwLen > MAX_PATH - 3)
988 dwLen = MAX_PATH - 3;
989 lstrcpynW(buff, sFile, dwLen);
991 do {
992 dwLen--;
993 GetTextExtentPointW(hDC, buff, dwLen, &size);
994 } while (dwLen && size.cx + dwEllipsesLen > dx);
996 if (!dwLen)
998 DWORD dwWritten = 0;
1000 dwEllipsesLen /= 3; /* Size of a single '.' */
1002 /* Write as much of the Ellipses string as possible */
1003 while (dwWritten + dwEllipsesLen < dx && dwLen < 3)
1005 *lpszPath++ = '.';
1006 dwWritten += dwEllipsesLen;
1007 dwLen++;
1009 *lpszPath = '\0';
1010 bRet = FALSE;
1012 else
1014 lstrcpyW(buff + dwLen, szEllipses);
1015 lstrcpyW(lpszPath, buff);
1019 end:
1020 if (hdc)
1021 ReleaseDC(0, hdc);
1023 return bRet;
1026 /*************************************************************************
1027 * SHLWAPI_UseSystemForSystemFolders
1029 * Internal helper for PathMakeSystemFolderW.
1031 static BOOL SHLWAPI_UseSystemForSystemFolders(void)
1033 static BOOL bCheckedReg = FALSE;
1034 static BOOL bUseSystemForSystemFolders = FALSE;
1036 if (!bCheckedReg)
1038 bCheckedReg = TRUE;
1040 /* Key tells Win what file attributes to use on system folders */
1041 if (SHGetValueA(HKEY_LOCAL_MACHINE,
1042 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
1043 "UseSystemForSystemFolders", 0, 0, 0))
1044 bUseSystemForSystemFolders = TRUE;
1046 return bUseSystemForSystemFolders;
1049 /*************************************************************************
1050 * PathMakeSystemFolderA [SHLWAPI.@]
1052 * Set system folder attribute for a path.
1054 * PARAMS
1055 * lpszPath [I] The path to turn into a system folder
1057 * RETURNS
1058 * TRUE If the path was changed to/already was a system folder
1059 * FALSE If the path is invalid or SetFileAttributesA() fails
1061 BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath)
1063 BOOL bRet = FALSE;
1065 TRACE("(%s)\n", debugstr_a(lpszPath));
1067 if (lpszPath && *lpszPath)
1069 WCHAR szPath[MAX_PATH];
1070 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1071 bRet = PathMakeSystemFolderW(szPath);
1073 return bRet;
1076 /*************************************************************************
1077 * PathMakeSystemFolderW [SHLWAPI.@]
1079 * See PathMakeSystemFolderA.
1081 BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath)
1083 DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr;
1084 WCHAR buff[MAX_PATH];
1086 TRACE("(%s)\n", debugstr_w(lpszPath));
1088 if (!lpszPath || !*lpszPath)
1089 return FALSE;
1091 /* If the directory is already a system directory, don't do anything */
1092 GetSystemDirectoryW(buff, MAX_PATH);
1093 if (!wcscmp(buff, lpszPath))
1094 return TRUE;
1096 GetWindowsDirectoryW(buff, MAX_PATH);
1097 if (!wcscmp(buff, lpszPath))
1098 return TRUE;
1100 /* "UseSystemForSystemFolders" Tells Win what attributes to use */
1101 if (SHLWAPI_UseSystemForSystemFolders())
1102 dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM;
1104 if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
1105 return FALSE;
1107 /* Change file attributes to system attributes */
1108 dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
1109 return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr);
1112 /*************************************************************************
1113 * PathUnmakeSystemFolderA [SHLWAPI.@]
1115 * Remove the system folder attributes from a path.
1117 * PARAMS
1118 * lpszPath [I] The path to remove attributes from
1120 * RETURNS
1121 * Success: TRUE.
1122 * Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling
1123 * SetFileAttributesA() fails.
1125 BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath)
1127 DWORD dwAttr;
1129 TRACE("(%s)\n", debugstr_a(lpszPath));
1131 if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
1132 !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
1133 return FALSE;
1135 dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
1136 return SetFileAttributesA(lpszPath, dwAttr);
1139 /*************************************************************************
1140 * PathUnmakeSystemFolderW [SHLWAPI.@]
1142 * See PathUnmakeSystemFolderA.
1144 BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath)
1146 DWORD dwAttr;
1148 TRACE("(%s)\n", debugstr_w(lpszPath));
1150 if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
1151 !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
1152 return FALSE;
1154 dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
1155 return SetFileAttributesW(lpszPath, dwAttr);
1159 /*************************************************************************
1160 * PathSetDlgItemPathA [SHLWAPI.@]
1162 * Set the text of a dialog item to a path, shrinking the path to fit
1163 * if it is too big for the item.
1165 * PARAMS
1166 * hDlg [I] Dialog handle
1167 * id [I] ID of item in the dialog
1168 * lpszPath [I] Path to set as the items text
1170 * RETURNS
1171 * Nothing.
1173 * NOTES
1174 * If lpszPath is NULL, a blank string ("") is set (i.e. The previous
1175 * window text is erased).
1177 VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath)
1179 WCHAR szPath[MAX_PATH];
1181 TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath));
1183 if (lpszPath)
1184 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1185 else
1186 szPath[0] = '\0';
1187 PathSetDlgItemPathW(hDlg, id, szPath);
1190 /*************************************************************************
1191 * PathSetDlgItemPathW [SHLWAPI.@]
1193 * See PathSetDlgItemPathA.
1195 VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath)
1197 WCHAR path[MAX_PATH + 1];
1198 HWND hwItem;
1199 RECT rect;
1200 HDC hdc;
1201 HGDIOBJ hPrevObj;
1203 TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath));
1205 if (!(hwItem = GetDlgItem(hDlg, id)))
1206 return;
1208 if (lpszPath)
1209 lstrcpynW(path, lpszPath, ARRAY_SIZE(path));
1210 else
1211 path[0] = '\0';
1213 GetClientRect(hwItem, &rect);
1214 hdc = GetDC(hDlg);
1215 hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0));
1217 if (hPrevObj)
1219 PathCompactPathW(hdc, path, rect.right);
1220 SelectObject(hdc, hPrevObj);
1223 ReleaseDC(hDlg, hdc);
1224 SetWindowTextW(hwItem, path);
1227 /*************************************************************************
1228 * PathIsNetworkPathA [SHLWAPI.@]
1230 * Determine if the given path is a network path.
1232 * PARAMS
1233 * lpszPath [I] Path to check
1235 * RETURNS
1236 * TRUE If lpszPath is a UNC share or mapped network drive, or
1237 * FALSE If lpszPath is a local drive or cannot be determined
1239 BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath)
1241 int dwDriveNum;
1243 TRACE("(%s)\n",debugstr_a(lpszPath));
1245 if (!lpszPath)
1246 return FALSE;
1247 if (*lpszPath == '\\' && lpszPath[1] == '\\')
1248 return TRUE;
1249 dwDriveNum = PathGetDriveNumberA(lpszPath);
1250 if (dwDriveNum == -1)
1251 return FALSE;
1252 GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
1253 return pIsNetDrive(dwDriveNum);
1256 /*************************************************************************
1257 * PathIsNetworkPathW [SHLWAPI.@]
1259 * See PathIsNetworkPathA.
1261 BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath)
1263 int dwDriveNum;
1265 TRACE("(%s)\n", debugstr_w(lpszPath));
1267 if (!lpszPath)
1268 return FALSE;
1269 if (*lpszPath == '\\' && lpszPath[1] == '\\')
1270 return TRUE;
1271 dwDriveNum = PathGetDriveNumberW(lpszPath);
1272 if (dwDriveNum == -1)
1273 return FALSE;
1274 GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
1275 return pIsNetDrive(dwDriveNum);
1278 /*************************************************************************
1279 * PathIsDirectoryEmptyA [SHLWAPI.@]
1281 * Determine if a given directory is empty.
1283 * PARAMS
1284 * lpszPath [I] Directory to check
1286 * RETURNS
1287 * TRUE If the directory exists and contains no files,
1288 * FALSE Otherwise
1290 BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath)
1292 BOOL bRet = FALSE;
1294 TRACE("(%s)\n",debugstr_a(lpszPath));
1296 if (lpszPath)
1298 WCHAR szPath[MAX_PATH];
1299 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1300 bRet = PathIsDirectoryEmptyW(szPath);
1302 return bRet;
1305 /*************************************************************************
1306 * PathIsDirectoryEmptyW [SHLWAPI.@]
1308 * See PathIsDirectoryEmptyA.
1310 BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath)
1312 static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' };
1313 WCHAR szSearch[MAX_PATH];
1314 DWORD dwLen;
1315 HANDLE hfind;
1316 BOOL retVal = TRUE;
1317 WIN32_FIND_DATAW find_data;
1319 TRACE("(%s)\n",debugstr_w(lpszPath));
1321 if (!lpszPath || !PathIsDirectoryW(lpszPath))
1322 return FALSE;
1324 lstrcpynW(szSearch, lpszPath, MAX_PATH);
1325 PathAddBackslashW(szSearch);
1326 dwLen = lstrlenW(szSearch);
1327 if (dwLen > MAX_PATH - 4)
1328 return FALSE;
1330 lstrcpyW(szSearch + dwLen, szAllFiles);
1331 hfind = FindFirstFileW(szSearch, &find_data);
1332 if (hfind == INVALID_HANDLE_VALUE)
1333 return FALSE;
1337 if (find_data.cFileName[0] == '.')
1339 if (find_data.cFileName[1] == '\0') continue;
1340 if (find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0') continue;
1343 retVal = FALSE;
1344 break;
1346 while (FindNextFileW(hfind, &find_data));
1348 FindClose(hfind);
1349 return retVal;
1353 /*************************************************************************
1354 * PathFindSuffixArrayA [SHLWAPI.@]
1356 * Find a suffix string in an array of suffix strings
1358 * PARAMS
1359 * lpszSuffix [I] Suffix string to search for
1360 * lppszArray [I] Array of suffix strings to search
1361 * dwCount [I] Number of elements in lppszArray
1363 * RETURNS
1364 * Success: The index of the position of lpszSuffix in lppszArray
1365 * Failure: 0, if any parameters are invalid or lpszSuffix is not found
1367 * NOTES
1368 * The search is case sensitive.
1369 * The match is made against the end of the suffix string, so for example:
1370 * lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not.
1372 LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount)
1374 size_t dwLen;
1375 int dwRet = 0;
1377 TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount);
1379 if (lpszSuffix && lppszArray && dwCount > 0)
1381 dwLen = strlen(lpszSuffix);
1383 while (dwRet < dwCount)
1385 size_t dwCompareLen = strlen(*lppszArray);
1386 if (dwCompareLen < dwLen)
1388 if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
1389 return *lppszArray; /* Found */
1391 dwRet++;
1392 lppszArray++;
1395 return NULL;
1398 /*************************************************************************
1399 * PathFindSuffixArrayW [SHLWAPI.@]
1401 * See PathFindSuffixArrayA.
1403 LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount)
1405 size_t dwLen;
1406 int dwRet = 0;
1408 TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount);
1410 if (lpszSuffix && lppszArray && dwCount > 0)
1412 dwLen = lstrlenW(lpszSuffix);
1414 while (dwRet < dwCount)
1416 size_t dwCompareLen = lstrlenW(*lppszArray);
1417 if (dwCompareLen < dwLen)
1419 if (!wcscmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
1420 return *lppszArray; /* Found */
1422 dwRet++;
1423 lppszArray++;
1426 return NULL;
1429 /*************************************************************************
1430 * PathUndecorateA [SHLWAPI.@]
1432 * Undecorate a file path
1434 * PARAMS
1435 * lpszPath [I/O] Path to remove any decoration from
1437 * RETURNS
1438 * Nothing
1440 * NOTES
1441 * A decorations form is "path[n].ext" where "n" is an optional decimal number.
1443 void WINAPI PathUndecorateA(char *path)
1445 char *ext, *skip;
1447 TRACE("(%s)\n", debugstr_a(path));
1449 if (!path) return;
1451 ext = PathFindExtensionA(path);
1452 if (ext == path || ext[-1] != ']') return;
1454 skip = ext - 2;
1455 while (skip > path && '0' <= *skip && *skip <= '9')
1456 skip--;
1458 if (skip > path && *skip == '[' && skip[-1] != '\\')
1459 memmove(skip, ext, strlen(ext) + 1);
1462 /*************************************************************************
1463 * PathUndecorateW [SHLWAPI.@]
1465 * See PathUndecorateA.
1467 void WINAPI PathUndecorateW(WCHAR *path)
1469 WCHAR *ext, *skip;
1471 TRACE("(%s)\n", debugstr_w(path));
1473 if (!path) return;
1475 ext = PathFindExtensionW(path);
1476 if (ext == path || ext[-1] != ']') return;
1478 skip = ext - 2;
1479 while (skip > path && '0' <= *skip && *skip <= '9')
1480 skip--;
1482 if (skip > path && *skip == '[' && skip[-1] != '\\')
1483 memmove(skip, ext, (wcslen(ext) + 1) * sizeof(WCHAR));
1486 /*************************************************************************
1487 * @ [SHLWAPI.440]
1489 * Find localised or default web content in "%WINDOWS%\web\".
1491 * PARAMS
1492 * lpszFile [I] File name containing content to look for
1493 * lpszPath [O] Buffer to contain the full path to the file
1494 * dwPathLen [I] Length of lpszPath
1496 * RETURNS
1497 * Success: S_OK. lpszPath contains the full path to the content.
1498 * Failure: E_FAIL. The content does not exist or lpszPath is too short.
1500 HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen)
1502 WCHAR szFile[MAX_PATH], szPath[MAX_PATH];
1503 HRESULT hRet;
1505 TRACE("(%s,%p,%ld)\n", lpszFile, lpszPath, dwPathLen);
1507 MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH);
1508 szPath[0] = '\0';
1509 hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen);
1510 WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0);
1511 return hRet;
1514 /*************************************************************************
1515 * @ [SHLWAPI.441]
1517 * Unicode version of SHGetWebFolderFilePathA.
1519 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen)
1521 static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'};
1522 static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'};
1523 DWORD dwLen, dwFileLen;
1524 LANGID lidSystem, lidUser;
1526 TRACE("(%s,%p,%ld)\n", debugstr_w(lpszFile), lpszPath, dwPathLen);
1528 /* Get base directory for web content */
1529 dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen);
1530 if (dwLen > 0 && lpszPath[dwLen-1] == '\\')
1531 dwLen--;
1533 dwFileLen = lstrlenW(lpszFile);
1535 if (dwLen + dwFileLen + ARRAY_SIZE(szWeb) >= dwPathLen)
1536 return E_FAIL; /* lpszPath too short */
1538 lstrcpyW(lpszPath+dwLen, szWeb);
1539 dwLen += ARRAY_SIZE(szWeb);
1540 dwPathLen = dwPathLen - dwLen; /* Remaining space */
1542 lidSystem = GetSystemDefaultUILanguage();
1543 lidUser = GetUserDefaultUILanguage();
1545 if (lidSystem != lidUser)
1547 if (dwFileLen + ARRAY_SIZE(szWebMui) < dwPathLen)
1549 /* Use localised content in the users UI language if present */
1550 wsprintfW(lpszPath + dwLen, szWebMui, lidUser);
1551 lstrcpyW(lpszPath + dwLen + ARRAY_SIZE(szWebMui), lpszFile);
1552 if (PathFileExistsW(lpszPath))
1553 return S_OK;
1557 /* Fall back to OS default installed content */
1558 lstrcpyW(lpszPath + dwLen, lpszFile);
1559 if (PathFileExistsW(lpszPath))
1560 return S_OK;
1561 return E_FAIL;