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