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