xref: /reactos/dll/win32/shlwapi/path.c (revision 1fbf09f5)
1 /*
2  * Path Functions
3  *
4  * Copyright 1999, 2000 Juergen Schmied
5  * Copyright 2001, 2002 Jon Griffiths
6  *
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.
11  *
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.
16  *
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
20  */
21 
22 #include "config.h"
23 #include "wine/port.h"
24 
25 #include <stdarg.h>
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #include "wine/unicode.h"
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "winreg.h"
35 #include "winternl.h"
36 #define NO_SHLWAPI_STREAM
37 #include "shlwapi.h"
38 #include "wine/debug.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(shell);
41 
42 #ifdef __REACTOS__
43 
44 #include <shlobj.h>
45 #include <shlwapi_undoc.h>
46 
47 int WINAPI IsNetDrive(int drive);
48 
49 #else
50 
51 /* Get a function pointer from a DLL handle */
52 #define GET_FUNC(func, module, name, fail) \
53   do { \
54     if (!func) { \
55       if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
56       func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
57       if (!func) return fail; \
58     } \
59   } while (0)
60 
61 /* DLL handles for late bound calls */
62 static HMODULE SHLWAPI_hshell32;
63 
64 /* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
65 typedef BOOL (WINAPI *fnpIsNetDrive)(int);
66 static  fnpIsNetDrive pIsNetDrive;
67 
68 #endif /* __REACTOS__ */
69 
70 
71 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD);
72 
heap_strdupAtoW(LPCSTR str)73 static inline WCHAR* heap_strdupAtoW(LPCSTR str)
74 {
75     WCHAR *ret = NULL;
76 
77     if (str)
78     {
79         DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
80         ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
81         if (ret)
82             MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
83     }
84 
85     return ret;
86 }
87 
88 /*************************************************************************
89  * PathAppendA    [SHLWAPI.@]
90  *
91  * Append one path to another.
92  *
93  * PARAMS
94  *  lpszPath   [I/O] Initial part of path, and destination for output
95  *  lpszAppend [I]   Path to append
96  *
97  * RETURNS
98  *  Success: TRUE. lpszPath contains the newly created path.
99  *  Failure: FALSE, if either path is NULL, or PathCombineA() fails.
100  *
101  * NOTES
102  *  lpszAppend must contain at least one backslash ('\') if not NULL.
103  *  Because PathCombineA() is used to join the paths, the resulting
104  *  path is also canonicalized.
105  */
PathAppendA(LPSTR lpszPath,LPCSTR lpszAppend)106 BOOL WINAPI PathAppendA (LPSTR lpszPath, LPCSTR lpszAppend)
107 {
108   TRACE("(%s,%s)\n",debugstr_a(lpszPath), debugstr_a(lpszAppend));
109 
110   if (lpszPath && lpszAppend)
111   {
112     if (!PathIsUNCA(lpszAppend))
113       while (*lpszAppend == '\\')
114         lpszAppend++;
115     if (PathCombineA(lpszPath, lpszPath, lpszAppend))
116       return TRUE;
117   }
118   return FALSE;
119 }
120 
121 /*************************************************************************
122  * PathAppendW    [SHLWAPI.@]
123  *
124  * See PathAppendA.
125  */
PathAppendW(LPWSTR lpszPath,LPCWSTR lpszAppend)126 BOOL WINAPI PathAppendW(LPWSTR lpszPath, LPCWSTR lpszAppend)
127 {
128   TRACE("(%s,%s)\n",debugstr_w(lpszPath), debugstr_w(lpszAppend));
129 
130   if (lpszPath && lpszAppend)
131   {
132     if (!PathIsUNCW(lpszAppend))
133       while (*lpszAppend == '\\')
134         lpszAppend++;
135     if (PathCombineW(lpszPath, lpszPath, lpszAppend))
136       return TRUE;
137   }
138   return FALSE;
139 }
140 
141 /*************************************************************************
142  * PathCombineA		[SHLWAPI.@]
143  *
144  * Combine two paths together.
145  *
146  * PARAMS
147  *  lpszDest [O] Destination for combined path
148  *  lpszDir  [I] Directory path
149  *  lpszFile [I] File path
150  *
151  * RETURNS
152  *  Success: The output path
153  *  Failure: NULL, if inputs are invalid.
154  *
155  * NOTES
156  *  lpszDest should be at least MAX_PATH in size, and may point to the same
157  *  memory location as lpszDir. The combined path is canonicalised.
158  */
PathCombineA(LPSTR lpszDest,LPCSTR lpszDir,LPCSTR lpszFile)159 LPSTR WINAPI PathCombineA(LPSTR lpszDest, LPCSTR lpszDir, LPCSTR lpszFile)
160 {
161   WCHAR szDest[MAX_PATH];
162   WCHAR szDir[MAX_PATH];
163   WCHAR szFile[MAX_PATH];
164   TRACE("(%p,%s,%s)\n", lpszDest, debugstr_a(lpszDir), debugstr_a(lpszFile));
165 
166   /* Invalid parameters */
167   if (!lpszDest)
168     return NULL;
169   if (!lpszDir && !lpszFile)
170     goto fail;
171 
172   if (lpszDir)
173     if (!MultiByteToWideChar(CP_ACP,0,lpszDir,-1,szDir,MAX_PATH))
174       goto fail;
175 
176   if (lpszFile)
177     if (!MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH))
178       goto fail;
179 
180   if (PathCombineW(szDest, lpszDir ? szDir : NULL, lpszFile ? szFile : NULL))
181     if (WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0))
182       return lpszDest;
183 
184 fail:
185   lpszDest[0] = 0;
186   return NULL;
187 }
188 
189 /*************************************************************************
190  * PathCombineW		 [SHLWAPI.@]
191  *
192  * See PathCombineA.
193  */
PathCombineW(LPWSTR lpszDest,LPCWSTR lpszDir,LPCWSTR lpszFile)194 LPWSTR WINAPI PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
195 {
196   WCHAR szTemp[MAX_PATH];
197   BOOL bUseBoth = FALSE, bStrip = FALSE;
198 
199   TRACE("(%p,%s,%s)\n", lpszDest, debugstr_w(lpszDir), debugstr_w(lpszFile));
200 
201   /* Invalid parameters */
202   if (!lpszDest)
203     return NULL;
204   if (!lpszDir && !lpszFile)
205   {
206     lpszDest[0] = 0;
207     return NULL;
208   }
209 
210   if ((!lpszFile || !*lpszFile) && lpszDir)
211   {
212     /* Use dir only */
213     lstrcpynW(szTemp, lpszDir, MAX_PATH);
214   }
215   else if (!lpszDir || !*lpszDir || !PathIsRelativeW(lpszFile))
216   {
217     if (!lpszDir || !*lpszDir || *lpszFile != '\\' || PathIsUNCW(lpszFile))
218     {
219       /* Use file only */
220       lstrcpynW(szTemp, lpszFile, MAX_PATH);
221     }
222     else
223     {
224       bUseBoth = TRUE;
225       bStrip = TRUE;
226     }
227   }
228   else
229     bUseBoth = TRUE;
230 
231   if (bUseBoth)
232   {
233     lstrcpynW(szTemp, lpszDir, MAX_PATH);
234     if (bStrip)
235     {
236       PathStripToRootW(szTemp);
237       lpszFile++; /* Skip '\' */
238     }
239     if (!PathAddBackslashW(szTemp) || strlenW(szTemp) + strlenW(lpszFile) >= MAX_PATH)
240     {
241       lpszDest[0] = 0;
242       return NULL;
243     }
244     strcatW(szTemp, lpszFile);
245   }
246 
247   PathCanonicalizeW(lpszDest, szTemp);
248   return lpszDest;
249 }
250 
251 /*************************************************************************
252  * PathAddBackslashA	[SHLWAPI.@]
253  *
254  * Append a backslash ('\') to a path if one doesn't exist.
255  *
256  * PARAMS
257  *  lpszPath [I/O] The path to append a backslash to.
258  *
259  * RETURNS
260  *  Success: The position of the last backslash in the path.
261  *  Failure: NULL, if lpszPath is NULL or the path is too large.
262  */
PathAddBackslashA(LPSTR lpszPath)263 LPSTR WINAPI PathAddBackslashA(LPSTR lpszPath)
264 {
265   size_t iLen;
266   LPSTR prev = lpszPath;
267 
268   TRACE("(%s)\n",debugstr_a(lpszPath));
269 
270   if (!lpszPath || (iLen = strlen(lpszPath)) >= MAX_PATH)
271     return NULL;
272 
273   if (iLen)
274   {
275     do {
276       lpszPath = CharNextA(prev);
277       if (*lpszPath)
278         prev = lpszPath;
279     } while (*lpszPath);
280     if (*prev != '\\')
281     {
282       *lpszPath++ = '\\';
283       *lpszPath = '\0';
284     }
285   }
286   return lpszPath;
287 }
288 
289 /*************************************************************************
290  * PathAddBackslashW  [SHLWAPI.@]
291  *
292  * See PathAddBackslashA.
293  */
PathAddBackslashW(LPWSTR lpszPath)294 LPWSTR WINAPI PathAddBackslashW( LPWSTR lpszPath )
295 {
296   size_t iLen;
297 
298   TRACE("(%s)\n",debugstr_w(lpszPath));
299 
300   if (!lpszPath || (iLen = strlenW(lpszPath)) >= MAX_PATH)
301     return NULL;
302 
303   if (iLen)
304   {
305     lpszPath += iLen;
306     if (lpszPath[-1] != '\\')
307     {
308       *lpszPath++ = '\\';
309       *lpszPath = '\0';
310     }
311   }
312   return lpszPath;
313 }
314 
315 /*************************************************************************
316  * PathBuildRootA    [SHLWAPI.@]
317  *
318  * Create a root drive string (e.g. "A:\") from a drive number.
319  *
320  * PARAMS
321  *  lpszPath [O] Destination for the drive string
322  *
323  * RETURNS
324  *  lpszPath
325  *
326  * NOTES
327  *  If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
328  */
PathBuildRootA(LPSTR lpszPath,int drive)329 LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive)
330 {
331   TRACE("(%p,%d)\n", lpszPath, drive);
332 
333   if (lpszPath && drive >= 0 && drive < 26)
334   {
335     lpszPath[0] = 'A' + drive;
336     lpszPath[1] = ':';
337     lpszPath[2] = '\\';
338     lpszPath[3] = '\0';
339   }
340   return lpszPath;
341 }
342 
343 /*************************************************************************
344  * PathBuildRootW    [SHLWAPI.@]
345  *
346  * See PathBuildRootA.
347  */
PathBuildRootW(LPWSTR lpszPath,int drive)348 LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive)
349 {
350   TRACE("(%p,%d)\n", lpszPath, drive);
351 
352   if (lpszPath && drive >= 0 && drive < 26)
353   {
354     lpszPath[0] = 'A' + drive;
355     lpszPath[1] = ':';
356     lpszPath[2] = '\\';
357     lpszPath[3] = '\0';
358   }
359   return lpszPath;
360 }
361 
362 /*************************************************************************
363  * PathFindFileNameA  [SHLWAPI.@]
364  *
365  * Locate the start of the file name in a path
366  *
367  * PARAMS
368  *  lpszPath [I] Path to search
369  *
370  * RETURNS
371  *  A pointer to the first character of the file name
372  */
PathFindFileNameA(LPCSTR lpszPath)373 LPSTR WINAPI PathFindFileNameA(LPCSTR lpszPath)
374 {
375   LPCSTR lastSlash = lpszPath;
376 
377   TRACE("(%s)\n",debugstr_a(lpszPath));
378 
379   while (lpszPath && *lpszPath)
380   {
381     if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') &&
382         lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/')
383       lastSlash = lpszPath + 1;
384     lpszPath = CharNextA(lpszPath);
385   }
386   return (LPSTR)lastSlash;
387 }
388 
389 /*************************************************************************
390  * PathFindFileNameW  [SHLWAPI.@]
391  *
392  * See PathFindFileNameA.
393  */
PathFindFileNameW(LPCWSTR lpszPath)394 LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath)
395 {
396   LPCWSTR lastSlash = lpszPath;
397 
398   TRACE("(%s)\n",debugstr_w(lpszPath));
399 
400   while (lpszPath && *lpszPath)
401   {
402     if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') &&
403         lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/')
404       lastSlash = lpszPath + 1;
405     lpszPath++;
406   }
407   return (LPWSTR)lastSlash;
408 }
409 
410 /*************************************************************************
411  * PathFindExtensionA  [SHLWAPI.@]
412  *
413  * Locate the start of the file extension in a path
414  *
415  * PARAMS
416  *  lpszPath [I] The path to search
417  *
418  * RETURNS
419  *  A pointer to the first character of the extension, the end of
420  *  the string if the path has no extension, or NULL If lpszPath is NULL
421  */
PathFindExtensionA(LPCSTR lpszPath)422 LPSTR WINAPI PathFindExtensionA( LPCSTR lpszPath )
423 {
424   LPCSTR lastpoint = NULL;
425 
426   TRACE("(%s)\n", debugstr_a(lpszPath));
427 
428   if (lpszPath)
429   {
430     while (*lpszPath)
431     {
432       if (*lpszPath == '\\' || *lpszPath==' ')
433         lastpoint = NULL;
434       else if (*lpszPath == '.')
435         lastpoint = lpszPath;
436       lpszPath = CharNextA(lpszPath);
437     }
438   }
439   return (LPSTR)(lastpoint ? lastpoint : lpszPath);
440 }
441 
442 /*************************************************************************
443  * PathFindExtensionW  [SHLWAPI.@]
444  *
445  * See PathFindExtensionA.
446  */
PathFindExtensionW(LPCWSTR lpszPath)447 LPWSTR WINAPI PathFindExtensionW( LPCWSTR lpszPath )
448 {
449   LPCWSTR lastpoint = NULL;
450 
451   TRACE("(%s)\n", debugstr_w(lpszPath));
452 
453   if (lpszPath)
454   {
455     while (*lpszPath)
456     {
457       if (*lpszPath == '\\' || *lpszPath==' ')
458         lastpoint = NULL;
459       else if (*lpszPath == '.')
460         lastpoint = lpszPath;
461       lpszPath++;
462     }
463   }
464   return (LPWSTR)(lastpoint ? lastpoint : lpszPath);
465 }
466 
467 /*************************************************************************
468  * PathGetArgsA    [SHLWAPI.@]
469  *
470  * Find the next argument in a string delimited by spaces.
471  *
472  * PARAMS
473  *  lpszPath [I] The string to search for arguments in
474  *
475  * RETURNS
476  *  The start of the next argument in lpszPath, or NULL if lpszPath is NULL
477  *
478  * NOTES
479  *  Spaces in quoted strings are ignored as delimiters.
480  */
PathGetArgsA(LPCSTR lpszPath)481 LPSTR WINAPI PathGetArgsA(LPCSTR lpszPath)
482 {
483   BOOL bSeenQuote = FALSE;
484 
485   TRACE("(%s)\n",debugstr_a(lpszPath));
486 
487   if (lpszPath)
488   {
489     while (*lpszPath)
490     {
491       if ((*lpszPath==' ') && !bSeenQuote)
492         return (LPSTR)lpszPath + 1;
493       if (*lpszPath == '"')
494         bSeenQuote = !bSeenQuote;
495       lpszPath = CharNextA(lpszPath);
496     }
497   }
498   return (LPSTR)lpszPath;
499 }
500 
501 /*************************************************************************
502  * PathGetArgsW    [SHLWAPI.@]
503  *
504  * See PathGetArgsA.
505  */
PathGetArgsW(LPCWSTR lpszPath)506 LPWSTR WINAPI PathGetArgsW(LPCWSTR lpszPath)
507 {
508   BOOL bSeenQuote = FALSE;
509 
510   TRACE("(%s)\n",debugstr_w(lpszPath));
511 
512   if (lpszPath)
513   {
514     while (*lpszPath)
515     {
516       if ((*lpszPath==' ') && !bSeenQuote)
517         return (LPWSTR)lpszPath + 1;
518       if (*lpszPath == '"')
519         bSeenQuote = !bSeenQuote;
520       lpszPath++;
521     }
522   }
523   return (LPWSTR)lpszPath;
524 }
525 
526 /*************************************************************************
527  * PathGetDriveNumberA	[SHLWAPI.@]
528  *
529  * Return the drive number from a path
530  *
531  * PARAMS
532  *  lpszPath [I] Path to get the drive number from
533  *
534  * RETURNS
535  *  Success: The drive number corresponding to the drive in the path
536  *  Failure: -1, if lpszPath contains no valid drive
537  */
PathGetDriveNumberA(LPCSTR lpszPath)538 int WINAPI PathGetDriveNumberA(LPCSTR lpszPath)
539 {
540   TRACE ("(%s)\n",debugstr_a(lpszPath));
541 
542   if (lpszPath && !IsDBCSLeadByte(*lpszPath) && lpszPath[1] == ':' &&
543       tolower(*lpszPath) >= 'a' && tolower(*lpszPath) <= 'z')
544     return tolower(*lpszPath) - 'a';
545   return -1;
546 }
547 
548 /*************************************************************************
549  * PathGetDriveNumberW	[SHLWAPI.@]
550  *
551  * See PathGetDriveNumberA.
552  */
PathGetDriveNumberW(const WCHAR * path)553 int WINAPI PathGetDriveNumberW(const WCHAR *path)
554 {
555     WCHAR drive;
556 
557     static const WCHAR nt_prefixW[] = {'\\','\\','?','\\'};
558 
559     TRACE("(%s)\n", debugstr_w(path));
560 
561     if (!path)
562         return -1;
563 
564     if (!strncmpW(path, nt_prefixW, 4))
565         path += 4;
566 
567     drive = tolowerW(path[0]);
568     if (drive < 'a' || drive > 'z' || path[1] != ':')
569         return -1;
570 
571     return drive - 'a';
572 }
573 
574 /*************************************************************************
575  * PathRemoveFileSpecA	[SHLWAPI.@]
576  *
577  * Remove the file specification from a path.
578  *
579  * PARAMS
580  *  lpszPath [I/O] Path to remove the file spec from
581  *
582  * RETURNS
583  *  TRUE  If the path was valid and modified
584  *  FALSE Otherwise
585  */
PathRemoveFileSpecA(LPSTR lpszPath)586 BOOL WINAPI PathRemoveFileSpecA(LPSTR lpszPath)
587 {
588   LPSTR lpszFileSpec = lpszPath;
589   BOOL bModified = FALSE;
590 
591   TRACE("(%s)\n",debugstr_a(lpszPath));
592 
593   if(lpszPath)
594   {
595     /* Skip directory or UNC path */
596     if (*lpszPath == '\\')
597       lpszFileSpec = ++lpszPath;
598     if (*lpszPath == '\\')
599       lpszFileSpec = ++lpszPath;
600 
601     while (*lpszPath)
602     {
603       if(*lpszPath == '\\')
604         lpszFileSpec = lpszPath; /* Skip dir */
605       else if(*lpszPath == ':')
606       {
607         lpszFileSpec = ++lpszPath; /* Skip drive */
608         if (*lpszPath == '\\')
609           lpszFileSpec++;
610       }
611       if (!(lpszPath = CharNextA(lpszPath)))
612         break;
613     }
614 
615     if (*lpszFileSpec)
616     {
617       *lpszFileSpec = '\0';
618       bModified = TRUE;
619     }
620   }
621   return bModified;
622 }
623 
624 /*************************************************************************
625  * PathRemoveFileSpecW	[SHLWAPI.@]
626  *
627  * See PathRemoveFileSpecA.
628  */
PathRemoveFileSpecW(LPWSTR lpszPath)629 BOOL WINAPI PathRemoveFileSpecW(LPWSTR lpszPath)
630 {
631   LPWSTR lpszFileSpec = lpszPath;
632   BOOL bModified = FALSE;
633 
634   TRACE("(%s)\n",debugstr_w(lpszPath));
635 
636   if(lpszPath)
637   {
638     /* Skip directory or UNC path */
639     if (*lpszPath == '\\')
640       lpszFileSpec = ++lpszPath;
641     if (*lpszPath == '\\')
642       lpszFileSpec = ++lpszPath;
643 
644     while (*lpszPath)
645     {
646       if(*lpszPath == '\\')
647         lpszFileSpec = lpszPath; /* Skip dir */
648       else if(*lpszPath == ':')
649       {
650         lpszFileSpec = ++lpszPath; /* Skip drive */
651         if (*lpszPath == '\\')
652           lpszFileSpec++;
653       }
654       lpszPath++;
655     }
656 
657     if (*lpszFileSpec)
658     {
659       *lpszFileSpec = '\0';
660       bModified = TRUE;
661     }
662   }
663   return bModified;
664 }
665 
666 /*************************************************************************
667  * PathStripPathA	[SHLWAPI.@]
668  *
669  * Remove the initial path from the beginning of a filename
670  *
671  * PARAMS
672  *  lpszPath [I/O] Path to remove the initial path from
673  *
674  * RETURNS
675  *  Nothing.
676  */
PathStripPathA(LPSTR lpszPath)677 void WINAPI PathStripPathA(LPSTR lpszPath)
678 {
679   TRACE("(%s)\n", debugstr_a(lpszPath));
680 
681   if (lpszPath)
682   {
683     LPSTR lpszFileName = PathFindFileNameA(lpszPath);
684     if(lpszFileName != lpszPath)
685       RtlMoveMemory(lpszPath, lpszFileName, strlen(lpszFileName)+1);
686   }
687 }
688 
689 /*************************************************************************
690  * PathStripPathW	[SHLWAPI.@]
691  *
692  * See PathStripPathA.
693  */
PathStripPathW(LPWSTR lpszPath)694 void WINAPI PathStripPathW(LPWSTR lpszPath)
695 {
696   LPWSTR lpszFileName;
697 
698   TRACE("(%s)\n", debugstr_w(lpszPath));
699   lpszFileName = PathFindFileNameW(lpszPath);
700   if(lpszFileName != lpszPath)
701     RtlMoveMemory(lpszPath, lpszFileName, (strlenW(lpszFileName)+1)*sizeof(WCHAR));
702 }
703 
704 /*************************************************************************
705  * PathStripToRootA	[SHLWAPI.@]
706  *
707  * Reduce a path to its root.
708  *
709  * PARAMS
710  *  lpszPath [I/O] the path to reduce
711  *
712  * RETURNS
713  *  Success: TRUE if the stripped path is a root path
714  *  Failure: FALSE if the path cannot be stripped or is NULL
715  */
PathStripToRootA(LPSTR lpszPath)716 BOOL WINAPI PathStripToRootA(LPSTR lpszPath)
717 {
718   TRACE("(%s)\n", debugstr_a(lpszPath));
719 
720   if (!lpszPath)
721     return FALSE;
722   while(!PathIsRootA(lpszPath))
723     if (!PathRemoveFileSpecA(lpszPath))
724       return FALSE;
725   return TRUE;
726 }
727 
728 /*************************************************************************
729  * PathStripToRootW	[SHLWAPI.@]
730  *
731  * See PathStripToRootA.
732  */
PathStripToRootW(LPWSTR lpszPath)733 BOOL WINAPI PathStripToRootW(LPWSTR lpszPath)
734 {
735   TRACE("(%s)\n", debugstr_w(lpszPath));
736 
737   if (!lpszPath)
738     return FALSE;
739   while(!PathIsRootW(lpszPath))
740     if (!PathRemoveFileSpecW(lpszPath))
741       return FALSE;
742   return TRUE;
743 }
744 
745 /*************************************************************************
746  * PathRemoveArgsA	[SHLWAPI.@]
747  *
748  * Strip space separated arguments from a path.
749  *
750  * PARAMS
751  *  lpszPath [I/O] Path to remove arguments from
752  *
753  * RETURNS
754  *  Nothing.
755  */
PathRemoveArgsA(LPSTR lpszPath)756 void WINAPI PathRemoveArgsA(LPSTR lpszPath)
757 {
758   TRACE("(%s)\n",debugstr_a(lpszPath));
759 
760   if(lpszPath)
761   {
762     LPSTR lpszArgs = PathGetArgsA(lpszPath);
763     if (*lpszArgs)
764       lpszArgs[-1] = '\0';
765     else
766     {
767       LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs);
768       if(*lpszLastChar == ' ')
769         *lpszLastChar = '\0';
770     }
771   }
772 }
773 
774 /*************************************************************************
775  * PathRemoveArgsW	[SHLWAPI.@]
776  *
777  * See PathRemoveArgsA.
778  */
PathRemoveArgsW(LPWSTR lpszPath)779 void WINAPI PathRemoveArgsW(LPWSTR lpszPath)
780 {
781   TRACE("(%s)\n",debugstr_w(lpszPath));
782 
783   if(lpszPath)
784   {
785     LPWSTR lpszArgs = PathGetArgsW(lpszPath);
786     if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' '))
787       lpszArgs[-1] = '\0';
788   }
789 }
790 
791 /*************************************************************************
792  * PathRemoveExtensionA		[SHLWAPI.@]
793  *
794  * Remove the file extension from a path
795  *
796  * PARAMS
797  *  lpszPath [I/O] Path to remove the extension from
798  *
799  * NOTES
800  *  The NUL terminator must be written only if extension exists
801  *  and if the pointed character is not already NUL.
802  *
803  * RETURNS
804  *  Nothing.
805  */
PathRemoveExtensionA(LPSTR lpszPath)806 void WINAPI PathRemoveExtensionA(LPSTR lpszPath)
807 {
808   TRACE("(%s)\n", debugstr_a(lpszPath));
809 
810   if (lpszPath)
811   {
812     lpszPath = PathFindExtensionA(lpszPath);
813     if (lpszPath && *lpszPath != '\0')
814       *lpszPath = '\0';
815   }
816 }
817 
818 /*************************************************************************
819  * PathRemoveExtensionW		[SHLWAPI.@]
820  *
821  * See PathRemoveExtensionA.
822 */
PathRemoveExtensionW(LPWSTR lpszPath)823 void WINAPI PathRemoveExtensionW(LPWSTR lpszPath)
824 {
825   TRACE("(%s)\n", debugstr_w(lpszPath));
826 
827   if (lpszPath)
828   {
829     lpszPath = PathFindExtensionW(lpszPath);
830     if (lpszPath && *lpszPath != '\0')
831       *lpszPath = '\0';
832   }
833 }
834 
835 /*************************************************************************
836  * PathRemoveBackslashA	[SHLWAPI.@]
837  *
838  * Remove a trailing backslash from a path.
839  *
840  * PARAMS
841  *  lpszPath [I/O] Path to remove backslash from
842  *
843  * RETURNS
844  *  Success: A pointer to the end of the path
845  *  Failure: NULL, if lpszPath is NULL
846  */
PathRemoveBackslashA(LPSTR lpszPath)847 LPSTR WINAPI PathRemoveBackslashA( LPSTR lpszPath )
848 {
849   LPSTR szTemp = NULL;
850 
851   TRACE("(%s)\n", debugstr_a(lpszPath));
852 
853   if(lpszPath)
854   {
855     szTemp = CharPrevA(lpszPath, lpszPath + strlen(lpszPath));
856     if (!PathIsRootA(lpszPath) && *szTemp == '\\')
857       *szTemp = '\0';
858   }
859   return szTemp;
860 }
861 
862 /*************************************************************************
863  * PathRemoveBackslashW	[SHLWAPI.@]
864  *
865  * See PathRemoveBackslashA.
866  */
PathRemoveBackslashW(LPWSTR lpszPath)867 LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpszPath )
868 {
869   LPWSTR szTemp = NULL;
870 
871   TRACE("(%s)\n", debugstr_w(lpszPath));
872 
873   if(lpszPath)
874   {
875     szTemp = lpszPath + strlenW(lpszPath);
876     if (szTemp > lpszPath) szTemp--;
877     if (!PathIsRootW(lpszPath) && *szTemp == '\\')
878       *szTemp = '\0';
879   }
880   return szTemp;
881 }
882 
883 /*************************************************************************
884  * PathRemoveBlanksA [SHLWAPI.@]
885  *
886  * Remove Spaces from the start and end of a path.
887  *
888  * PARAMS
889  *  lpszPath [I/O] Path to strip blanks from
890  *
891  * RETURNS
892  *  Nothing.
893  */
PathRemoveBlanksA(LPSTR lpszPath)894 VOID WINAPI PathRemoveBlanksA(LPSTR lpszPath)
895 {
896   TRACE("(%s)\n", debugstr_a(lpszPath));
897 
898   if(lpszPath && *lpszPath)
899   {
900     LPSTR start = lpszPath;
901 
902     while (*lpszPath == ' ')
903       lpszPath = CharNextA(lpszPath);
904 
905     while(*lpszPath)
906       *start++ = *lpszPath++;
907 
908     if (start != lpszPath)
909       while (start[-1] == ' ')
910         start--;
911     *start = '\0';
912   }
913 }
914 
915 /*************************************************************************
916  * PathRemoveBlanksW [SHLWAPI.@]
917  *
918  * See PathRemoveBlanksA.
919  */
PathRemoveBlanksW(LPWSTR lpszPath)920 VOID WINAPI PathRemoveBlanksW(LPWSTR lpszPath)
921 {
922   TRACE("(%s)\n", debugstr_w(lpszPath));
923 
924   if(lpszPath && *lpszPath)
925   {
926     LPWSTR start = lpszPath;
927 
928     while (*lpszPath == ' ')
929       lpszPath++;
930 
931     while(*lpszPath)
932       *start++ = *lpszPath++;
933 
934     if (start != lpszPath)
935       while (start[-1] == ' ')
936         start--;
937     *start = '\0';
938   }
939 }
940 
941 /*************************************************************************
942  * PathQuoteSpacesA [SHLWAPI.@]
943  *
944  * Surround a path containing spaces in quotes.
945  *
946  * PARAMS
947  *  lpszPath [I/O] Path to quote
948  *
949  * RETURNS
950  *  Nothing.
951  *
952  * NOTES
953  *  The path is not changed if it is invalid or has no spaces.
954  */
PathQuoteSpacesA(LPSTR lpszPath)955 VOID WINAPI PathQuoteSpacesA(LPSTR lpszPath)
956 {
957   TRACE("(%s)\n", debugstr_a(lpszPath));
958 
959   if(lpszPath && StrChrA(lpszPath,' '))
960   {
961     size_t iLen = strlen(lpszPath) + 1;
962 
963     if (iLen + 2 < MAX_PATH)
964     {
965       memmove(lpszPath + 1, lpszPath, iLen);
966       lpszPath[0] = '"';
967       lpszPath[iLen] = '"';
968       lpszPath[iLen + 1] = '\0';
969     }
970   }
971 }
972 
973 /*************************************************************************
974  * PathQuoteSpacesW [SHLWAPI.@]
975  *
976  * See PathQuoteSpacesA.
977  */
PathQuoteSpacesW(LPWSTR lpszPath)978 VOID WINAPI PathQuoteSpacesW(LPWSTR lpszPath)
979 {
980   TRACE("(%s)\n", debugstr_w(lpszPath));
981 
982   if(lpszPath && StrChrW(lpszPath,' '))
983   {
984     int iLen = strlenW(lpszPath) + 1;
985 
986     if (iLen + 2 < MAX_PATH)
987     {
988       memmove(lpszPath + 1, lpszPath, iLen * sizeof(WCHAR));
989       lpszPath[0] = '"';
990       lpszPath[iLen] = '"';
991       lpszPath[iLen + 1] = '\0';
992     }
993   }
994 }
995 
996 /*************************************************************************
997  * PathUnquoteSpacesA [SHLWAPI.@]
998  *
999  * Remove quotes ("") from around a path, if present.
1000  *
1001  * PARAMS
1002  *  lpszPath [I/O] Path to strip quotes from
1003  *
1004  * RETURNS
1005  *  Nothing
1006  *
1007  * NOTES
1008  *  If the path contains a single quote only, an empty string will result.
1009  *  Otherwise quotes are only removed if they appear at the start and end
1010  *  of the path.
1011  */
PathUnquoteSpacesA(LPSTR lpszPath)1012 VOID WINAPI PathUnquoteSpacesA(LPSTR lpszPath)
1013 {
1014   TRACE("(%s)\n", debugstr_a(lpszPath));
1015 
1016   if (lpszPath && *lpszPath == '"')
1017   {
1018     DWORD dwLen = strlen(lpszPath) - 1;
1019 
1020     if (lpszPath[dwLen] == '"')
1021     {
1022       lpszPath[dwLen] = '\0';
1023       for (; *lpszPath; lpszPath++)
1024         *lpszPath = lpszPath[1];
1025     }
1026   }
1027 }
1028 
1029 /*************************************************************************
1030  * PathUnquoteSpacesW [SHLWAPI.@]
1031  *
1032  * See PathUnquoteSpacesA.
1033  */
PathUnquoteSpacesW(LPWSTR lpszPath)1034 VOID WINAPI PathUnquoteSpacesW(LPWSTR lpszPath)
1035 {
1036   TRACE("(%s)\n", debugstr_w(lpszPath));
1037 
1038   if (lpszPath && *lpszPath == '"')
1039   {
1040     DWORD dwLen = strlenW(lpszPath) - 1;
1041 
1042     if (lpszPath[dwLen] == '"')
1043     {
1044       lpszPath[dwLen] = '\0';
1045       for (; *lpszPath; lpszPath++)
1046         *lpszPath = lpszPath[1];
1047     }
1048   }
1049 }
1050 
1051 /*************************************************************************
1052  * PathParseIconLocationA  [SHLWAPI.@]
1053  *
1054  * Parse the location of an icon from a path.
1055  *
1056  * PARAMS
1057  *  lpszPath [I/O] The path to parse the icon location from.
1058  *
1059  * RETURNS
1060  *  Success: The number of the icon
1061  *  Failure: 0 if the path does not contain an icon location or is NULL
1062  *
1063  * NOTES
1064  *  The path has surrounding quotes and spaces removed regardless
1065  *  of whether the call succeeds or not.
1066  */
PathParseIconLocationA(LPSTR lpszPath)1067 int WINAPI PathParseIconLocationA(LPSTR lpszPath)
1068 {
1069   int iRet = 0;
1070   LPSTR lpszComma;
1071 
1072   TRACE("(%s)\n", debugstr_a(lpszPath));
1073 
1074   if (lpszPath)
1075   {
1076     if ((lpszComma = strchr(lpszPath, ',')))
1077     {
1078       *lpszComma++ = '\0';
1079       iRet = StrToIntA(lpszComma);
1080     }
1081     PathUnquoteSpacesA(lpszPath);
1082     PathRemoveBlanksA(lpszPath);
1083   }
1084   return iRet;
1085 }
1086 
1087 /*************************************************************************
1088  * PathParseIconLocationW  [SHLWAPI.@]
1089  *
1090  * See PathParseIconLocationA.
1091  */
PathParseIconLocationW(LPWSTR lpszPath)1092 int WINAPI PathParseIconLocationW(LPWSTR lpszPath)
1093 {
1094   int iRet = 0;
1095   LPWSTR lpszComma;
1096 
1097   TRACE("(%s)\n", debugstr_w(lpszPath));
1098 
1099   if (lpszPath)
1100   {
1101     if ((lpszComma = StrChrW(lpszPath, ',')))
1102     {
1103       *lpszComma++ = '\0';
1104       iRet = StrToIntW(lpszComma);
1105     }
1106     PathUnquoteSpacesW(lpszPath);
1107     PathRemoveBlanksW(lpszPath);
1108   }
1109   return iRet;
1110 }
1111 
1112 /*************************************************************************
1113  * @	[SHLWAPI.4]
1114  *
1115  * Unicode version of PathFileExistsDefExtA.
1116  */
PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)1117 BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)
1118 {
1119   static const WCHAR pszExts[][5]  = { { '.', 'p', 'i', 'f', 0},
1120                                        { '.', 'c', 'o', 'm', 0},
1121                                        { '.', 'e', 'x', 'e', 0},
1122                                        { '.', 'b', 'a', 't', 0},
1123                                        { '.', 'l', 'n', 'k', 0},
1124                                        { '.', 'c', 'm', 'd', 0},
1125                                        { 0, 0, 0, 0, 0} };
1126 
1127   TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich);
1128 
1129   if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath))
1130     return FALSE;
1131 
1132   if (dwWhich)
1133   {
1134     LPCWSTR szExt = PathFindExtensionW(lpszPath);
1135 #ifndef __REACTOS__
1136     if (!*szExt || dwWhich & 0x40)
1137 #else
1138     if (!*szExt || dwWhich & WHICH_OPTIONAL)
1139 #endif
1140     {
1141       size_t iChoose = 0;
1142       int iLen = lstrlenW(lpszPath);
1143       if (iLen > (MAX_PATH - 5))
1144         return FALSE;
1145 #ifndef __REACTOS__
1146       while ( (dwWhich & 0x1) && pszExts[iChoose][0] )
1147 #else
1148       while (pszExts[iChoose][0])
1149 #endif
1150       {
1151 #ifdef __REACTOS__
1152         if (dwWhich & 0x1)
1153         {
1154         if (GetFileAttributes(lpszPath) != FILE_ATTRIBUTE_DIRECTORY)
1155 #endif
1156         lstrcpyW(lpszPath + iLen, pszExts[iChoose]);
1157         if (PathFileExistsW(lpszPath))
1158           return TRUE;
1159 #ifdef __REACTOS__
1160         }
1161 #endif
1162         iChoose++;
1163         dwWhich >>= 1;
1164       }
1165       *(lpszPath + iLen) = (WCHAR)'\0';
1166       return FALSE;
1167     }
1168   }
1169   return PathFileExistsW(lpszPath);
1170 }
1171 
1172 /*************************************************************************
1173  * @	[SHLWAPI.3]
1174  *
1175  * Determine if a file exists locally and is of an executable type.
1176  *
1177  * PARAMS
1178  *  lpszPath       [I/O] File to search for
1179  *  dwWhich        [I]   Type of executable to search for
1180  *
1181  * RETURNS
1182  *  TRUE  If the file was found. lpszPath contains the file name.
1183  *  FALSE Otherwise.
1184  *
1185  * NOTES
1186  *  lpszPath is modified in place and must be at least MAX_PATH in length.
1187  *  If the function returns FALSE, the path is modified to its original state.
1188  *  If the given path contains an extension or dwWhich is 0, executable
1189  *  extensions are not checked.
1190  *
1191  *  Ordinals 3-6 are a classic case of MS exposing limited functionality to
1192  *  users (here through PathFindOnPathA()) and keeping advanced functionality for
1193  *  their own developers exclusive use. Monopoly, anyone?
1194  */
PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)1195 BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)
1196 {
1197   BOOL bRet = FALSE;
1198 
1199   TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich);
1200 
1201   if (lpszPath)
1202   {
1203     WCHAR szPath[MAX_PATH];
1204     MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1205     bRet = PathFileExistsDefExtW(szPath, dwWhich);
1206     if (bRet)
1207       WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
1208   }
1209   return bRet;
1210 }
1211 
1212 /*************************************************************************
1213  * SHLWAPI_PathFindInOtherDirs
1214  *
1215  * Internal helper for SHLWAPI_PathFindOnPathExA/W.
1216  */
SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile,DWORD dwWhich)1217 static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich)
1218 {
1219   static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'};
1220   static const WCHAR szPath[] = { 'P','A','T','H','\0'};
1221   DWORD dwLenPATH;
1222   LPCWSTR lpszCurr;
1223   WCHAR *lpszPATH;
1224   WCHAR buff[MAX_PATH];
1225 
1226   TRACE("(%s,%08x)\n", debugstr_w(lpszFile), dwWhich);
1227 
1228   /* Try system directories */
1229   GetSystemDirectoryW(buff, MAX_PATH);
1230   if (!PathAppendW(buff, lpszFile))
1231      return FALSE;
1232   if (PathFileExistsDefExtW(buff, dwWhich))
1233   {
1234     strcpyW(lpszFile, buff);
1235     return TRUE;
1236   }
1237   GetWindowsDirectoryW(buff, MAX_PATH);
1238   if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile))
1239     return FALSE;
1240   if (PathFileExistsDefExtW(buff, dwWhich))
1241   {
1242     strcpyW(lpszFile, buff);
1243     return TRUE;
1244   }
1245   GetWindowsDirectoryW(buff, MAX_PATH);
1246   if (!PathAppendW(buff, lpszFile))
1247     return FALSE;
1248   if (PathFileExistsDefExtW(buff, dwWhich))
1249   {
1250     strcpyW(lpszFile, buff);
1251     return TRUE;
1252   }
1253   /* Try dirs listed in %PATH% */
1254   dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH);
1255 
1256   if (!dwLenPATH || !(lpszPATH = HeapAlloc(GetProcessHeap(), 0, (dwLenPATH + 1) * sizeof (WCHAR))))
1257     return FALSE;
1258 
1259   GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1);
1260   lpszCurr = lpszPATH;
1261   while (lpszCurr)
1262   {
1263     LPCWSTR lpszEnd = lpszCurr;
1264     LPWSTR pBuff = buff;
1265 
1266     while (*lpszEnd == ' ')
1267       lpszEnd++;
1268     while (*lpszEnd && *lpszEnd != ';')
1269       *pBuff++ = *lpszEnd++;
1270     *pBuff = '\0';
1271 
1272     if (*lpszEnd)
1273       lpszCurr = lpszEnd + 1;
1274     else
1275       lpszCurr = NULL; /* Last Path, terminate after this */
1276 
1277     if (!PathAppendW(buff, lpszFile))
1278     {
1279       HeapFree(GetProcessHeap(), 0, lpszPATH);
1280       return FALSE;
1281     }
1282     if (PathFileExistsDefExtW(buff, dwWhich))
1283     {
1284       strcpyW(lpszFile, buff);
1285       HeapFree(GetProcessHeap(), 0, lpszPATH);
1286       return TRUE;
1287     }
1288   }
1289   HeapFree(GetProcessHeap(), 0, lpszPATH);
1290   return FALSE;
1291 }
1292 
1293 /*************************************************************************
1294  * @	[SHLWAPI.5]
1295  *
1296  * Search a range of paths for a specific type of executable.
1297  *
1298  * PARAMS
1299  *  lpszFile       [I/O] File to search for
1300  *  lppszOtherDirs [I]   Other directories to look in
1301  *  dwWhich        [I]   Type of executable to search for
1302  *
1303  * RETURNS
1304  *  Success: TRUE. The path to the executable is stored in lpszFile.
1305  *  Failure: FALSE. The path to the executable is unchanged.
1306  */
PathFindOnPathExA(LPSTR lpszFile,LPCSTR * lppszOtherDirs,DWORD dwWhich)1307 BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich)
1308 {
1309   WCHAR szFile[MAX_PATH];
1310   WCHAR buff[MAX_PATH];
1311 
1312   TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich);
1313 
1314   if (!lpszFile || !PathIsFileSpecA(lpszFile))
1315     return FALSE;
1316 
1317   MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH);
1318 
1319   /* Search provided directories first */
1320   if (lppszOtherDirs && *lppszOtherDirs)
1321   {
1322     WCHAR szOther[MAX_PATH];
1323     LPCSTR *lpszOtherPath = lppszOtherDirs;
1324 
1325     while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
1326     {
1327       MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH);
1328       PathCombineW(buff, szOther, szFile);
1329       if (PathFileExistsDefExtW(buff, dwWhich))
1330       {
1331         WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0);
1332         return TRUE;
1333       }
1334       lpszOtherPath++;
1335     }
1336   }
1337   /* Not found, try system and path dirs */
1338   if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich))
1339   {
1340     WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0);
1341     return TRUE;
1342   }
1343   return FALSE;
1344 }
1345 
1346 /*************************************************************************
1347  * @	[SHLWAPI.6]
1348  *
1349  * Unicode version of PathFindOnPathExA.
1350  */
PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR * lppszOtherDirs,DWORD dwWhich)1351 BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich)
1352 {
1353   WCHAR buff[MAX_PATH];
1354 
1355   TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich);
1356 
1357   if (!lpszFile || !PathIsFileSpecW(lpszFile))
1358     return FALSE;
1359 
1360   /* Search provided directories first */
1361   if (lppszOtherDirs && *lppszOtherDirs)
1362   {
1363     LPCWSTR *lpszOtherPath = lppszOtherDirs;
1364     while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
1365     {
1366       PathCombineW(buff, *lpszOtherPath, lpszFile);
1367       if (PathFileExistsDefExtW(buff, dwWhich))
1368       {
1369         strcpyW(lpszFile, buff);
1370         return TRUE;
1371       }
1372       lpszOtherPath++;
1373     }
1374   }
1375   /* Not found, try system and path dirs */
1376   return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich);
1377 }
1378 
1379 /*************************************************************************
1380  * PathFindOnPathA	[SHLWAPI.@]
1381  *
1382  * Search a range of paths for an executable.
1383  *
1384  * PARAMS
1385  *  lpszFile       [I/O] File to search for
1386  *  lppszOtherDirs [I]   Other directories to look in
1387  *
1388  * RETURNS
1389  *  Success: TRUE. The path to the executable is stored in lpszFile.
1390  *  Failure: FALSE. The path to the executable is unchanged.
1391  */
PathFindOnPathA(LPSTR lpszFile,LPCSTR * lppszOtherDirs)1392 BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs)
1393 {
1394   TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs);
1395   return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0);
1396  }
1397 
1398 /*************************************************************************
1399  * PathFindOnPathW      [SHLWAPI.@]
1400  *
1401  * See PathFindOnPathA.
1402  */
PathFindOnPathW(LPWSTR lpszFile,LPCWSTR * lppszOtherDirs)1403 BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs)
1404 {
1405   TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs);
1406   return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0);
1407 }
1408 
1409 /*************************************************************************
1410  * PathCompactPathExA   [SHLWAPI.@]
1411  *
1412  * Compact a path into a given number of characters.
1413  *
1414  * PARAMS
1415  *  lpszDest [O] Destination for compacted path
1416  *  lpszPath [I] Source path
1417  *  cchMax   [I] Maximum size of compacted path
1418  *  dwFlags  [I] Reserved
1419  *
1420  * RETURNS
1421  *  Success: TRUE. The compacted path is written to lpszDest.
1422  *  Failure: FALSE. lpszPath is undefined.
1423  *
1424  * NOTES
1425  *  If cchMax is given as 0, lpszDest will still be NUL terminated.
1426  *
1427  *  The Win32 version of this function contains a bug: When cchMax == 7,
1428  *  8 bytes will be written to lpszDest. This bug is fixed in the Wine
1429  *  implementation.
1430  *
1431  *  Some relative paths will be different when cchMax == 5 or 6. This occurs
1432  *  because Win32 will insert a "\" in lpszDest, even if one is
1433  *  not present in the original path.
1434  */
PathCompactPathExA(LPSTR lpszDest,LPCSTR lpszPath,UINT cchMax,DWORD dwFlags)1435 BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath,
1436                                UINT cchMax, DWORD dwFlags)
1437 {
1438   BOOL bRet = FALSE;
1439 
1440   TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags);
1441 
1442   if (lpszPath && lpszDest)
1443   {
1444     WCHAR szPath[MAX_PATH];
1445     WCHAR szDest[MAX_PATH];
1446 
1447     MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1448     szDest[0] = '\0';
1449     bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags);
1450     WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0);
1451   }
1452   return bRet;
1453 }
1454 
1455 /*************************************************************************
1456  * PathCompactPathExW   [SHLWAPI.@]
1457  *
1458  * See PathCompactPathExA.
1459  */
PathCompactPathExW(LPWSTR lpszDest,LPCWSTR lpszPath,UINT cchMax,DWORD dwFlags)1460 BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath,
1461                                UINT cchMax, DWORD dwFlags)
1462 {
1463   static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
1464   LPCWSTR lpszFile;
1465   DWORD dwLen, dwFileLen = 0;
1466 
1467   TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags);
1468 
1469   if (!lpszPath)
1470     return FALSE;
1471 
1472   if (!lpszDest)
1473   {
1474     WARN("Invalid lpszDest would crash under Win32!\n");
1475     return FALSE;
1476   }
1477 
1478   *lpszDest = '\0';
1479 
1480   if (cchMax < 2)
1481     return TRUE;
1482 
1483   dwLen = strlenW(lpszPath) + 1;
1484 
1485   if (dwLen < cchMax)
1486   {
1487     /* Don't need to compact */
1488     memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
1489     return TRUE;
1490   }
1491 
1492   /* Path must be compacted to fit into lpszDest */
1493   lpszFile = PathFindFileNameW(lpszPath);
1494   dwFileLen = lpszPath + dwLen - lpszFile;
1495 
1496   if (dwFileLen == dwLen)
1497   {
1498     /* No root in psth */
1499     if (cchMax <= 4)
1500     {
1501       while (--cchMax > 0) /* No room left for anything but ellipses */
1502         *lpszDest++ = '.';
1503       *lpszDest = '\0';
1504       return TRUE;
1505     }
1506     /* Compact the file name with ellipses at the end */
1507     cchMax -= 4;
1508     memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
1509     strcpyW(lpszDest + cchMax, szEllipses);
1510     return TRUE;
1511   }
1512   /* We have a root in the path */
1513   lpszFile--; /* Start compacted filename with the path separator */
1514   dwFileLen++;
1515 
1516   if (dwFileLen + 3 > cchMax)
1517   {
1518     /* Compact the file name */
1519     if (cchMax <= 4)
1520     {
1521       while (--cchMax > 0) /* No room left for anything but ellipses */
1522         *lpszDest++ = '.';
1523       *lpszDest = '\0';
1524       return TRUE;
1525     }
1526     strcpyW(lpszDest, szEllipses);
1527     lpszDest += 3;
1528     cchMax -= 4;
1529     *lpszDest++ = *lpszFile++;
1530     if (cchMax <= 4)
1531     {
1532       while (--cchMax > 0) /* No room left for anything but ellipses */
1533         *lpszDest++ = '.';
1534       *lpszDest = '\0';
1535       return TRUE;
1536     }
1537     cchMax -= 4;
1538     memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
1539     strcpyW(lpszDest + cchMax, szEllipses);
1540     return TRUE;
1541   }
1542 
1543   /* Only the root needs to be Compacted */
1544   dwLen = cchMax - dwFileLen - 3;
1545   memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
1546   strcpyW(lpszDest + dwLen, szEllipses);
1547   strcpyW(lpszDest + dwLen + 3, lpszFile);
1548   return TRUE;
1549 }
1550 
1551 /*************************************************************************
1552  * PathIsRelativeA	[SHLWAPI.@]
1553  *
1554  * Determine if a path is a relative path.
1555  *
1556  * PARAMS
1557  *  lpszPath [I] Path to check
1558  *
1559  * RETURNS
1560  *  TRUE:  The path is relative, or is invalid.
1561  *  FALSE: The path is not relative.
1562  */
PathIsRelativeA(LPCSTR lpszPath)1563 BOOL WINAPI PathIsRelativeA (LPCSTR lpszPath)
1564 {
1565   TRACE("(%s)\n",debugstr_a(lpszPath));
1566 
1567   if (!lpszPath || !*lpszPath || IsDBCSLeadByte(*lpszPath))
1568     return TRUE;
1569   if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':'))
1570     return FALSE;
1571   return TRUE;
1572 }
1573 
1574 /*************************************************************************
1575  *  PathIsRelativeW	[SHLWAPI.@]
1576  *
1577  * See PathIsRelativeA.
1578  */
PathIsRelativeW(LPCWSTR lpszPath)1579 BOOL WINAPI PathIsRelativeW (LPCWSTR lpszPath)
1580 {
1581   TRACE("(%s)\n",debugstr_w(lpszPath));
1582 
1583   if (!lpszPath || !*lpszPath)
1584     return TRUE;
1585   if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':'))
1586     return FALSE;
1587   return TRUE;
1588 }
1589 
1590 /*************************************************************************
1591  * PathIsRootA		[SHLWAPI.@]
1592  *
1593  * Determine if a path is a root path.
1594  *
1595  * PARAMS
1596  *  lpszPath [I] Path to check
1597  *
1598  * RETURNS
1599  *  TRUE  If lpszPath is valid and a root path,
1600  *  FALSE Otherwise
1601  */
PathIsRootA(LPCSTR lpszPath)1602 BOOL WINAPI PathIsRootA(LPCSTR lpszPath)
1603 {
1604   TRACE("(%s)\n", debugstr_a(lpszPath));
1605 
1606   if (lpszPath && *lpszPath)
1607   {
1608     if (*lpszPath == '\\')
1609     {
1610       if (!lpszPath[1])
1611         return TRUE; /* \ */
1612       else if (lpszPath[1]=='\\')
1613       {
1614         BOOL bSeenSlash = FALSE;
1615         lpszPath += 2;
1616 
1617         /* Check for UNC root path */
1618         while (*lpszPath)
1619         {
1620           if (*lpszPath == '\\')
1621           {
1622             if (bSeenSlash)
1623               return FALSE;
1624             bSeenSlash = TRUE;
1625           }
1626           lpszPath = CharNextA(lpszPath);
1627         }
1628         return TRUE;
1629       }
1630     }
1631     else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0')
1632       return TRUE; /* X:\ */
1633   }
1634   return FALSE;
1635 }
1636 
1637 /*************************************************************************
1638  * PathIsRootW		[SHLWAPI.@]
1639  *
1640  * See PathIsRootA.
1641  */
PathIsRootW(LPCWSTR lpszPath)1642 BOOL WINAPI PathIsRootW(LPCWSTR lpszPath)
1643 {
1644   TRACE("(%s)\n", debugstr_w(lpszPath));
1645 
1646   if (lpszPath && *lpszPath)
1647   {
1648     if (*lpszPath == '\\')
1649     {
1650       if (!lpszPath[1])
1651         return TRUE; /* \ */
1652       else if (lpszPath[1]=='\\')
1653       {
1654         BOOL bSeenSlash = FALSE;
1655         lpszPath += 2;
1656 
1657         /* Check for UNC root path */
1658         while (*lpszPath)
1659         {
1660           if (*lpszPath == '\\')
1661           {
1662             if (bSeenSlash)
1663               return FALSE;
1664             bSeenSlash = TRUE;
1665           }
1666           lpszPath++;
1667         }
1668         return TRUE;
1669       }
1670     }
1671     else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0')
1672       return TRUE; /* X:\ */
1673   }
1674   return FALSE;
1675 }
1676 
1677 /*************************************************************************
1678  * PathIsDirectoryA	[SHLWAPI.@]
1679  *
1680  * Determine if a path is a valid directory
1681  *
1682  * PARAMS
1683  *  lpszPath [I] Path to check.
1684  *
1685  * RETURNS
1686  *  FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
1687  *  FALSE if lpszPath is invalid or not a directory.
1688  *
1689  * NOTES
1690  *  Although this function is prototyped as returning a BOOL, it returns
1691  *  FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
1692  *
1693  *|  if (PathIsDirectoryA("c:\\windows\\") == TRUE)
1694  *|    ...
1695  *
1696  *  will always fail.
1697  */
PathIsDirectoryA(LPCSTR lpszPath)1698 BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath)
1699 {
1700   DWORD dwAttr;
1701 
1702   TRACE("(%s)\n", debugstr_a(lpszPath));
1703 
1704   if (!lpszPath || PathIsUNCServerA(lpszPath))
1705     return FALSE;
1706 
1707   if (PathIsUNCServerShareA(lpszPath))
1708   {
1709     FIXME("UNC Server Share not yet supported - FAILING\n");
1710     return FALSE;
1711   }
1712 
1713   if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES)
1714     return FALSE;
1715   return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
1716 }
1717 
1718 /*************************************************************************
1719  * PathIsDirectoryW	[SHLWAPI.@]
1720  *
1721  * See PathIsDirectoryA.
1722  */
PathIsDirectoryW(LPCWSTR lpszPath)1723 BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
1724 {
1725   DWORD dwAttr;
1726 
1727   TRACE("(%s)\n", debugstr_w(lpszPath));
1728 
1729   if (!lpszPath || PathIsUNCServerW(lpszPath))
1730     return FALSE;
1731 
1732   if (PathIsUNCServerShareW(lpszPath))
1733   {
1734     FIXME("UNC Server Share not yet supported - FAILING\n");
1735     return FALSE;
1736   }
1737 
1738   if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
1739     return FALSE;
1740   return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
1741 }
1742 
1743 /*************************************************************************
1744  * PathFileExistsA	[SHLWAPI.@]
1745  *
1746  * Determine if a file exists.
1747  *
1748  * PARAMS
1749  *  lpszPath [I] Path to check
1750  *
1751  * RETURNS
1752  *  TRUE  If the file exists and is readable
1753  *  FALSE Otherwise
1754  */
PathFileExistsA(LPCSTR lpszPath)1755 BOOL WINAPI PathFileExistsA(LPCSTR lpszPath)
1756 {
1757   UINT iPrevErrMode;
1758   DWORD dwAttr;
1759 
1760   TRACE("(%s)\n",debugstr_a(lpszPath));
1761 
1762   if (!lpszPath)
1763     return FALSE;
1764 
1765   /* Prevent a dialog box if path is on a disk that has been ejected. */
1766   iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1767   dwAttr = GetFileAttributesA(lpszPath);
1768   SetErrorMode(iPrevErrMode);
1769   return dwAttr != INVALID_FILE_ATTRIBUTES;
1770 }
1771 
1772 /*************************************************************************
1773  * PathFileExistsW	[SHLWAPI.@]
1774  *
1775  * See PathFileExistsA.
1776  */
PathFileExistsW(LPCWSTR lpszPath)1777 BOOL WINAPI PathFileExistsW(LPCWSTR lpszPath)
1778 {
1779   UINT iPrevErrMode;
1780   DWORD dwAttr;
1781 
1782   TRACE("(%s)\n",debugstr_w(lpszPath));
1783 
1784   if (!lpszPath)
1785     return FALSE;
1786 
1787   iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1788   dwAttr = GetFileAttributesW(lpszPath);
1789   SetErrorMode(iPrevErrMode);
1790   return dwAttr != INVALID_FILE_ATTRIBUTES;
1791 }
1792 
1793 /*************************************************************************
1794  * PathFileExistsAndAttributesA	[SHLWAPI.445]
1795  *
1796  * Determine if a file exists.
1797  *
1798  * PARAMS
1799  *  lpszPath [I] Path to check
1800  *  dwAttr   [O] attributes of file
1801  *
1802  * RETURNS
1803  *  TRUE  If the file exists and is readable
1804  *  FALSE Otherwise
1805  */
PathFileExistsAndAttributesA(LPCSTR lpszPath,DWORD * dwAttr)1806 BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr)
1807 {
1808   UINT iPrevErrMode;
1809   DWORD dwVal = 0;
1810 
1811   TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr);
1812 
1813   if (dwAttr)
1814     *dwAttr = INVALID_FILE_ATTRIBUTES;
1815 
1816   if (!lpszPath)
1817     return FALSE;
1818 
1819   iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1820   dwVal = GetFileAttributesA(lpszPath);
1821   SetErrorMode(iPrevErrMode);
1822   if (dwAttr)
1823     *dwAttr = dwVal;
1824   return (dwVal != INVALID_FILE_ATTRIBUTES);
1825 }
1826 
1827 /*************************************************************************
1828  * PathFileExistsAndAttributesW	[SHLWAPI.446]
1829  *
1830  * See PathFileExistsA.
1831  */
PathFileExistsAndAttributesW(LPCWSTR lpszPath,DWORD * dwAttr)1832 BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr)
1833 {
1834   UINT iPrevErrMode;
1835   DWORD dwVal;
1836 
1837   TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr);
1838 
1839   if (!lpszPath)
1840     return FALSE;
1841 
1842   iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1843   dwVal = GetFileAttributesW(lpszPath);
1844   SetErrorMode(iPrevErrMode);
1845   if (dwAttr)
1846     *dwAttr = dwVal;
1847   return (dwVal != INVALID_FILE_ATTRIBUTES);
1848 }
1849 
1850 /*************************************************************************
1851  * PathMatchSingleMaskA	[internal]
1852  */
PathMatchSingleMaskA(LPCSTR name,LPCSTR mask)1853 static BOOL PathMatchSingleMaskA(LPCSTR name, LPCSTR mask)
1854 {
1855   while (*name && *mask && *mask!=';')
1856   {
1857     if (*mask == '*')
1858     {
1859       do
1860       {
1861         if (PathMatchSingleMaskA(name,mask+1))
1862           return TRUE;  /* try substrings */
1863       } while (*name++);
1864       return FALSE;
1865     }
1866 
1867     if (toupper(*mask) != toupper(*name) && *mask != '?')
1868       return FALSE;
1869 
1870     name = CharNextA(name);
1871     mask = CharNextA(mask);
1872   }
1873 
1874   if (!*name)
1875   {
1876     while (*mask == '*')
1877       mask++;
1878     if (!*mask || *mask == ';')
1879       return TRUE;
1880   }
1881   return FALSE;
1882 }
1883 
1884 /*************************************************************************
1885  * PathMatchSingleMaskW	[internal]
1886  */
PathMatchSingleMaskW(LPCWSTR name,LPCWSTR mask)1887 static BOOL PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask)
1888 {
1889   while (*name && *mask && *mask != ';')
1890   {
1891     if (*mask == '*')
1892     {
1893       do
1894       {
1895         if (PathMatchSingleMaskW(name,mask+1))
1896           return TRUE;  /* try substrings */
1897       } while (*name++);
1898       return FALSE;
1899     }
1900 
1901     if (toupperW(*mask) != toupperW(*name) && *mask != '?')
1902       return FALSE;
1903 
1904     name++;
1905     mask++;
1906   }
1907   if (!*name)
1908   {
1909     while (*mask == '*')
1910       mask++;
1911     if (!*mask || *mask == ';')
1912       return TRUE;
1913   }
1914   return FALSE;
1915 }
1916 
1917 /*************************************************************************
1918  * PathMatchSpecA	[SHLWAPI.@]
1919  *
1920  * Determine if a path matches one or more search masks.
1921  *
1922  * PARAMS
1923  *  lpszPath [I] Path to check
1924  *  lpszMask [I] Search mask(s)
1925  *
1926  * RETURNS
1927  *  TRUE  If lpszPath is valid and is matched
1928  *  FALSE Otherwise
1929  *
1930  * NOTES
1931  *  Multiple search masks may be given if they are separated by ";". The
1932  *  pattern "*.*" is treated specially in that it matches all paths (for
1933  *  backwards compatibility with DOS).
1934  */
PathMatchSpecA(LPCSTR lpszPath,LPCSTR lpszMask)1935 BOOL WINAPI PathMatchSpecA(LPCSTR lpszPath, LPCSTR lpszMask)
1936 {
1937   TRACE("(%s,%s)\n", lpszPath, lpszMask);
1938 
1939   if (!lstrcmpA(lpszMask, "*.*"))
1940     return TRUE; /* Matches every path */
1941 
1942   while (*lpszMask)
1943   {
1944     while (*lpszMask == ' ')
1945       lpszMask++; /* Eat leading spaces */
1946 
1947     if (PathMatchSingleMaskA(lpszPath, lpszMask))
1948       return TRUE; /* Matches the current mask */
1949 
1950     while (*lpszMask && *lpszMask != ';')
1951       lpszMask = CharNextA(lpszMask); /* masks separated by ';' */
1952 
1953     if (*lpszMask == ';')
1954       lpszMask++;
1955   }
1956   return FALSE;
1957 }
1958 
1959 /*************************************************************************
1960  * PathMatchSpecW	[SHLWAPI.@]
1961  *
1962  * See PathMatchSpecA.
1963  */
PathMatchSpecW(LPCWSTR lpszPath,LPCWSTR lpszMask)1964 BOOL WINAPI PathMatchSpecW(LPCWSTR lpszPath, LPCWSTR lpszMask)
1965 {
1966   static const WCHAR szStarDotStar[] = { '*', '.', '*', '\0' };
1967 
1968   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszMask));
1969 
1970   if (!lstrcmpW(lpszMask, szStarDotStar))
1971     return TRUE; /* Matches every path */
1972 
1973   while (*lpszMask)
1974   {
1975     while (*lpszMask == ' ')
1976       lpszMask++; /* Eat leading spaces */
1977 
1978     if (PathMatchSingleMaskW(lpszPath, lpszMask))
1979       return TRUE; /* Matches the current path */
1980 
1981     while (*lpszMask && *lpszMask != ';')
1982       lpszMask++; /* masks separated by ';' */
1983 
1984     if (*lpszMask == ';')
1985       lpszMask++;
1986   }
1987   return FALSE;
1988 }
1989 
1990 /*************************************************************************
1991  * PathIsSameRootA	[SHLWAPI.@]
1992  *
1993  * Determine if two paths share the same root.
1994  *
1995  * PARAMS
1996  *  lpszPath1 [I] Source path
1997  *  lpszPath2 [I] Path to compare with
1998  *
1999  * RETURNS
2000  *  TRUE  If both paths are valid and share the same root.
2001  *  FALSE If either path is invalid or the paths do not share the same root.
2002  */
PathIsSameRootA(LPCSTR lpszPath1,LPCSTR lpszPath2)2003 BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2)
2004 {
2005   LPCSTR lpszStart;
2006   int dwLen;
2007 
2008   TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2));
2009 
2010   if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1)))
2011     return FALSE;
2012 
2013   dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1;
2014   if (lpszStart - lpszPath1 > dwLen)
2015     return FALSE; /* Paths not common up to length of the root */
2016   return TRUE;
2017 }
2018 
2019 /*************************************************************************
2020  * PathIsSameRootW	[SHLWAPI.@]
2021  *
2022  * See PathIsSameRootA.
2023  */
PathIsSameRootW(LPCWSTR lpszPath1,LPCWSTR lpszPath2)2024 BOOL WINAPI PathIsSameRootW(LPCWSTR lpszPath1, LPCWSTR lpszPath2)
2025 {
2026   LPCWSTR lpszStart;
2027   int dwLen;
2028 
2029   TRACE("(%s,%s)\n", debugstr_w(lpszPath1), debugstr_w(lpszPath2));
2030 
2031   if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootW(lpszPath1)))
2032     return FALSE;
2033 
2034   dwLen = PathCommonPrefixW(lpszPath1, lpszPath2, NULL) + 1;
2035   if (lpszStart - lpszPath1 > dwLen)
2036     return FALSE; /* Paths not common up to length of the root */
2037   return TRUE;
2038 }
2039 
2040 /*************************************************************************
2041  * PathIsContentTypeA   [SHLWAPI.@]
2042  *
2043  * Determine if a file is of a given registered content type.
2044  *
2045  * PARAMS
2046  *  lpszPath        [I] File to check
2047  *  lpszContentType [I] Content type to check for
2048  *
2049  * RETURNS
2050  *  TRUE  If lpszPath is a given registered content type,
2051  *  FALSE Otherwise.
2052  *
2053  * NOTES
2054  *  This function looks up the registered content type for lpszPath. If
2055  *  a content type is registered, it is compared (case insensitively) to
2056  *  lpszContentType. Only if this matches does the function succeed.
2057  */
PathIsContentTypeA(LPCSTR lpszPath,LPCSTR lpszContentType)2058 BOOL WINAPI PathIsContentTypeA(LPCSTR lpszPath, LPCSTR lpszContentType)
2059 {
2060   LPCSTR szExt;
2061   DWORD dwDummy;
2062   char szBuff[MAX_PATH];
2063 
2064   TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszContentType));
2065 
2066   if (lpszPath && (szExt = PathFindExtensionA(lpszPath)) && *szExt &&
2067       !SHGetValueA(HKEY_CLASSES_ROOT, szExt, "Content Type",
2068                    REG_NONE, szBuff, &dwDummy) &&
2069       !strcasecmp(lpszContentType, szBuff))
2070   {
2071     return TRUE;
2072   }
2073   return FALSE;
2074 }
2075 
2076 /*************************************************************************
2077  * PathIsContentTypeW   [SHLWAPI.@]
2078  *
2079  * See PathIsContentTypeA.
2080  */
PathIsContentTypeW(LPCWSTR lpszPath,LPCWSTR lpszContentType)2081 BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType)
2082 {
2083   static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
2084   LPCWSTR szExt;
2085   DWORD dwDummy;
2086   WCHAR szBuff[MAX_PATH];
2087 
2088   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType));
2089 
2090   if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt &&
2091       !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType,
2092                    REG_NONE, szBuff, &dwDummy) &&
2093       !strcmpiW(lpszContentType, szBuff))
2094   {
2095     return TRUE;
2096   }
2097   return FALSE;
2098 }
2099 
2100 /*************************************************************************
2101  * PathIsFileSpecA   [SHLWAPI.@]
2102  *
2103  * Determine if a path is a file specification.
2104  *
2105  * PARAMS
2106  *  lpszPath [I] Path to check
2107  *
2108  * RETURNS
2109  *  TRUE  If lpszPath is a file specification (i.e. Contains no directories).
2110  *  FALSE Otherwise.
2111  */
PathIsFileSpecA(LPCSTR lpszPath)2112 BOOL WINAPI PathIsFileSpecA(LPCSTR lpszPath)
2113 {
2114   TRACE("(%s)\n", debugstr_a(lpszPath));
2115 
2116   if (!lpszPath)
2117     return FALSE;
2118 
2119   while (*lpszPath)
2120   {
2121     if (*lpszPath == '\\' || *lpszPath == ':')
2122       return FALSE;
2123     lpszPath = CharNextA(lpszPath);
2124   }
2125   return TRUE;
2126 }
2127 
2128 /*************************************************************************
2129  * PathIsFileSpecW   [SHLWAPI.@]
2130  *
2131  * See PathIsFileSpecA.
2132  */
PathIsFileSpecW(LPCWSTR lpszPath)2133 BOOL WINAPI PathIsFileSpecW(LPCWSTR lpszPath)
2134 {
2135   TRACE("(%s)\n", debugstr_w(lpszPath));
2136 
2137   if (!lpszPath)
2138     return FALSE;
2139 
2140   while (*lpszPath)
2141   {
2142     if (*lpszPath == '\\' || *lpszPath == ':')
2143       return FALSE;
2144     lpszPath++;
2145   }
2146   return TRUE;
2147 }
2148 
2149 /*************************************************************************
2150  * PathIsPrefixA   [SHLWAPI.@]
2151  *
2152  * Determine if a path is a prefix of another.
2153  *
2154  * PARAMS
2155  *  lpszPrefix [I] Prefix
2156  *  lpszPath   [I] Path to check
2157  *
2158  * RETURNS
2159  *  TRUE  If lpszPath has lpszPrefix as its prefix,
2160  *  FALSE If either path is NULL or lpszPrefix is not a prefix
2161  */
PathIsPrefixA(LPCSTR lpszPrefix,LPCSTR lpszPath)2162 BOOL WINAPI PathIsPrefixA (LPCSTR lpszPrefix, LPCSTR lpszPath)
2163 {
2164   TRACE("(%s,%s)\n", debugstr_a(lpszPrefix), debugstr_a(lpszPath));
2165 
2166   if (lpszPrefix && lpszPath &&
2167       PathCommonPrefixA(lpszPath, lpszPrefix, NULL) == (int)strlen(lpszPrefix))
2168     return TRUE;
2169   return FALSE;
2170 }
2171 
2172 /*************************************************************************
2173  *  PathIsPrefixW   [SHLWAPI.@]
2174  *
2175  *  See PathIsPrefixA.
2176  */
PathIsPrefixW(LPCWSTR lpszPrefix,LPCWSTR lpszPath)2177 BOOL WINAPI PathIsPrefixW(LPCWSTR lpszPrefix, LPCWSTR lpszPath)
2178 {
2179   TRACE("(%s,%s)\n", debugstr_w(lpszPrefix), debugstr_w(lpszPath));
2180 
2181   if (lpszPrefix && lpszPath &&
2182       PathCommonPrefixW(lpszPath, lpszPrefix, NULL) == (int)strlenW(lpszPrefix))
2183     return TRUE;
2184   return FALSE;
2185 }
2186 
2187 /*************************************************************************
2188  * PathIsSystemFolderA   [SHLWAPI.@]
2189  *
2190  * Determine if a path or file attributes are a system folder.
2191  *
2192  * PARAMS
2193  *  lpszPath  [I] Path to check.
2194  *  dwAttrib  [I] Attributes to check, if lpszPath is NULL.
2195  *
2196  * RETURNS
2197  *  TRUE   If lpszPath or dwAttrib are a system folder.
2198  *  FALSE  If GetFileAttributesA() fails or neither parameter is a system folder.
2199  */
PathIsSystemFolderA(LPCSTR lpszPath,DWORD dwAttrib)2200 BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib)
2201 {
2202   TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath), dwAttrib);
2203 
2204   if (lpszPath && *lpszPath)
2205     dwAttrib = GetFileAttributesA(lpszPath);
2206 
2207   if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
2208       !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
2209     return FALSE;
2210   return TRUE;
2211 }
2212 
2213 /*************************************************************************
2214  * PathIsSystemFolderW   [SHLWAPI.@]
2215  *
2216  * See PathIsSystemFolderA.
2217  */
PathIsSystemFolderW(LPCWSTR lpszPath,DWORD dwAttrib)2218 BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib)
2219 {
2220   TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath), dwAttrib);
2221 
2222   if (lpszPath && *lpszPath)
2223     dwAttrib = GetFileAttributesW(lpszPath);
2224 
2225   if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
2226       !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
2227     return FALSE;
2228   return TRUE;
2229 }
2230 
2231 /*************************************************************************
2232  * PathIsUNCA		[SHLWAPI.@]
2233  *
2234  * Determine if a path is in UNC format.
2235  *
2236  * PARAMS
2237  *  lpszPath [I] Path to check
2238  *
2239  * RETURNS
2240  *  TRUE: The path is UNC.
2241  *  FALSE: The path is not UNC or is NULL.
2242  */
PathIsUNCA(LPCSTR lpszPath)2243 BOOL WINAPI PathIsUNCA(LPCSTR lpszPath)
2244 {
2245   TRACE("(%s)\n",debugstr_a(lpszPath));
2246 
2247 /*
2248  * On Windows 2003, tests show that strings starting with "\\?" are
2249  * considered UNC, while on Windows Vista+ this is not the case anymore.
2250  */
2251 // #ifdef __REACTOS__
2252 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
2253   if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?'))
2254 #else
2255   if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\'))
2256 #endif
2257     return TRUE;
2258   return FALSE;
2259 }
2260 
2261 /*************************************************************************
2262  * PathIsUNCW		[SHLWAPI.@]
2263  *
2264  * See PathIsUNCA.
2265  */
PathIsUNCW(LPCWSTR lpszPath)2266 BOOL WINAPI PathIsUNCW(LPCWSTR lpszPath)
2267 {
2268   TRACE("(%s)\n",debugstr_w(lpszPath));
2269 
2270 /*
2271  * On Windows 2003, tests show that strings starting with "\\?" are
2272  * considered UNC, while on Windows Vista+ this is not the case anymore.
2273  */
2274 // #ifdef __REACTOS__
2275 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
2276   if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?'))
2277 #else
2278   if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\'))
2279 #endif
2280     return TRUE;
2281   return FALSE;
2282 }
2283 
2284 /*************************************************************************
2285  * PathIsUNCServerA   [SHLWAPI.@]
2286  *
2287  * Determine if a path is a UNC server name ("\\SHARENAME").
2288  *
2289  * PARAMS
2290  *  lpszPath  [I] Path to check.
2291  *
2292  * RETURNS
2293  *  TRUE   If lpszPath is a valid UNC server name.
2294  *  FALSE  Otherwise.
2295  *
2296  * NOTES
2297  *  This routine is bug compatible with Win32: Server names with a
2298  *  trailing backslash (e.g. "\\FOO\"), return FALSE incorrectly.
2299  *  Fixing this bug may break other shlwapi functions!
2300  */
PathIsUNCServerA(LPCSTR lpszPath)2301 BOOL WINAPI PathIsUNCServerA(LPCSTR lpszPath)
2302 {
2303   TRACE("(%s)\n", debugstr_a(lpszPath));
2304 
2305   if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2306   {
2307     while (*lpszPath)
2308     {
2309       if (*lpszPath == '\\')
2310         return FALSE;
2311       lpszPath = CharNextA(lpszPath);
2312     }
2313     return TRUE;
2314   }
2315   return FALSE;
2316 }
2317 
2318 /*************************************************************************
2319  * PathIsUNCServerW   [SHLWAPI.@]
2320  *
2321  * See PathIsUNCServerA.
2322  */
PathIsUNCServerW(LPCWSTR lpszPath)2323 BOOL WINAPI PathIsUNCServerW(LPCWSTR lpszPath)
2324 {
2325   TRACE("(%s)\n", debugstr_w(lpszPath));
2326 
2327   if (lpszPath && lpszPath[0] == '\\' && lpszPath[1] == '\\')
2328   {
2329       return !strchrW( lpszPath + 2, '\\' );
2330   }
2331   return FALSE;
2332 }
2333 
2334 /*************************************************************************
2335  * PathIsUNCServerShareA   [SHLWAPI.@]
2336  *
2337  * Determine if a path is a UNC server share ("\\SHARENAME\SHARE").
2338  *
2339  * PARAMS
2340  *  lpszPath  [I] Path to check.
2341  *
2342  * RETURNS
2343  *  TRUE   If lpszPath is a valid UNC server share.
2344  *  FALSE  Otherwise.
2345  *
2346  * NOTES
2347  *  This routine is bug compatible with Win32: Server shares with a
2348  *  trailing backslash (e.g. "\\FOO\BAR\"), return FALSE incorrectly.
2349  *  Fixing this bug may break other shlwapi functions!
2350  */
PathIsUNCServerShareA(LPCSTR lpszPath)2351 BOOL WINAPI PathIsUNCServerShareA(LPCSTR lpszPath)
2352 {
2353   TRACE("(%s)\n", debugstr_a(lpszPath));
2354 
2355   if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2356   {
2357     BOOL bSeenSlash = FALSE;
2358     while (*lpszPath)
2359     {
2360       if (*lpszPath == '\\')
2361       {
2362         if (bSeenSlash)
2363           return FALSE;
2364         bSeenSlash = TRUE;
2365       }
2366       lpszPath = CharNextA(lpszPath);
2367     }
2368     return bSeenSlash;
2369   }
2370   return FALSE;
2371 }
2372 
2373 /*************************************************************************
2374  * PathIsUNCServerShareW   [SHLWAPI.@]
2375  *
2376  * See PathIsUNCServerShareA.
2377  */
PathIsUNCServerShareW(LPCWSTR lpszPath)2378 BOOL WINAPI PathIsUNCServerShareW(LPCWSTR lpszPath)
2379 {
2380   TRACE("(%s)\n", debugstr_w(lpszPath));
2381 
2382   if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2383   {
2384     BOOL bSeenSlash = FALSE;
2385     while (*lpszPath)
2386     {
2387       if (*lpszPath == '\\')
2388       {
2389         if (bSeenSlash)
2390           return FALSE;
2391         bSeenSlash = TRUE;
2392       }
2393       lpszPath++;
2394     }
2395     return bSeenSlash;
2396   }
2397   return FALSE;
2398 }
2399 
2400 /*************************************************************************
2401  * PathCanonicalizeA   [SHLWAPI.@]
2402  *
2403  * Convert a path to its canonical form.
2404  *
2405  * PARAMS
2406  *  lpszBuf  [O] Output path
2407  *  lpszPath [I] Path to canonicalize
2408  *
2409  * RETURNS
2410  *  Success: TRUE.  lpszBuf contains the output path,
2411  *  Failure: FALSE, If input path is invalid. lpszBuf is undefined
2412  */
PathCanonicalizeA(LPSTR lpszBuf,LPCSTR lpszPath)2413 BOOL WINAPI PathCanonicalizeA(LPSTR lpszBuf, LPCSTR lpszPath)
2414 {
2415   BOOL bRet = FALSE;
2416 
2417   TRACE("(%p,%s)\n", lpszBuf, debugstr_a(lpszPath));
2418 
2419   if (lpszBuf)
2420     *lpszBuf = '\0';
2421 
2422   if (!lpszBuf || !lpszPath)
2423     SetLastError(ERROR_INVALID_PARAMETER);
2424   else
2425   {
2426     WCHAR szPath[MAX_PATH];
2427     WCHAR szBuff[MAX_PATH];
2428     int ret = MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
2429 
2430     if (!ret) {
2431 	WARN("Failed to convert string to widechar (too long?), LE %d.\n", GetLastError());
2432 	return FALSE;
2433     }
2434     bRet = PathCanonicalizeW(szBuff, szPath);
2435     WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszBuf,MAX_PATH,0,0);
2436   }
2437   return bRet;
2438 }
2439 
2440 
2441 /*************************************************************************
2442  * PathCanonicalizeW   [SHLWAPI.@]
2443  *
2444  * See PathCanonicalizeA.
2445  */
PathCanonicalizeW(LPWSTR lpszBuf,LPCWSTR lpszPath)2446 BOOL WINAPI PathCanonicalizeW(LPWSTR lpszBuf, LPCWSTR lpszPath)
2447 {
2448   LPWSTR lpszDst = lpszBuf;
2449   LPCWSTR lpszSrc = lpszPath;
2450 
2451   TRACE("(%p,%s)\n", lpszBuf, debugstr_w(lpszPath));
2452 
2453   if (lpszBuf)
2454     *lpszDst = '\0';
2455 
2456   if (!lpszBuf || !lpszPath)
2457   {
2458     SetLastError(ERROR_INVALID_PARAMETER);
2459     return FALSE;
2460   }
2461 
2462   if (!*lpszPath)
2463   {
2464     *lpszBuf++ = '\\';
2465     *lpszBuf = '\0';
2466     return TRUE;
2467   }
2468 
2469   /* Copy path root */
2470   if (*lpszSrc == '\\')
2471   {
2472     *lpszDst++ = *lpszSrc++;
2473   }
2474   else if (*lpszSrc && lpszSrc[1] == ':')
2475   {
2476     /* X:\ */
2477     *lpszDst++ = *lpszSrc++;
2478     *lpszDst++ = *lpszSrc++;
2479     if (*lpszSrc == '\\')
2480       *lpszDst++ = *lpszSrc++;
2481   }
2482 
2483   /* Canonicalize the rest of the path */
2484   while (*lpszSrc)
2485   {
2486     if (*lpszSrc == '.')
2487     {
2488       if (lpszSrc[1] == '\\' && (lpszSrc == lpszPath || lpszSrc[-1] == '\\' || lpszSrc[-1] == ':'))
2489       {
2490         lpszSrc += 2; /* Skip .\ */
2491       }
2492       else if (lpszSrc[1] == '.' && (lpszDst == lpszBuf || lpszDst[-1] == '\\'))
2493       {
2494         /* \.. backs up a directory, over the root if it has no \ following X:.
2495          * .. is ignored if it would remove a UNC server name or initial \\
2496          */
2497         if (lpszDst != lpszBuf)
2498         {
2499           *lpszDst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */
2500           if (lpszDst > lpszBuf+1 && lpszDst[-1] == '\\' &&
2501              (lpszDst[-2] != '\\' || lpszDst > lpszBuf+2))
2502           {
2503             if (lpszDst[-2] == ':' && (lpszDst > lpszBuf+3 || lpszDst[-3] == ':'))
2504             {
2505               lpszDst -= 2;
2506               while (lpszDst > lpszBuf && *lpszDst != '\\')
2507                 lpszDst--;
2508               if (*lpszDst == '\\')
2509                 lpszDst++; /* Reset to last '\' */
2510               else
2511                 lpszDst = lpszBuf; /* Start path again from new root */
2512             }
2513             else if (lpszDst[-2] != ':' && !PathIsUNCServerShareW(lpszBuf))
2514               lpszDst -= 2;
2515           }
2516           while (lpszDst > lpszBuf && *lpszDst != '\\')
2517             lpszDst--;
2518           if (lpszDst == lpszBuf)
2519           {
2520             *lpszDst++ = '\\';
2521             lpszSrc++;
2522           }
2523         }
2524         lpszSrc += 2; /* Skip .. in src path */
2525       }
2526       else
2527         *lpszDst++ = *lpszSrc++;
2528     }
2529     else
2530       *lpszDst++ = *lpszSrc++;
2531   }
2532   /* Append \ to naked drive specs */
2533   if (lpszDst - lpszBuf == 2 && lpszDst[-1] == ':')
2534     *lpszDst++ = '\\';
2535   *lpszDst++ = '\0';
2536   return TRUE;
2537 }
2538 
2539 /*************************************************************************
2540  * PathFindNextComponentA   [SHLWAPI.@]
2541  *
2542  * Find the next component in a path.
2543  *
2544  * PARAMS
2545  *   lpszPath [I] Path to find next component in
2546  *
2547  * RETURNS
2548  *  Success: A pointer to the next component, or the end of the string.
2549  *  Failure: NULL, If lpszPath is invalid
2550  *
2551  * NOTES
2552  *  A 'component' is either a backslash character (\) or UNC marker (\\).
2553  *  Because of this, relative paths (e.g "c:foo") are regarded as having
2554  *  only one component.
2555  */
PathFindNextComponentA(LPCSTR lpszPath)2556 LPSTR WINAPI PathFindNextComponentA(LPCSTR lpszPath)
2557 {
2558   LPSTR lpszSlash;
2559 
2560   TRACE("(%s)\n", debugstr_a(lpszPath));
2561 
2562   if(!lpszPath || !*lpszPath)
2563     return NULL;
2564 
2565   if ((lpszSlash = StrChrA(lpszPath, '\\')))
2566   {
2567     if (lpszSlash[1] == '\\')
2568       lpszSlash++;
2569     return lpszSlash + 1;
2570   }
2571   return (LPSTR)lpszPath + strlen(lpszPath);
2572 }
2573 
2574 /*************************************************************************
2575  * PathFindNextComponentW   [SHLWAPI.@]
2576  *
2577  * See PathFindNextComponentA.
2578  */
PathFindNextComponentW(LPCWSTR lpszPath)2579 LPWSTR WINAPI PathFindNextComponentW(LPCWSTR lpszPath)
2580 {
2581   LPWSTR lpszSlash;
2582 
2583   TRACE("(%s)\n", debugstr_w(lpszPath));
2584 
2585   if(!lpszPath || !*lpszPath)
2586     return NULL;
2587 
2588   if ((lpszSlash = StrChrW(lpszPath, '\\')))
2589   {
2590     if (lpszSlash[1] == '\\')
2591       lpszSlash++;
2592     return lpszSlash + 1;
2593   }
2594   return (LPWSTR)lpszPath + strlenW(lpszPath);
2595 }
2596 
2597 /*************************************************************************
2598  * PathAddExtensionA   [SHLWAPI.@]
2599  *
2600  * Add a file extension to a path
2601  *
2602  * PARAMS
2603  *  lpszPath      [I/O] Path to add extension to
2604  *  lpszExtension [I]   Extension to add to lpszPath
2605  *
2606  * RETURNS
2607  *  TRUE  If the path was modified,
2608  *  FALSE If lpszPath or lpszExtension are invalid, lpszPath has an
2609  *        extension already, or the new path length is too big.
2610  *
2611  * FIXME
2612  *  What version of shlwapi.dll adds "exe" if lpszExtension is NULL? Win2k
2613  *  does not do this, so the behaviour was removed.
2614  */
PathAddExtensionA(LPSTR lpszPath,LPCSTR lpszExtension)2615 BOOL WINAPI PathAddExtensionA(LPSTR lpszPath, LPCSTR lpszExtension)
2616 {
2617   size_t dwLen;
2618 
2619   TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExtension));
2620 
2621   if (!lpszPath || !lpszExtension || *(PathFindExtensionA(lpszPath)))
2622     return FALSE;
2623 
2624   dwLen = strlen(lpszPath);
2625 
2626   if (dwLen + strlen(lpszExtension) >= MAX_PATH)
2627     return FALSE;
2628 
2629   strcpy(lpszPath + dwLen, lpszExtension);
2630   return TRUE;
2631 }
2632 
2633 /*************************************************************************
2634  * PathAddExtensionW   [SHLWAPI.@]
2635  *
2636  * See PathAddExtensionA.
2637  */
PathAddExtensionW(LPWSTR lpszPath,LPCWSTR lpszExtension)2638 BOOL WINAPI PathAddExtensionW(LPWSTR lpszPath, LPCWSTR lpszExtension)
2639 {
2640   size_t dwLen;
2641 
2642   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExtension));
2643 
2644   if (!lpszPath || !lpszExtension || *(PathFindExtensionW(lpszPath)))
2645     return FALSE;
2646 
2647   dwLen = strlenW(lpszPath);
2648 
2649   if (dwLen + strlenW(lpszExtension) >= MAX_PATH)
2650     return FALSE;
2651 
2652   strcpyW(lpszPath + dwLen, lpszExtension);
2653   return TRUE;
2654 }
2655 
2656 /*************************************************************************
2657  * PathMakePrettyA   [SHLWAPI.@]
2658  *
2659  * Convert an uppercase DOS filename into lowercase.
2660  *
2661  * PARAMS
2662  *  lpszPath [I/O] Path to convert.
2663  *
2664  * RETURNS
2665  *  TRUE  If the path was an uppercase DOS path and was converted,
2666  *  FALSE Otherwise.
2667  */
PathMakePrettyA(LPSTR lpszPath)2668 BOOL WINAPI PathMakePrettyA(LPSTR lpszPath)
2669 {
2670   LPSTR pszIter = lpszPath;
2671 
2672   TRACE("(%s)\n", debugstr_a(lpszPath));
2673 
2674   if (!pszIter)
2675     return FALSE;
2676 
2677   if (*pszIter)
2678   {
2679     do
2680     {
2681       if (islower(*pszIter) || IsDBCSLeadByte(*pszIter))
2682         return FALSE; /* Not DOS path */
2683       pszIter++;
2684     } while (*pszIter);
2685     pszIter = lpszPath + 1;
2686     while (*pszIter)
2687     {
2688       *pszIter = tolower(*pszIter);
2689       pszIter++;
2690     }
2691   }
2692   return TRUE;
2693 }
2694 
2695 /*************************************************************************
2696  * PathMakePrettyW   [SHLWAPI.@]
2697  *
2698  * See PathMakePrettyA.
2699  */
PathMakePrettyW(LPWSTR lpszPath)2700 BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath)
2701 {
2702   LPWSTR pszIter = lpszPath;
2703 
2704   TRACE("(%s)\n", debugstr_w(lpszPath));
2705 
2706   if (!pszIter)
2707     return FALSE;
2708 
2709   if (*pszIter)
2710   {
2711     do
2712     {
2713       if (islowerW(*pszIter))
2714         return FALSE; /* Not DOS path */
2715       pszIter++;
2716     } while (*pszIter);
2717     pszIter = lpszPath + 1;
2718     while (*pszIter)
2719     {
2720       *pszIter = tolowerW(*pszIter);
2721       pszIter++;
2722     }
2723   }
2724   return TRUE;
2725 }
2726 
2727 /*************************************************************************
2728  * PathCommonPrefixA   [SHLWAPI.@]
2729  *
2730  * Determine the length of the common prefix between two paths.
2731  *
2732  * PARAMS
2733  *  lpszFile1 [I] First path for comparison
2734  *  lpszFile2 [I] Second path for comparison
2735  *  achPath   [O] Destination for common prefix string
2736  *
2737  * RETURNS
2738  *  The length of the common prefix. This is 0 if there is no common
2739  *  prefix between the paths or if any parameters are invalid. If the prefix
2740  *  is non-zero and achPath is not NULL, achPath is filled with the common
2741  *  part of the prefix and NUL terminated.
2742  *
2743  * NOTES
2744  *  A common prefix of 2 is always returned as 3. It is thus possible for
2745  *  the length returned to be invalid (i.e. Longer than one or both of the
2746  *  strings given as parameters). This Win32 behaviour has been implemented
2747  *  here, and cannot be changed (fixed?) without breaking other SHLWAPI calls.
2748  *  To work around this when using this function, always check that the byte
2749  *  at [common_prefix_len-1] is not a NUL. If it is, deduct 1 from the prefix.
2750  */
PathCommonPrefixA(LPCSTR lpszFile1,LPCSTR lpszFile2,LPSTR achPath)2751 int WINAPI PathCommonPrefixA(LPCSTR lpszFile1, LPCSTR lpszFile2, LPSTR achPath)
2752 {
2753   size_t iLen = 0;
2754   LPCSTR lpszIter1 = lpszFile1;
2755   LPCSTR lpszIter2 = lpszFile2;
2756 
2757   TRACE("(%s,%s,%p)\n", debugstr_a(lpszFile1), debugstr_a(lpszFile2), achPath);
2758 
2759   if (achPath)
2760     *achPath = '\0';
2761 
2762   if (!lpszFile1 || !lpszFile2)
2763     return 0;
2764 
2765   /* Handle roots first */
2766   if (PathIsUNCA(lpszFile1))
2767   {
2768     if (!PathIsUNCA(lpszFile2))
2769       return 0;
2770     lpszIter1 += 2;
2771     lpszIter2 += 2;
2772   }
2773   else if (PathIsUNCA(lpszFile2))
2774       return 0; /* Know already lpszFile1 is not UNC */
2775 
2776   do
2777   {
2778     /* Update len */
2779     if ((!*lpszIter1 || *lpszIter1 == '\\') &&
2780         (!*lpszIter2 || *lpszIter2 == '\\'))
2781       iLen = lpszIter1 - lpszFile1; /* Common to this point */
2782 
2783     if (!*lpszIter1 || (tolower(*lpszIter1) != tolower(*lpszIter2)))
2784       break; /* Strings differ at this point */
2785 
2786     lpszIter1++;
2787     lpszIter2++;
2788   } while (1);
2789 
2790   if (iLen == 2)
2791     iLen++; /* Feature/Bug compatible with Win32 */
2792 
2793   if (iLen && achPath)
2794   {
2795     memcpy(achPath,lpszFile1,iLen);
2796     achPath[iLen] = '\0';
2797   }
2798   return iLen;
2799 }
2800 
2801 /*************************************************************************
2802  * PathCommonPrefixW   [SHLWAPI.@]
2803  *
2804  * See PathCommonPrefixA.
2805  */
PathCommonPrefixW(LPCWSTR lpszFile1,LPCWSTR lpszFile2,LPWSTR achPath)2806 int WINAPI PathCommonPrefixW(LPCWSTR lpszFile1, LPCWSTR lpszFile2, LPWSTR achPath)
2807 {
2808   size_t iLen = 0;
2809   LPCWSTR lpszIter1 = lpszFile1;
2810   LPCWSTR lpszIter2 = lpszFile2;
2811 
2812   TRACE("(%s,%s,%p)\n", debugstr_w(lpszFile1), debugstr_w(lpszFile2), achPath);
2813 
2814   if (achPath)
2815     *achPath = '\0';
2816 
2817   if (!lpszFile1 || !lpszFile2)
2818     return 0;
2819 
2820   /* Handle roots first */
2821   if (PathIsUNCW(lpszFile1))
2822   {
2823     if (!PathIsUNCW(lpszFile2))
2824       return 0;
2825     lpszIter1 += 2;
2826     lpszIter2 += 2;
2827   }
2828   else if (PathIsUNCW(lpszFile2))
2829       return 0; /* Know already lpszFile1 is not UNC */
2830 
2831   do
2832   {
2833     /* Update len */
2834     if ((!*lpszIter1 || *lpszIter1 == '\\') &&
2835         (!*lpszIter2 || *lpszIter2 == '\\'))
2836       iLen = lpszIter1 - lpszFile1; /* Common to this point */
2837 
2838     if (!*lpszIter1 || (tolowerW(*lpszIter1) != tolowerW(*lpszIter2)))
2839       break; /* Strings differ at this point */
2840 
2841     lpszIter1++;
2842     lpszIter2++;
2843   } while (1);
2844 
2845   if (iLen == 2)
2846     iLen++; /* Feature/Bug compatible with Win32 */
2847 
2848   if (iLen && achPath)
2849   {
2850     memcpy(achPath,lpszFile1,iLen * sizeof(WCHAR));
2851     achPath[iLen] = '\0';
2852   }
2853   return iLen;
2854 }
2855 
2856 /*************************************************************************
2857  * PathCompactPathA   [SHLWAPI.@]
2858  *
2859  * Make a path fit into a given width when printed to a DC.
2860  *
2861  * PARAMS
2862  *  hDc      [I]   Destination DC
2863  *  lpszPath [I/O] Path to be printed to hDc
2864  *  dx       [I]   Desired width
2865  *
2866  * RETURNS
2867  *  TRUE  If the path was modified/went well.
2868  *  FALSE Otherwise.
2869  */
PathCompactPathA(HDC hDC,LPSTR lpszPath,UINT dx)2870 BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx)
2871 {
2872   BOOL bRet = FALSE;
2873 
2874   TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx);
2875 
2876   if (lpszPath)
2877   {
2878     WCHAR szPath[MAX_PATH];
2879     MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
2880     bRet = PathCompactPathW(hDC, szPath, dx);
2881     WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
2882   }
2883   return bRet;
2884 }
2885 
2886 /*************************************************************************
2887  * PathCompactPathW   [SHLWAPI.@]
2888  *
2889  * See PathCompactPathA.
2890  */
PathCompactPathW(HDC hDC,LPWSTR lpszPath,UINT dx)2891 BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx)
2892 {
2893   static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
2894   BOOL bRet = TRUE;
2895   HDC hdc = 0;
2896   WCHAR buff[MAX_PATH];
2897   SIZE size;
2898   DWORD dwLen;
2899 
2900   TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx);
2901 
2902   if (!lpszPath)
2903     return FALSE;
2904 
2905   if (!hDC)
2906     hdc = hDC = GetDC(0);
2907 
2908   /* Get the length of the whole path */
2909   dwLen = strlenW(lpszPath);
2910   GetTextExtentPointW(hDC, lpszPath, dwLen, &size);
2911 
2912   if ((UINT)size.cx > dx)
2913   {
2914     /* Path too big, must reduce it */
2915     LPWSTR sFile;
2916     DWORD dwEllipsesLen = 0, dwPathLen = 0;
2917 
2918     sFile = PathFindFileNameW(lpszPath);
2919     if (sFile != lpszPath) sFile--;
2920 
2921     /* Get the size of ellipses */
2922     GetTextExtentPointW(hDC, szEllipses, 3, &size);
2923     dwEllipsesLen = size.cx;
2924     /* Get the size of the file name */
2925     GetTextExtentPointW(hDC, sFile, strlenW(sFile), &size);
2926     dwPathLen = size.cx;
2927 
2928     if (sFile != lpszPath)
2929     {
2930       LPWSTR sPath = sFile;
2931       BOOL bEllipses = FALSE;
2932 
2933       /* The path includes a file name. Include as much of the path prior to
2934        * the file name as possible, allowing for the ellipses, e.g:
2935        * c:\some very long path\filename ==> c:\some v...\filename
2936        */
2937       lstrcpynW(buff, sFile, MAX_PATH);
2938 
2939       do
2940       {
2941         DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen;
2942 
2943         GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size);
2944         dwTotalLen += size.cx;
2945         if (dwTotalLen <= dx)
2946           break;
2947         sPath--;
2948         if (!bEllipses)
2949         {
2950           bEllipses = TRUE;
2951           sPath -= 2;
2952         }
2953       } while (sPath > lpszPath);
2954 
2955       if (sPath > lpszPath)
2956       {
2957         if (bEllipses)
2958         {
2959           strcpyW(sPath, szEllipses);
2960           strcpyW(sPath+3, buff);
2961         }
2962         bRet = TRUE;
2963         goto end;
2964       }
2965       strcpyW(lpszPath, szEllipses);
2966       strcpyW(lpszPath+3, buff);
2967       bRet = FALSE;
2968       goto end;
2969     }
2970 
2971     /* Trim the path by adding ellipses to the end, e.g:
2972      * A very long file name.txt ==> A very...
2973      */
2974     dwLen = strlenW(lpszPath);
2975 
2976     if (dwLen > MAX_PATH - 3)
2977       dwLen =  MAX_PATH - 3;
2978     lstrcpynW(buff, sFile, dwLen);
2979 
2980     do {
2981       dwLen--;
2982       GetTextExtentPointW(hDC, buff, dwLen, &size);
2983     } while (dwLen && size.cx + dwEllipsesLen > dx);
2984 
2985    if (!dwLen)
2986    {
2987      DWORD dwWritten = 0;
2988 
2989      dwEllipsesLen /= 3; /* Size of a single '.' */
2990 
2991      /* Write as much of the Ellipses string as possible */
2992      while (dwWritten + dwEllipsesLen < dx && dwLen < 3)
2993      {
2994        *lpszPath++ = '.';
2995        dwWritten += dwEllipsesLen;
2996        dwLen++;
2997      }
2998      *lpszPath = '\0';
2999      bRet = FALSE;
3000    }
3001    else
3002    {
3003      strcpyW(buff + dwLen, szEllipses);
3004      strcpyW(lpszPath, buff);
3005     }
3006   }
3007 
3008 end:
3009   if (hdc)
3010     ReleaseDC(0, hdc);
3011 
3012   return bRet;
3013 }
3014 
3015 /*************************************************************************
3016  * PathGetCharTypeA   [SHLWAPI.@]
3017  *
3018  * Categorise a character from a file path.
3019  *
3020  * PARAMS
3021  *  ch [I] Character to get the type of
3022  *
3023  * RETURNS
3024  *  A set of GCT_ bit flags (from "shlwapi.h") indicating the character type.
3025  */
PathGetCharTypeA(UCHAR ch)3026 UINT WINAPI PathGetCharTypeA(UCHAR ch)
3027 {
3028   return PathGetCharTypeW(ch);
3029 }
3030 
3031 /*************************************************************************
3032  * PathGetCharTypeW   [SHLWAPI.@]
3033  *
3034  * See PathGetCharTypeA.
3035  */
PathGetCharTypeW(WCHAR ch)3036 UINT WINAPI PathGetCharTypeW(WCHAR ch)
3037 {
3038   UINT flags = 0;
3039 
3040   TRACE("(%d)\n", ch);
3041 
3042   if (!ch || ch < ' ' || ch == '<' || ch == '>' ||
3043       ch == '"' || ch == '|' || ch == '/')
3044     flags = GCT_INVALID; /* Invalid */
3045   else if (ch == '*' || ch=='?')
3046     flags = GCT_WILD; /* Wildchars */
3047   else if ((ch == '\\') || (ch == ':'))
3048     return GCT_SEPARATOR; /* Path separators */
3049   else
3050   {
3051      if (ch < 126)
3052      {
3053          if (((ch & 0x1) && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' ||
3054             ch == '.' || ch == '@' || ch == '^' ||
3055             ch == '\'' || ch == 130 || ch == '`')
3056          flags |= GCT_SHORTCHAR; /* All these are valid for DOS */
3057      }
3058      else
3059        flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */
3060      flags |= GCT_LFNCHAR; /* Valid for long file names */
3061   }
3062   return flags;
3063 }
3064 
3065 /*************************************************************************
3066  * SHLWAPI_UseSystemForSystemFolders
3067  *
3068  * Internal helper for PathMakeSystemFolderW.
3069  */
SHLWAPI_UseSystemForSystemFolders(void)3070 static BOOL SHLWAPI_UseSystemForSystemFolders(void)
3071 {
3072   static BOOL bCheckedReg = FALSE;
3073   static BOOL bUseSystemForSystemFolders = FALSE;
3074 
3075   if (!bCheckedReg)
3076   {
3077     bCheckedReg = TRUE;
3078 
3079     /* Key tells Win what file attributes to use on system folders */
3080     if (SHGetValueA(HKEY_LOCAL_MACHINE,
3081         "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
3082         "UseSystemForSystemFolders", 0, 0, 0))
3083       bUseSystemForSystemFolders = TRUE;
3084   }
3085   return bUseSystemForSystemFolders;
3086 }
3087 
3088 /*************************************************************************
3089  * PathMakeSystemFolderA   [SHLWAPI.@]
3090  *
3091  * Set system folder attribute for a path.
3092  *
3093  * PARAMS
3094  *  lpszPath [I] The path to turn into a system folder
3095  *
3096  * RETURNS
3097  *  TRUE  If the path was changed to/already was a system folder
3098  *  FALSE If the path is invalid or SetFileAttributesA() fails
3099  */
PathMakeSystemFolderA(LPCSTR lpszPath)3100 BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath)
3101 {
3102   BOOL bRet = FALSE;
3103 
3104   TRACE("(%s)\n", debugstr_a(lpszPath));
3105 
3106   if (lpszPath && *lpszPath)
3107   {
3108     WCHAR szPath[MAX_PATH];
3109     MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
3110     bRet = PathMakeSystemFolderW(szPath);
3111   }
3112   return bRet;
3113 }
3114 
3115 /*************************************************************************
3116  * PathMakeSystemFolderW   [SHLWAPI.@]
3117  *
3118  * See PathMakeSystemFolderA.
3119  */
PathMakeSystemFolderW(LPCWSTR lpszPath)3120 BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath)
3121 {
3122   DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr;
3123   WCHAR buff[MAX_PATH];
3124 
3125   TRACE("(%s)\n", debugstr_w(lpszPath));
3126 
3127   if (!lpszPath || !*lpszPath)
3128     return FALSE;
3129 
3130   /* If the directory is already a system directory, don't do anything */
3131   GetSystemDirectoryW(buff, MAX_PATH);
3132   if (!strcmpW(buff, lpszPath))
3133     return TRUE;
3134 
3135   GetWindowsDirectoryW(buff, MAX_PATH);
3136   if (!strcmpW(buff, lpszPath))
3137     return TRUE;
3138 
3139   /* "UseSystemForSystemFolders" Tells Win what attributes to use */
3140   if (SHLWAPI_UseSystemForSystemFolders())
3141     dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM;
3142 
3143   if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
3144     return FALSE;
3145 
3146   /* Change file attributes to system attributes */
3147   dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
3148   return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr);
3149 }
3150 
3151 /*************************************************************************
3152  * PathRenameExtensionA   [SHLWAPI.@]
3153  *
3154  * Swap the file extension in a path with another extension.
3155  *
3156  * PARAMS
3157  *  lpszPath [I/O] Path to swap the extension in
3158  *  lpszExt  [I]   The new extension
3159  *
3160  * RETURNS
3161  *  TRUE  if lpszPath was modified,
3162  *  FALSE if lpszPath or lpszExt is NULL, or the new path is too long
3163  */
PathRenameExtensionA(LPSTR lpszPath,LPCSTR lpszExt)3164 BOOL WINAPI PathRenameExtensionA(LPSTR lpszPath, LPCSTR lpszExt)
3165 {
3166   LPSTR lpszExtension;
3167 
3168   TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExt));
3169 
3170   lpszExtension = PathFindExtensionA(lpszPath);
3171 
3172   if (!lpszExtension || (lpszExtension - lpszPath + strlen(lpszExt) >= MAX_PATH))
3173     return FALSE;
3174 
3175   strcpy(lpszExtension, lpszExt);
3176   return TRUE;
3177 }
3178 
3179 /*************************************************************************
3180  * PathRenameExtensionW   [SHLWAPI.@]
3181  *
3182  * See PathRenameExtensionA.
3183  */
PathRenameExtensionW(LPWSTR lpszPath,LPCWSTR lpszExt)3184 BOOL WINAPI PathRenameExtensionW(LPWSTR lpszPath, LPCWSTR lpszExt)
3185 {
3186   LPWSTR lpszExtension;
3187 
3188   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExt));
3189 
3190   lpszExtension = PathFindExtensionW(lpszPath);
3191 
3192   if (!lpszExtension || (lpszExtension - lpszPath + strlenW(lpszExt) >= MAX_PATH))
3193     return FALSE;
3194 
3195   strcpyW(lpszExtension, lpszExt);
3196   return TRUE;
3197 }
3198 
3199 /*************************************************************************
3200  * PathSearchAndQualifyA   [SHLWAPI.@]
3201  *
3202  * Determine if a given path is correct and fully qualified.
3203  *
3204  * PARAMS
3205  *  lpszPath [I] Path to check
3206  *  lpszBuf  [O] Output for correct path
3207  *  cchBuf   [I] Size of lpszBuf
3208  *
3209  * RETURNS
3210  *  Unknown.
3211  */
PathSearchAndQualifyA(LPCSTR lpszPath,LPSTR lpszBuf,UINT cchBuf)3212 BOOL WINAPI PathSearchAndQualifyA(LPCSTR lpszPath, LPSTR lpszBuf, UINT cchBuf)
3213 {
3214     TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszPath), lpszBuf, cchBuf);
3215 
3216     if(SearchPathA(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL))
3217         return TRUE;
3218     return !!GetFullPathNameA(lpszPath, cchBuf, lpszBuf, NULL);
3219 }
3220 
3221 /*************************************************************************
3222  * PathSearchAndQualifyW   [SHLWAPI.@]
3223  *
3224  * See PathSearchAndQualifyA.
3225  */
PathSearchAndQualifyW(LPCWSTR lpszPath,LPWSTR lpszBuf,UINT cchBuf)3226 BOOL WINAPI PathSearchAndQualifyW(LPCWSTR lpszPath, LPWSTR lpszBuf, UINT cchBuf)
3227 {
3228     TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszPath), lpszBuf, cchBuf);
3229 
3230     if(SearchPathW(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL))
3231         return TRUE;
3232     return !!GetFullPathNameW(lpszPath, cchBuf, lpszBuf, NULL);
3233 }
3234 
3235 /*************************************************************************
3236  * PathSkipRootA   [SHLWAPI.@]
3237  *
3238  * Return the portion of a path following the drive letter or mount point.
3239  *
3240  * PARAMS
3241  *  lpszPath [I] The path to skip on
3242  *
3243  * RETURNS
3244  *  Success: A pointer to the next character after the root.
3245  *  Failure: NULL, if lpszPath is invalid, has no root or is a multibyte string.
3246  */
PathSkipRootA(LPCSTR lpszPath)3247 LPSTR WINAPI PathSkipRootA(LPCSTR lpszPath)
3248 {
3249   TRACE("(%s)\n", debugstr_a(lpszPath));
3250 
3251   if (!lpszPath || !*lpszPath)
3252     return NULL;
3253 
3254   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3255   {
3256     /* Network share: skip share server and mount point */
3257     lpszPath += 2;
3258     if ((lpszPath = StrChrA(lpszPath, '\\')) &&
3259         (lpszPath = StrChrA(lpszPath + 1, '\\')))
3260       lpszPath++;
3261     return (LPSTR)lpszPath;
3262   }
3263 
3264   if (IsDBCSLeadByte(*lpszPath))
3265     return NULL;
3266 
3267   /* Check x:\ */
3268   if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\')
3269     return (LPSTR)lpszPath + 3;
3270   return NULL;
3271 }
3272 
3273 /*************************************************************************
3274  * PathSkipRootW   [SHLWAPI.@]
3275  *
3276  * See PathSkipRootA.
3277  */
PathSkipRootW(LPCWSTR lpszPath)3278 LPWSTR WINAPI PathSkipRootW(LPCWSTR lpszPath)
3279 {
3280   TRACE("(%s)\n", debugstr_w(lpszPath));
3281 
3282   if (!lpszPath || !*lpszPath)
3283     return NULL;
3284 
3285   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3286   {
3287     /* Network share: skip share server and mount point */
3288     lpszPath += 2;
3289     if ((lpszPath = StrChrW(lpszPath, '\\')) &&
3290         (lpszPath = StrChrW(lpszPath + 1, '\\')))
3291      lpszPath++;
3292     return (LPWSTR)lpszPath;
3293   }
3294 
3295   /* Check x:\ */
3296   if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\')
3297     return (LPWSTR)lpszPath + 3;
3298   return NULL;
3299 }
3300 
3301 /*************************************************************************
3302  * PathCreateFromUrlA   [SHLWAPI.@]
3303  *
3304  * See PathCreateFromUrlW
3305  */
PathCreateFromUrlA(LPCSTR pszUrl,LPSTR pszPath,LPDWORD pcchPath,DWORD dwReserved)3306 HRESULT WINAPI PathCreateFromUrlA(LPCSTR pszUrl, LPSTR pszPath,
3307                                   LPDWORD pcchPath, DWORD dwReserved)
3308 {
3309     WCHAR bufW[MAX_PATH];
3310     WCHAR *pathW = bufW;
3311     UNICODE_STRING urlW;
3312     HRESULT ret;
3313     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
3314 
3315     if (!pszUrl || !pszPath || !pcchPath || !*pcchPath)
3316         return E_INVALIDARG;
3317 
3318     if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
3319         return E_INVALIDARG;
3320     if((ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved)) == E_POINTER) {
3321         pathW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
3322         ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved);
3323     }
3324     if(ret == S_OK) {
3325         RtlUnicodeToMultiByteSize(&lenA, pathW, lenW * sizeof(WCHAR));
3326         if(*pcchPath > lenA) {
3327             RtlUnicodeToMultiByteN(pszPath, *pcchPath - 1, &lenA, pathW, lenW * sizeof(WCHAR));
3328             pszPath[lenA] = 0;
3329             *pcchPath = lenA;
3330         } else {
3331             *pcchPath = lenA + 1;
3332             ret = E_POINTER;
3333         }
3334     }
3335     if(pathW != bufW) HeapFree(GetProcessHeap(), 0, pathW);
3336     RtlFreeUnicodeString(&urlW);
3337     return ret;
3338 }
3339 
3340 /*************************************************************************
3341  * PathCreateFromUrlW   [SHLWAPI.@]
3342  *
3343  * Create a path from a URL
3344  *
3345  * PARAMS
3346  *  lpszUrl  [I] URL to convert into a path
3347  *  lpszPath [O] Output buffer for the resulting Path
3348  *  pcchPath [I] Length of lpszPath
3349  *  dwFlags  [I] Flags controlling the conversion
3350  *
3351  * RETURNS
3352  *  Success: S_OK. lpszPath contains the URL in path format,
3353  *  Failure: An HRESULT error code such as E_INVALIDARG.
3354  */
PathCreateFromUrlW(LPCWSTR pszUrl,LPWSTR pszPath,LPDWORD pcchPath,DWORD dwReserved)3355 HRESULT WINAPI PathCreateFromUrlW(LPCWSTR pszUrl, LPWSTR pszPath,
3356                                   LPDWORD pcchPath, DWORD dwReserved)
3357 {
3358     static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
3359     static const WCHAR localhost[] = { 'l','o','c','a','l','h','o','s','t',0 };
3360     DWORD nslashes, unescape, len;
3361     const WCHAR *src;
3362     WCHAR *tpath, *dst;
3363     HRESULT ret;
3364 
3365     TRACE("(%s,%p,%p,0x%08x)\n", debugstr_w(pszUrl), pszPath, pcchPath, dwReserved);
3366 
3367     if (!pszUrl || !pszPath || !pcchPath || !*pcchPath)
3368         return E_INVALIDARG;
3369 
3370     if (lstrlenW(pszUrl) < 5)
3371         return E_INVALIDARG;
3372 
3373     if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
3374                        file_colon, 5) != CSTR_EQUAL)
3375         return E_INVALIDARG;
3376     pszUrl += 5;
3377     ret = S_OK;
3378 
3379     src = pszUrl;
3380     nslashes = 0;
3381     while (*src == '/' || *src == '\\') {
3382         nslashes++;
3383         src++;
3384     }
3385 
3386     /* We need a temporary buffer so we can compute what size to ask for.
3387      * We know that the final string won't be longer than the current pszUrl
3388      * plus at most two backslashes. All the other transformations make it
3389      * shorter.
3390      */
3391     len = 2 + lstrlenW(pszUrl) + 1;
3392     if (*pcchPath < len)
3393         tpath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3394     else
3395         tpath = pszPath;
3396 
3397     len = 0;
3398     dst = tpath;
3399     unescape = 1;
3400     switch (nslashes)
3401     {
3402     case 0:
3403         /* 'file:' + escaped DOS path */
3404         break;
3405     case 1:
3406         /* 'file:/' + escaped DOS path */
3407         /* fall through */
3408     case 3:
3409         /* 'file:///' (implied localhost) + escaped DOS path */
3410         if (!isalphaW(*src) || (src[1] != ':' && src[1] != '|'))
3411             src -= 1;
3412         break;
3413     case 2:
3414         if (lstrlenW(src) >= 10 && CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE,
3415             src, 9, localhost, 9) == CSTR_EQUAL && (src[9] == '/' || src[9] == '\\'))
3416         {
3417             /* 'file://localhost/' + escaped DOS path */
3418             src += 10;
3419         }
3420         else if (isalphaW(*src) && (src[1] == ':' || src[1] == '|'))
3421         {
3422             /* 'file://' + unescaped DOS path */
3423             unescape = 0;
3424         }
3425         else
3426         {
3427             /*    'file://hostname:port/path' (where path is escaped)
3428              * or 'file:' + escaped UNC path (\\server\share\path)
3429              * The second form is clearly specific to Windows and it might
3430              * even be doing a network lookup to try to figure it out.
3431              */
3432             while (*src && *src != '/' && *src != '\\')
3433                 src++;
3434             len = src - pszUrl;
3435             StrCpyNW(dst, pszUrl, len + 1);
3436             dst += len;
3437             if (*src && isalphaW(src[1]) && (src[2] == ':' || src[2] == '|'))
3438             {
3439                 /* 'Forget' to add a trailing '/', just like Windows */
3440                 src++;
3441             }
3442         }
3443         break;
3444     case 4:
3445         /* 'file://' + unescaped UNC path (\\server\share\path) */
3446         unescape = 0;
3447         if (isalphaW(*src) && (src[1] == ':' || src[1] == '|'))
3448             break;
3449         /* fall through */
3450     default:
3451         /* 'file:/...' + escaped UNC path (\\server\share\path) */
3452         src -= 2;
3453     }
3454 
3455     /* Copy the remainder of the path */
3456     len += lstrlenW(src);
3457     StrCpyW(dst, src);
3458 
3459      /* First do the Windows-specific path conversions */
3460     for (dst = tpath; *dst; dst++)
3461         if (*dst == '/') *dst = '\\';
3462     if (isalphaW(*tpath) && tpath[1] == '|')
3463         tpath[1] = ':'; /* c| -> c: */
3464 
3465     /* And only then unescape the path (i.e. escaped slashes are left as is) */
3466     if (unescape)
3467     {
3468         ret = UrlUnescapeW(tpath, NULL, &len, URL_UNESCAPE_INPLACE);
3469         if (ret == S_OK)
3470         {
3471             /* When working in-place UrlUnescapeW() does not set len */
3472             len = lstrlenW(tpath);
3473         }
3474     }
3475 
3476     if (*pcchPath < len + 1)
3477     {
3478         ret = E_POINTER;
3479         *pcchPath = len + 1;
3480     }
3481     else
3482     {
3483         *pcchPath = len;
3484         if (tpath != pszPath)
3485             StrCpyW(pszPath, tpath);
3486     }
3487     if (tpath != pszPath)
3488       HeapFree(GetProcessHeap(), 0, tpath);
3489 
3490     TRACE("Returning (%u) %s\n", *pcchPath, debugstr_w(pszPath));
3491     return ret;
3492 }
3493 
3494 /*************************************************************************
3495  * PathCreateFromUrlAlloc   [SHLWAPI.@]
3496  */
PathCreateFromUrlAlloc(LPCWSTR pszUrl,LPWSTR * pszPath,DWORD dwReserved)3497 HRESULT WINAPI PathCreateFromUrlAlloc(LPCWSTR pszUrl, LPWSTR *pszPath,
3498                                       DWORD dwReserved)
3499 {
3500     WCHAR pathW[MAX_PATH];
3501     DWORD size;
3502     HRESULT hr;
3503 
3504     size = MAX_PATH;
3505     hr = PathCreateFromUrlW(pszUrl, pathW, &size, dwReserved);
3506     if (SUCCEEDED(hr))
3507     {
3508         /* Yes, this is supposed to crash if pszPath is NULL */
3509         *pszPath = StrDupW(pathW);
3510     }
3511     return hr;
3512 }
3513 
3514 /*************************************************************************
3515  * PathRelativePathToA   [SHLWAPI.@]
3516  *
3517  * Create a relative path from one path to another.
3518  *
3519  * PARAMS
3520  *  lpszPath   [O] Destination for relative path
3521  *  lpszFrom   [I] Source path
3522  *  dwAttrFrom [I] File attribute of source path
3523  *  lpszTo     [I] Destination path
3524  *  dwAttrTo   [I] File attributes of destination path
3525  *
3526  * RETURNS
3527  *  TRUE  If a relative path can be formed. lpszPath contains the new path
3528  *  FALSE If the paths are not relative or any parameters are invalid
3529  *
3530  * NOTES
3531  *  lpszTo should be at least MAX_PATH in length.
3532  *
3533  *  Calling this function with relative paths for lpszFrom or lpszTo may
3534  *  give erroneous results.
3535  *
3536  *  The Win32 version of this function contains a bug where the lpszTo string
3537  *  may be referenced 1 byte beyond the end of the string. As a result random
3538  *  garbage may be written to the output path, depending on what lies beyond
3539  *  the last byte of the string. This bug occurs because of the behaviour of
3540  *  PathCommonPrefix() (see notes for that function), and no workaround seems
3541  *  possible with Win32.
3542  *
3543  *  This bug has been fixed here, so for example the relative path from "\\"
3544  *  to "\\" is correctly determined as "." in this implementation.
3545  */
PathRelativePathToA(LPSTR lpszPath,LPCSTR lpszFrom,DWORD dwAttrFrom,LPCSTR lpszTo,DWORD dwAttrTo)3546 BOOL WINAPI PathRelativePathToA(LPSTR lpszPath, LPCSTR lpszFrom, DWORD dwAttrFrom,
3547                                 LPCSTR lpszTo, DWORD dwAttrTo)
3548 {
3549   BOOL bRet = FALSE;
3550 
3551   TRACE("(%p,%s,0x%08x,%s,0x%08x)\n", lpszPath, debugstr_a(lpszFrom),
3552         dwAttrFrom, debugstr_a(lpszTo), dwAttrTo);
3553 
3554   if(lpszPath && lpszFrom && lpszTo)
3555   {
3556     WCHAR szPath[MAX_PATH];
3557     WCHAR szFrom[MAX_PATH];
3558     WCHAR szTo[MAX_PATH];
3559     MultiByteToWideChar(CP_ACP,0,lpszFrom,-1,szFrom,MAX_PATH);
3560     MultiByteToWideChar(CP_ACP,0,lpszTo,-1,szTo,MAX_PATH);
3561     bRet = PathRelativePathToW(szPath,szFrom,dwAttrFrom,szTo,dwAttrTo);
3562     WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
3563   }
3564   return bRet;
3565 }
3566 
3567 /*************************************************************************
3568  * PathRelativePathToW   [SHLWAPI.@]
3569  *
3570  * See PathRelativePathToA.
3571  */
PathRelativePathToW(LPWSTR lpszPath,LPCWSTR lpszFrom,DWORD dwAttrFrom,LPCWSTR lpszTo,DWORD dwAttrTo)3572 BOOL WINAPI PathRelativePathToW(LPWSTR lpszPath, LPCWSTR lpszFrom, DWORD dwAttrFrom,
3573                                 LPCWSTR lpszTo, DWORD dwAttrTo)
3574 {
3575   static const WCHAR szPrevDirSlash[] = { '.', '.', '\\', '\0' };
3576   static const WCHAR szPrevDir[] = { '.', '.', '\0' };
3577   WCHAR szFrom[MAX_PATH];
3578   WCHAR szTo[MAX_PATH];
3579   DWORD dwLen;
3580 
3581   TRACE("(%p,%s,0x%08x,%s,0x%08x)\n", lpszPath, debugstr_w(lpszFrom),
3582         dwAttrFrom, debugstr_w(lpszTo), dwAttrTo);
3583 
3584   if(!lpszPath || !lpszFrom || !lpszTo)
3585     return FALSE;
3586 
3587   *lpszPath = '\0';
3588   lstrcpynW(szFrom, lpszFrom, MAX_PATH);
3589   lstrcpynW(szTo, lpszTo, MAX_PATH);
3590 
3591   if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY))
3592     PathRemoveFileSpecW(szFrom);
3593   if(!(dwAttrTo & FILE_ATTRIBUTE_DIRECTORY))
3594     PathRemoveFileSpecW(szTo);
3595 
3596   /* Paths can only be relative if they have a common root */
3597   if(!(dwLen = PathCommonPrefixW(szFrom, szTo, 0)))
3598     return FALSE;
3599 
3600   /* Strip off lpszFrom components to the root, by adding "..\" */
3601   lpszFrom = szFrom + dwLen;
3602   if (!*lpszFrom)
3603   {
3604     lpszPath[0] = '.';
3605     lpszPath[1] = '\0';
3606   }
3607   if (*lpszFrom == '\\')
3608     lpszFrom++;
3609 
3610   while (*lpszFrom)
3611   {
3612     lpszFrom = PathFindNextComponentW(lpszFrom);
3613     strcatW(lpszPath, *lpszFrom ? szPrevDirSlash : szPrevDir);
3614   }
3615 
3616   /* From the root add the components of lpszTo */
3617   lpszTo += dwLen;
3618   /* We check lpszTo[-1] to avoid skipping end of string. See the notes for
3619    * this function.
3620    */
3621   if (*lpszTo && lpszTo[-1])
3622   {
3623     if (*lpszTo != '\\')
3624       lpszTo--;
3625     dwLen = strlenW(lpszPath);
3626     if (dwLen + strlenW(lpszTo) >= MAX_PATH)
3627     {
3628       *lpszPath = '\0';
3629       return FALSE;
3630     }
3631     strcpyW(lpszPath + dwLen, lpszTo);
3632   }
3633   return TRUE;
3634 }
3635 
3636 /*************************************************************************
3637  * PathUnmakeSystemFolderA   [SHLWAPI.@]
3638  *
3639  * Remove the system folder attributes from a path.
3640  *
3641  * PARAMS
3642  *  lpszPath [I] The path to remove attributes from
3643  *
3644  * RETURNS
3645  *  Success: TRUE.
3646  *  Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling
3647  *           SetFileAttributesA() fails.
3648  */
PathUnmakeSystemFolderA(LPCSTR lpszPath)3649 BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath)
3650 {
3651   DWORD dwAttr;
3652 
3653   TRACE("(%s)\n", debugstr_a(lpszPath));
3654 
3655   if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
3656       !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
3657     return FALSE;
3658 
3659   dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
3660   return SetFileAttributesA(lpszPath, dwAttr);
3661 }
3662 
3663 /*************************************************************************
3664  * PathUnmakeSystemFolderW   [SHLWAPI.@]
3665  *
3666  * See PathUnmakeSystemFolderA.
3667  */
PathUnmakeSystemFolderW(LPCWSTR lpszPath)3668 BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath)
3669 {
3670   DWORD dwAttr;
3671 
3672   TRACE("(%s)\n", debugstr_w(lpszPath));
3673 
3674   if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
3675     !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
3676     return FALSE;
3677 
3678   dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
3679   return SetFileAttributesW(lpszPath, dwAttr);
3680 }
3681 
3682 
3683 /*************************************************************************
3684  * PathSetDlgItemPathA   [SHLWAPI.@]
3685  *
3686  * Set the text of a dialog item to a path, shrinking the path to fit
3687  * if it is too big for the item.
3688  *
3689  * PARAMS
3690  *  hDlg     [I] Dialog handle
3691  *  id       [I] ID of item in the dialog
3692  *  lpszPath [I] Path to set as the items text
3693  *
3694  * RETURNS
3695  *  Nothing.
3696  *
3697  * NOTES
3698  *  If lpszPath is NULL, a blank string ("") is set (i.e. The previous
3699  *  window text is erased).
3700  */
PathSetDlgItemPathA(HWND hDlg,int id,LPCSTR lpszPath)3701 VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath)
3702 {
3703   WCHAR szPath[MAX_PATH];
3704 
3705   TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath));
3706 
3707   if (lpszPath)
3708     MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
3709   else
3710     szPath[0] = '\0';
3711   PathSetDlgItemPathW(hDlg, id, szPath);
3712 }
3713 
3714 /*************************************************************************
3715  * PathSetDlgItemPathW   [SHLWAPI.@]
3716  *
3717  * See PathSetDlgItemPathA.
3718  */
PathSetDlgItemPathW(HWND hDlg,int id,LPCWSTR lpszPath)3719 VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath)
3720 {
3721   WCHAR path[MAX_PATH + 1];
3722   HWND hwItem;
3723   RECT rect;
3724   HDC hdc;
3725   HGDIOBJ hPrevObj;
3726 
3727   TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath));
3728 
3729   if (!(hwItem = GetDlgItem(hDlg, id)))
3730     return;
3731 
3732   if (lpszPath)
3733     lstrcpynW(path, lpszPath, sizeof(path) / sizeof(WCHAR));
3734   else
3735     path[0] = '\0';
3736 
3737   GetClientRect(hwItem, &rect);
3738   hdc = GetDC(hDlg);
3739   hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0));
3740 
3741   if (hPrevObj)
3742   {
3743     PathCompactPathW(hdc, path, rect.right);
3744     SelectObject(hdc, hPrevObj);
3745   }
3746 
3747   ReleaseDC(hDlg, hdc);
3748   SetWindowTextW(hwItem, path);
3749 }
3750 
3751 /*************************************************************************
3752  * PathIsNetworkPathA [SHLWAPI.@]
3753  *
3754  * Determine if the given path is a network path.
3755  *
3756  * PARAMS
3757  *  lpszPath [I] Path to check
3758  *
3759  * RETURNS
3760  *  TRUE  If lpszPath is a UNC share or mapped network drive, or
3761  *  FALSE If lpszPath is a local drive or cannot be determined
3762  */
PathIsNetworkPathA(LPCSTR lpszPath)3763 BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath)
3764 {
3765   int dwDriveNum;
3766 
3767   TRACE("(%s)\n",debugstr_a(lpszPath));
3768 
3769   if (!lpszPath)
3770     return FALSE;
3771   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3772     return TRUE;
3773   dwDriveNum = PathGetDriveNumberA(lpszPath);
3774   if (dwDriveNum == -1)
3775     return FALSE;
3776 #ifdef __REACTOS__
3777   return IsNetDrive(dwDriveNum);
3778 #else
3779   GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
3780   return pIsNetDrive(dwDriveNum);
3781 #endif
3782 }
3783 
3784 /*************************************************************************
3785  * PathIsNetworkPathW [SHLWAPI.@]
3786  *
3787  * See PathIsNetworkPathA.
3788  */
PathIsNetworkPathW(LPCWSTR lpszPath)3789 BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath)
3790 {
3791   int dwDriveNum;
3792 
3793   TRACE("(%s)\n", debugstr_w(lpszPath));
3794 
3795   if (!lpszPath)
3796     return FALSE;
3797   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3798     return TRUE;
3799   dwDriveNum = PathGetDriveNumberW(lpszPath);
3800   if (dwDriveNum == -1)
3801     return FALSE;
3802 #ifdef __REACTOS__
3803   return IsNetDrive(dwDriveNum);
3804 #else
3805   GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
3806   return pIsNetDrive(dwDriveNum);
3807 #endif
3808 }
3809 
3810 /*************************************************************************
3811  * PathIsLFNFileSpecA [SHLWAPI.@]
3812  *
3813  * Determine if the given path is a long file name
3814  *
3815  * PARAMS
3816  *  lpszPath [I] Path to check
3817  *
3818  * RETURNS
3819  *  TRUE  If path is a long file name,
3820  *  FALSE If path is a valid DOS short file name
3821  */
PathIsLFNFileSpecA(LPCSTR lpszPath)3822 BOOL WINAPI PathIsLFNFileSpecA(LPCSTR lpszPath)
3823 {
3824   DWORD dwNameLen = 0, dwExtLen = 0;
3825 
3826   TRACE("(%s)\n",debugstr_a(lpszPath));
3827 
3828   if (!lpszPath)
3829     return FALSE;
3830 
3831   while (*lpszPath)
3832   {
3833     if (*lpszPath == ' ')
3834       return TRUE; /* DOS names cannot have spaces */
3835     if (*lpszPath == '.')
3836     {
3837       if (dwExtLen)
3838         return TRUE; /* DOS names have only one dot */
3839       dwExtLen = 1;
3840     }
3841     else if (dwExtLen)
3842     {
3843       dwExtLen++;
3844       if (dwExtLen > 4)
3845         return TRUE; /* DOS extensions are <= 3 chars*/
3846     }
3847     else
3848     {
3849       dwNameLen++;
3850       if (dwNameLen > 8)
3851         return TRUE; /* DOS names are <= 8 chars */
3852     }
3853     lpszPath += IsDBCSLeadByte(*lpszPath) ? 2 : 1;
3854   }
3855   return FALSE; /* Valid DOS path */
3856 }
3857 
3858 /*************************************************************************
3859  * PathIsLFNFileSpecW [SHLWAPI.@]
3860  *
3861  * See PathIsLFNFileSpecA.
3862  */
PathIsLFNFileSpecW(LPCWSTR lpszPath)3863 BOOL WINAPI PathIsLFNFileSpecW(LPCWSTR lpszPath)
3864 {
3865   DWORD dwNameLen = 0, dwExtLen = 0;
3866 
3867   TRACE("(%s)\n",debugstr_w(lpszPath));
3868 
3869   if (!lpszPath)
3870     return FALSE;
3871 
3872   while (*lpszPath)
3873   {
3874     if (*lpszPath == ' ')
3875       return TRUE; /* DOS names cannot have spaces */
3876     if (*lpszPath == '.')
3877     {
3878       if (dwExtLen)
3879         return TRUE; /* DOS names have only one dot */
3880       dwExtLen = 1;
3881     }
3882     else if (dwExtLen)
3883     {
3884       dwExtLen++;
3885       if (dwExtLen > 4)
3886         return TRUE; /* DOS extensions are <= 3 chars*/
3887     }
3888     else
3889     {
3890       dwNameLen++;
3891       if (dwNameLen > 8)
3892         return TRUE; /* DOS names are <= 8 chars */
3893     }
3894     lpszPath++;
3895   }
3896   return FALSE; /* Valid DOS path */
3897 }
3898 
3899 /*************************************************************************
3900  * PathIsDirectoryEmptyA [SHLWAPI.@]
3901  *
3902  * Determine if a given directory is empty.
3903  *
3904  * PARAMS
3905  *  lpszPath [I] Directory to check
3906  *
3907  * RETURNS
3908  *  TRUE  If the directory exists and contains no files,
3909  *  FALSE Otherwise
3910  */
PathIsDirectoryEmptyA(LPCSTR lpszPath)3911 BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath)
3912 {
3913   BOOL bRet = FALSE;
3914 
3915   TRACE("(%s)\n",debugstr_a(lpszPath));
3916 
3917   if (lpszPath)
3918   {
3919     WCHAR szPath[MAX_PATH];
3920     MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
3921     bRet = PathIsDirectoryEmptyW(szPath);
3922   }
3923   return bRet;
3924 }
3925 
3926 /*************************************************************************
3927  * PathIsDirectoryEmptyW [SHLWAPI.@]
3928  *
3929  * See PathIsDirectoryEmptyA.
3930  */
PathIsDirectoryEmptyW(LPCWSTR lpszPath)3931 BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath)
3932 {
3933   static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' };
3934   WCHAR szSearch[MAX_PATH];
3935   DWORD dwLen;
3936   HANDLE hfind;
3937   BOOL retVal = TRUE;
3938   WIN32_FIND_DATAW find_data;
3939 
3940   TRACE("(%s)\n",debugstr_w(lpszPath));
3941 
3942   if (!lpszPath || !PathIsDirectoryW(lpszPath))
3943     return FALSE;
3944 
3945   lstrcpynW(szSearch, lpszPath, MAX_PATH);
3946   PathAddBackslashW(szSearch);
3947   dwLen = strlenW(szSearch);
3948   if (dwLen > MAX_PATH - 4)
3949     return FALSE;
3950 
3951   strcpyW(szSearch + dwLen, szAllFiles);
3952   hfind = FindFirstFileW(szSearch, &find_data);
3953   if (hfind == INVALID_HANDLE_VALUE)
3954     return FALSE;
3955 
3956   do
3957   {
3958     if (find_data.cFileName[0] == '.')
3959     {
3960       if (find_data.cFileName[1] == '\0') continue;
3961       if (find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0') continue;
3962     }
3963 
3964     retVal = FALSE;
3965     break;
3966   }
3967   while (FindNextFileW(hfind, &find_data));
3968 
3969   FindClose(hfind);
3970   return retVal;
3971 }
3972 
3973 
3974 /*************************************************************************
3975  * PathFindSuffixArrayA [SHLWAPI.@]
3976  *
3977  * Find a suffix string in an array of suffix strings
3978  *
3979  * PARAMS
3980  *  lpszSuffix [I] Suffix string to search for
3981  *  lppszArray [I] Array of suffix strings to search
3982  *  dwCount    [I] Number of elements in lppszArray
3983  *
3984  * RETURNS
3985  *  Success: The index of the position of lpszSuffix in lppszArray
3986  *  Failure: 0, if any parameters are invalid or lpszSuffix is not found
3987  *
3988  * NOTES
3989  *  The search is case sensitive.
3990  *  The match is made against the end of the suffix string, so for example:
3991  *  lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not.
3992  */
PathFindSuffixArrayA(LPCSTR lpszSuffix,LPCSTR * lppszArray,int dwCount)3993 LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount)
3994 {
3995   size_t dwLen;
3996   int dwRet = 0;
3997 
3998   TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount);
3999 
4000   if (lpszSuffix && lppszArray && dwCount > 0)
4001   {
4002     dwLen = strlen(lpszSuffix);
4003 
4004     while (dwRet < dwCount)
4005     {
4006       size_t dwCompareLen = strlen(*lppszArray);
4007       if (dwCompareLen < dwLen)
4008       {
4009         if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
4010           return *lppszArray; /* Found */
4011       }
4012       dwRet++;
4013       lppszArray++;
4014     }
4015   }
4016   return NULL;
4017 }
4018 
4019 /*************************************************************************
4020  * PathFindSuffixArrayW [SHLWAPI.@]
4021  *
4022  * See PathFindSuffixArrayA.
4023  */
PathFindSuffixArrayW(LPCWSTR lpszSuffix,LPCWSTR * lppszArray,int dwCount)4024 LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount)
4025 {
4026   size_t dwLen;
4027   int dwRet = 0;
4028 
4029   TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount);
4030 
4031   if (lpszSuffix && lppszArray && dwCount > 0)
4032   {
4033     dwLen = strlenW(lpszSuffix);
4034 
4035     while (dwRet < dwCount)
4036     {
4037       size_t dwCompareLen = strlenW(*lppszArray);
4038       if (dwCompareLen < dwLen)
4039       {
4040         if (!strcmpW(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
4041           return *lppszArray; /* Found */
4042       }
4043       dwRet++;
4044       lppszArray++;
4045     }
4046   }
4047   return NULL;
4048 }
4049 
4050 /*************************************************************************
4051  * PathUndecorateA [SHLWAPI.@]
4052  *
4053  * Undecorate a file path
4054  *
4055  * PARAMS
4056  *  lpszPath [I/O] Path to remove any decoration from
4057  *
4058  * RETURNS
4059  *  Nothing
4060  *
4061  * NOTES
4062  *  A decorations form is "path[n].ext" where "n" is an optional decimal number.
4063  */
PathUndecorateA(LPSTR lpszPath)4064 VOID WINAPI PathUndecorateA(LPSTR lpszPath)
4065 {
4066   TRACE("(%s)\n",debugstr_a(lpszPath));
4067 
4068   if (lpszPath)
4069   {
4070     LPSTR lpszExt = PathFindExtensionA(lpszPath);
4071     if (lpszExt > lpszPath && lpszExt[-1] == ']')
4072     {
4073       LPSTR lpszSkip = lpszExt - 2;
4074       if (*lpszSkip == '[')
4075         lpszSkip++;  /* [] (no number) */
4076       else
4077         while (lpszSkip > lpszPath && isdigit(lpszSkip[-1]))
4078           lpszSkip--;
4079       if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\')
4080       {
4081         /* remove the [n] */
4082         lpszSkip--;
4083         while (*lpszExt)
4084           *lpszSkip++ = *lpszExt++;
4085         *lpszSkip = '\0';
4086       }
4087     }
4088   }
4089 }
4090 
4091 /*************************************************************************
4092  * PathUndecorateW [SHLWAPI.@]
4093  *
4094  * See PathUndecorateA.
4095  */
PathUndecorateW(LPWSTR lpszPath)4096 VOID WINAPI PathUndecorateW(LPWSTR lpszPath)
4097 {
4098   TRACE("(%s)\n",debugstr_w(lpszPath));
4099 
4100   if (lpszPath)
4101   {
4102     LPWSTR lpszExt = PathFindExtensionW(lpszPath);
4103     if (lpszExt > lpszPath && lpszExt[-1] == ']')
4104     {
4105       LPWSTR lpszSkip = lpszExt - 2;
4106       if (*lpszSkip == '[')
4107         lpszSkip++; /* [] (no number) */
4108       else
4109         while (lpszSkip > lpszPath && isdigitW(lpszSkip[-1]))
4110           lpszSkip--;
4111       if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\')
4112       {
4113         /* remove the [n] */
4114         lpszSkip--;
4115         while (*lpszExt)
4116           *lpszSkip++ = *lpszExt++;
4117         *lpszSkip = '\0';
4118       }
4119     }
4120   }
4121 }
4122 
4123 /*************************************************************************
4124  * PathUnExpandEnvStringsA [SHLWAPI.@]
4125  *
4126  * Substitute folder names in a path with their corresponding environment
4127  * strings.
4128  *
4129  * PARAMS
4130  *  path    [I] Buffer containing the path to unexpand.
4131  *  buffer  [O] Buffer to receive the unexpanded path.
4132  *  buf_len [I] Size of pszBuf in characters.
4133  *
4134  * RETURNS
4135  *  Success: TRUE
4136  *  Failure: FALSE
4137  */
PathUnExpandEnvStringsA(LPCSTR path,LPSTR buffer,UINT buf_len)4138 BOOL WINAPI PathUnExpandEnvStringsA(LPCSTR path, LPSTR buffer, UINT buf_len)
4139 {
4140     WCHAR bufferW[MAX_PATH], *pathW;
4141     DWORD len;
4142     BOOL ret;
4143 
4144     TRACE("(%s, %p, %d)\n", debugstr_a(path), buffer, buf_len);
4145 
4146     pathW = heap_strdupAtoW(path);
4147     if (!pathW) return FALSE;
4148 
4149     ret = PathUnExpandEnvStringsW(pathW, bufferW, MAX_PATH);
4150     HeapFree(GetProcessHeap(), 0, pathW);
4151     if (!ret) return FALSE;
4152 
4153     len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
4154     if (buf_len < len + 1) return FALSE;
4155 
4156     WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buf_len, NULL, NULL);
4157     return TRUE;
4158 }
4159 
4160 static const WCHAR allusersprofileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%',0};
4161 static const WCHAR appdataW[] = {'%','A','P','P','D','A','T','A','%',0};
4162 static const WCHAR programfilesW[] = {'%','P','r','o','g','r','a','m','F','i','l','e','s','%',0};
4163 static const WCHAR systemrootW[] = {'%','S','y','s','t','e','m','R','o','o','t','%',0};
4164 static const WCHAR systemdriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%',0};
4165 static const WCHAR userprofileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%',0};
4166 
4167 struct envvars_map
4168 {
4169     const WCHAR *var;
4170     UINT  varlen;
4171     WCHAR path[MAX_PATH];
4172     DWORD len;
4173 };
4174 
init_envvars_map(struct envvars_map * map)4175 static void init_envvars_map(struct envvars_map *map)
4176 {
4177     while (map->var)
4178     {
4179         map->len = ExpandEnvironmentStringsW(map->var, map->path, sizeof(map->path)/sizeof(WCHAR));
4180         /* exclude null from length */
4181         if (map->len) map->len--;
4182         map++;
4183     }
4184 }
4185 
4186 /*************************************************************************
4187  * PathUnExpandEnvStringsW [SHLWAPI.@]
4188  *
4189  * Unicode version of PathUnExpandEnvStringsA.
4190  */
PathUnExpandEnvStringsW(LPCWSTR path,LPWSTR buffer,UINT buf_len)4191 BOOL WINAPI PathUnExpandEnvStringsW(LPCWSTR path, LPWSTR buffer, UINT buf_len)
4192 {
4193     static struct envvars_map null_var = {NULL, 0, {0}, 0};
4194     struct envvars_map *match = &null_var, *cur;
4195     struct envvars_map envvars[] = {
4196         { allusersprofileW, sizeof(allusersprofileW)/sizeof(WCHAR) },
4197         { appdataW,         sizeof(appdataW)/sizeof(WCHAR)         },
4198         { programfilesW,    sizeof(programfilesW)/sizeof(WCHAR)    },
4199         { systemrootW,      sizeof(systemrootW)/sizeof(WCHAR)      },
4200         { systemdriveW,     sizeof(systemdriveW)/sizeof(WCHAR)     },
4201         { userprofileW,     sizeof(userprofileW)/sizeof(WCHAR)     },
4202         { NULL }
4203     };
4204     DWORD pathlen;
4205     UINT  needed;
4206 
4207     TRACE("(%s, %p, %d)\n", debugstr_w(path), buffer, buf_len);
4208 
4209     pathlen = strlenW(path);
4210     init_envvars_map(envvars);
4211     cur = envvars;
4212     while (cur->var)
4213     {
4214         /* path can't contain expanded value or value wasn't retrieved */
4215         if (cur->len == 0 || cur->len > pathlen || strncmpiW(cur->path, path, cur->len))
4216         {
4217             cur++;
4218             continue;
4219         }
4220 
4221         if (cur->len > match->len)
4222             match = cur;
4223         cur++;
4224     }
4225 
4226     /* 'varlen' includes NULL termination char */
4227     needed = match->varlen + pathlen - match->len;
4228     if (match->len == 0 || needed > buf_len) return FALSE;
4229 
4230     strcpyW(buffer, match->var);
4231     strcatW(buffer, &path[match->len]);
4232     TRACE("ret %s\n", debugstr_w(buffer));
4233 
4234     return TRUE;
4235 }
4236 
4237 /*************************************************************************
4238  * @     [SHLWAPI.440]
4239  *
4240  * Find localised or default web content in "%WINDOWS%\web\".
4241  *
4242  * PARAMS
4243  *  lpszFile  [I] File name containing content to look for
4244  *  lpszPath  [O] Buffer to contain the full path to the file
4245  *  dwPathLen [I] Length of lpszPath
4246  *
4247  * RETURNS
4248  *  Success: S_OK. lpszPath contains the full path to the content.
4249  *  Failure: E_FAIL. The content does not exist or lpszPath is too short.
4250  */
SHGetWebFolderFilePathA(LPCSTR lpszFile,LPSTR lpszPath,DWORD dwPathLen)4251 HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen)
4252 {
4253   WCHAR szFile[MAX_PATH], szPath[MAX_PATH];
4254   HRESULT hRet;
4255 
4256   TRACE("(%s,%p,%d)\n", lpszFile, lpszPath, dwPathLen);
4257 
4258   MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH);
4259   szPath[0] = '\0';
4260   hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen);
4261   WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0);
4262   return hRet;
4263 }
4264 
4265 /*************************************************************************
4266  * @     [SHLWAPI.441]
4267  *
4268  * Unicode version of SHGetWebFolderFilePathA.
4269  */
SHGetWebFolderFilePathW(LPCWSTR lpszFile,LPWSTR lpszPath,DWORD dwPathLen)4270 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen)
4271 {
4272   static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'};
4273   static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'};
4274 #define szWebLen (sizeof(szWeb)/sizeof(WCHAR))
4275 #define szWebMuiLen ((sizeof(szWebMui)+1)/sizeof(WCHAR))
4276   DWORD dwLen, dwFileLen;
4277   LANGID lidSystem, lidUser;
4278 
4279   TRACE("(%s,%p,%d)\n", debugstr_w(lpszFile), lpszPath, dwPathLen);
4280 
4281   /* Get base directory for web content */
4282   dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen);
4283   if (dwLen > 0 && lpszPath[dwLen-1] == '\\')
4284     dwLen--;
4285 
4286   dwFileLen = strlenW(lpszFile);
4287 
4288   if (dwLen + dwFileLen + szWebLen >= dwPathLen)
4289     return E_FAIL; /* lpszPath too short */
4290 
4291   strcpyW(lpszPath+dwLen, szWeb);
4292   dwLen += szWebLen;
4293   dwPathLen = dwPathLen - dwLen; /* Remaining space */
4294 
4295   lidSystem = GetSystemDefaultUILanguage();
4296   lidUser = GetUserDefaultUILanguage();
4297 
4298   if (lidSystem != lidUser)
4299   {
4300     if (dwFileLen + szWebMuiLen < dwPathLen)
4301     {
4302       /* Use localised content in the users UI language if present */
4303       wsprintfW(lpszPath + dwLen, szWebMui, lidUser);
4304       strcpyW(lpszPath + dwLen + szWebMuiLen, lpszFile);
4305       if (PathFileExistsW(lpszPath))
4306         return S_OK;
4307     }
4308   }
4309 
4310   /* Fall back to OS default installed content */
4311   strcpyW(lpszPath + dwLen, lpszFile);
4312   if (PathFileExistsW(lpszPath))
4313     return S_OK;
4314   return E_FAIL;
4315 }
4316 
4317 #ifndef __REACTOS__ /* Defined in <shlwapi_undoc.h> */
4318 #define PATH_CHAR_CLASS_LETTER      0x00000001
4319 #define PATH_CHAR_CLASS_ASTERIX     0x00000002
4320 #define PATH_CHAR_CLASS_DOT         0x00000004
4321 #define PATH_CHAR_CLASS_BACKSLASH   0x00000008
4322 #define PATH_CHAR_CLASS_COLON       0x00000010
4323 #define PATH_CHAR_CLASS_SEMICOLON   0x00000020
4324 #define PATH_CHAR_CLASS_COMMA       0x00000040
4325 #define PATH_CHAR_CLASS_SPACE       0x00000080
4326 #define PATH_CHAR_CLASS_OTHER_VALID 0x00000100
4327 #define PATH_CHAR_CLASS_DOUBLEQUOTE 0x00000200
4328 
4329 #define PATH_CHAR_CLASS_INVALID     0x00000000
4330 #define PATH_CHAR_CLASS_ANY         0xffffffff
4331 #endif
4332 
4333 static const DWORD SHELL_charclass[] =
4334 {
4335     /* 0x00 */  PATH_CHAR_CLASS_INVALID,      /* 0x01 */  PATH_CHAR_CLASS_INVALID,
4336     /* 0x02 */  PATH_CHAR_CLASS_INVALID,      /* 0x03 */  PATH_CHAR_CLASS_INVALID,
4337     /* 0x04 */  PATH_CHAR_CLASS_INVALID,      /* 0x05 */  PATH_CHAR_CLASS_INVALID,
4338     /* 0x06 */  PATH_CHAR_CLASS_INVALID,      /* 0x07 */  PATH_CHAR_CLASS_INVALID,
4339     /* 0x08 */  PATH_CHAR_CLASS_INVALID,      /* 0x09 */  PATH_CHAR_CLASS_INVALID,
4340     /* 0x0a */  PATH_CHAR_CLASS_INVALID,      /* 0x0b */  PATH_CHAR_CLASS_INVALID,
4341     /* 0x0c */  PATH_CHAR_CLASS_INVALID,      /* 0x0d */  PATH_CHAR_CLASS_INVALID,
4342     /* 0x0e */  PATH_CHAR_CLASS_INVALID,      /* 0x0f */  PATH_CHAR_CLASS_INVALID,
4343     /* 0x10 */  PATH_CHAR_CLASS_INVALID,      /* 0x11 */  PATH_CHAR_CLASS_INVALID,
4344     /* 0x12 */  PATH_CHAR_CLASS_INVALID,      /* 0x13 */  PATH_CHAR_CLASS_INVALID,
4345     /* 0x14 */  PATH_CHAR_CLASS_INVALID,      /* 0x15 */  PATH_CHAR_CLASS_INVALID,
4346     /* 0x16 */  PATH_CHAR_CLASS_INVALID,      /* 0x17 */  PATH_CHAR_CLASS_INVALID,
4347     /* 0x18 */  PATH_CHAR_CLASS_INVALID,      /* 0x19 */  PATH_CHAR_CLASS_INVALID,
4348     /* 0x1a */  PATH_CHAR_CLASS_INVALID,      /* 0x1b */  PATH_CHAR_CLASS_INVALID,
4349     /* 0x1c */  PATH_CHAR_CLASS_INVALID,      /* 0x1d */  PATH_CHAR_CLASS_INVALID,
4350     /* 0x1e */  PATH_CHAR_CLASS_INVALID,      /* 0x1f */  PATH_CHAR_CLASS_INVALID,
4351     /* ' '  */  PATH_CHAR_CLASS_SPACE,        /* '!'  */  PATH_CHAR_CLASS_OTHER_VALID,
4352     /* '"'  */  PATH_CHAR_CLASS_DOUBLEQUOTE,  /* '#'  */  PATH_CHAR_CLASS_OTHER_VALID,
4353     /* '$'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '%'  */  PATH_CHAR_CLASS_OTHER_VALID,
4354     /* '&'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '\'' */  PATH_CHAR_CLASS_OTHER_VALID,
4355     /* '('  */  PATH_CHAR_CLASS_OTHER_VALID,  /* ')'  */  PATH_CHAR_CLASS_OTHER_VALID,
4356     /* '*'  */  PATH_CHAR_CLASS_ASTERIX,      /* '+'  */  PATH_CHAR_CLASS_OTHER_VALID,
4357     /* ','  */  PATH_CHAR_CLASS_COMMA,        /* '-'  */  PATH_CHAR_CLASS_OTHER_VALID,
4358     /* '.'  */  PATH_CHAR_CLASS_DOT,          /* '/'  */  PATH_CHAR_CLASS_INVALID,
4359     /* '0'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '1'  */  PATH_CHAR_CLASS_OTHER_VALID,
4360     /* '2'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '3'  */  PATH_CHAR_CLASS_OTHER_VALID,
4361     /* '4'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '5'  */  PATH_CHAR_CLASS_OTHER_VALID,
4362     /* '6'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '7'  */  PATH_CHAR_CLASS_OTHER_VALID,
4363     /* '8'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '9'  */  PATH_CHAR_CLASS_OTHER_VALID,
4364     /* ':'  */  PATH_CHAR_CLASS_COLON,        /* ';'  */  PATH_CHAR_CLASS_SEMICOLON,
4365     /* '<'  */  PATH_CHAR_CLASS_INVALID,      /* '='  */  PATH_CHAR_CLASS_OTHER_VALID,
4366     /* '>'  */  PATH_CHAR_CLASS_INVALID,      /* '?'  */  PATH_CHAR_CLASS_LETTER,
4367     /* '@'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* 'A'  */  PATH_CHAR_CLASS_ANY,
4368     /* 'B'  */  PATH_CHAR_CLASS_ANY,          /* 'C'  */  PATH_CHAR_CLASS_ANY,
4369     /* 'D'  */  PATH_CHAR_CLASS_ANY,          /* 'E'  */  PATH_CHAR_CLASS_ANY,
4370     /* 'F'  */  PATH_CHAR_CLASS_ANY,          /* 'G'  */  PATH_CHAR_CLASS_ANY,
4371     /* 'H'  */  PATH_CHAR_CLASS_ANY,          /* 'I'  */  PATH_CHAR_CLASS_ANY,
4372     /* 'J'  */  PATH_CHAR_CLASS_ANY,          /* 'K'  */  PATH_CHAR_CLASS_ANY,
4373     /* 'L'  */  PATH_CHAR_CLASS_ANY,          /* 'M'  */  PATH_CHAR_CLASS_ANY,
4374     /* 'N'  */  PATH_CHAR_CLASS_ANY,          /* 'O'  */  PATH_CHAR_CLASS_ANY,
4375     /* 'P'  */  PATH_CHAR_CLASS_ANY,          /* 'Q'  */  PATH_CHAR_CLASS_ANY,
4376     /* 'R'  */  PATH_CHAR_CLASS_ANY,          /* 'S'  */  PATH_CHAR_CLASS_ANY,
4377     /* 'T'  */  PATH_CHAR_CLASS_ANY,          /* 'U'  */  PATH_CHAR_CLASS_ANY,
4378     /* 'V'  */  PATH_CHAR_CLASS_ANY,          /* 'W'  */  PATH_CHAR_CLASS_ANY,
4379     /* 'X'  */  PATH_CHAR_CLASS_ANY,          /* 'Y'  */  PATH_CHAR_CLASS_ANY,
4380     /* 'Z'  */  PATH_CHAR_CLASS_ANY,          /* '['  */  PATH_CHAR_CLASS_OTHER_VALID,
4381     /* '\\' */  PATH_CHAR_CLASS_BACKSLASH,    /* ']'  */  PATH_CHAR_CLASS_OTHER_VALID,
4382     /* '^'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* '_'  */  PATH_CHAR_CLASS_OTHER_VALID,
4383     /* '`'  */  PATH_CHAR_CLASS_OTHER_VALID,  /* 'a'  */  PATH_CHAR_CLASS_ANY,
4384     /* 'b'  */  PATH_CHAR_CLASS_ANY,          /* 'c'  */  PATH_CHAR_CLASS_ANY,
4385     /* 'd'  */  PATH_CHAR_CLASS_ANY,          /* 'e'  */  PATH_CHAR_CLASS_ANY,
4386     /* 'f'  */  PATH_CHAR_CLASS_ANY,          /* 'g'  */  PATH_CHAR_CLASS_ANY,
4387     /* 'h'  */  PATH_CHAR_CLASS_ANY,          /* 'i'  */  PATH_CHAR_CLASS_ANY,
4388     /* 'j'  */  PATH_CHAR_CLASS_ANY,          /* 'k'  */  PATH_CHAR_CLASS_ANY,
4389     /* 'l'  */  PATH_CHAR_CLASS_ANY,          /* 'm'  */  PATH_CHAR_CLASS_ANY,
4390     /* 'n'  */  PATH_CHAR_CLASS_ANY,          /* 'o'  */  PATH_CHAR_CLASS_ANY,
4391     /* 'p'  */  PATH_CHAR_CLASS_ANY,          /* 'q'  */  PATH_CHAR_CLASS_ANY,
4392     /* 'r'  */  PATH_CHAR_CLASS_ANY,          /* 's'  */  PATH_CHAR_CLASS_ANY,
4393     /* 't'  */  PATH_CHAR_CLASS_ANY,          /* 'u'  */  PATH_CHAR_CLASS_ANY,
4394     /* 'v'  */  PATH_CHAR_CLASS_ANY,          /* 'w'  */  PATH_CHAR_CLASS_ANY,
4395     /* 'x'  */  PATH_CHAR_CLASS_ANY,          /* 'y'  */  PATH_CHAR_CLASS_ANY,
4396     /* 'z'  */  PATH_CHAR_CLASS_ANY,          /* '{'  */  PATH_CHAR_CLASS_OTHER_VALID,
4397     /* '|'  */  PATH_CHAR_CLASS_INVALID,      /* '}'  */  PATH_CHAR_CLASS_OTHER_VALID,
4398     /* '~'  */  PATH_CHAR_CLASS_OTHER_VALID
4399 };
4400 
4401 /*************************************************************************
4402  * @     [SHLWAPI.455]
4403  *
4404  * Check if an ASCII char is of a certain class
4405  */
PathIsValidCharA(char c,DWORD class)4406 BOOL WINAPI PathIsValidCharA( char c, DWORD class )
4407 {
4408     if ((unsigned)c > 0x7e)
4409         return class & PATH_CHAR_CLASS_OTHER_VALID;
4410 
4411     return class & SHELL_charclass[(unsigned)c];
4412 }
4413 
4414 /*************************************************************************
4415  * @     [SHLWAPI.456]
4416  *
4417  * Check if a Unicode char is of a certain class
4418  */
PathIsValidCharW(WCHAR c,DWORD class)4419 BOOL WINAPI PathIsValidCharW( WCHAR c, DWORD class )
4420 {
4421     if (c > 0x7e)
4422         return class & PATH_CHAR_CLASS_OTHER_VALID;
4423 
4424     return class & SHELL_charclass[c];
4425 }
4426