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