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