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