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