Implemented SHSimpleIDListFromPath correctly.
[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       return FALSE;
1203     if (PathFileExistsDefExtW(buff, dwWhich))
1204     {
1205       strcpyW(lpszFile, buff);
1206       free(lpszPATH);
1207       return TRUE;
1208     }
1209   }
1210   free(lpszPATH);
1211   return FALSE;
1212 }
1213
1214 /*************************************************************************
1215  * @    [SHLWAPI.5]
1216  *
1217  * Search a range of paths for a specific type of executable.
1218  *
1219  * PARAMS
1220  *  lpszFile       [I/O] File to search for
1221  *  lppszOtherDirs [I]   Other directories to look in
1222  *  dwWhich        [I]   Type of executable to search for
1223  *
1224  * RETURNS
1225  *  Success: TRUE. The path to the executable is stored in lpszFile.
1226  *  Failure: FALSE. The path to the executable is unchanged.
1227  */
1228 BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich)
1229 {
1230   WCHAR szFile[MAX_PATH];
1231   WCHAR buff[MAX_PATH];
1232
1233   TRACE("(%s,%p,%08lx)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich);
1234
1235   if (!lpszFile || !PathIsFileSpecA(lpszFile))
1236     return FALSE;
1237
1238   MultiByteToWideChar(0,0,lpszFile,-1,szFile,MAX_PATH);
1239
1240   /* Search provided directories first */
1241   if (lppszOtherDirs && *lppszOtherDirs)
1242   {
1243     WCHAR szOther[MAX_PATH];
1244     LPCSTR *lpszOtherPath = lppszOtherDirs;
1245
1246     while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
1247     {
1248       MultiByteToWideChar(0,0,*lpszOtherPath,-1,szOther,MAX_PATH);
1249       PathCombineW(buff, szOther, szFile);
1250       if (PathFileExistsDefExtW(buff, dwWhich))
1251       {
1252         WideCharToMultiByte(0,0,buff,-1,lpszFile,MAX_PATH,0,0);
1253         return TRUE;
1254       }
1255       lpszOtherPath++;
1256     }
1257   }
1258   /* Not found, try system and path dirs */
1259   if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich))
1260   {
1261     WideCharToMultiByte(0,0,szFile,-1,lpszFile,MAX_PATH,0,0);
1262     return TRUE;
1263   }
1264   return FALSE;
1265 }
1266
1267 /*************************************************************************
1268  * @    [SHLWAPI.6]
1269  *
1270  * Unicode version of PathFindOnPathExA.
1271  */
1272 BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich)
1273 {
1274   WCHAR buff[MAX_PATH];
1275
1276   TRACE("(%s,%p,%08lx)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich);
1277
1278   if (!lpszFile || !PathIsFileSpecW(lpszFile))
1279     return FALSE;
1280
1281   /* Search provided directories first */
1282   if (lppszOtherDirs && *lppszOtherDirs)
1283   {
1284     LPCWSTR *lpszOtherPath = lppszOtherDirs;
1285     while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
1286     {
1287       PathCombineW(buff, *lpszOtherPath, lpszFile);
1288       if (PathFileExistsDefExtW(buff, dwWhich))
1289       {
1290         strcpyW(lpszFile, buff);
1291         return TRUE;
1292       }
1293       lpszOtherPath++;
1294     }
1295   }
1296   /* Not found, try system and path dirs */
1297   return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich);
1298 }
1299
1300 /*************************************************************************
1301  * PathFindOnPathA      [SHLWAPI.@]
1302  *
1303  * Search a range of paths for an executable.
1304  *
1305  * PARAMS
1306  *  lpszFile       [I/O] File to search for
1307  *  lppszOtherDirs [I]   Other directories to look in
1308  *
1309  * RETURNS
1310  *  Success: TRUE. The path to the executable is stored in lpszFile.
1311  *  Failure: FALSE. The path to the executable is unchanged.
1312  */
1313 BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs)
1314 {
1315   TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs);
1316   return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0);
1317  }
1318
1319 /*************************************************************************
1320  * PathFindOnPathW      [SHLWAPI.@]
1321  *
1322  * See PathFindOnPathA.
1323  */
1324 BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs)
1325 {
1326   TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs);
1327   return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0);
1328 }
1329
1330 /*************************************************************************
1331  * PathCompactPathExA   [SHLWAPI.@]
1332  *
1333  * Compact a path into a given number of characters.
1334  *
1335  * PARAMS
1336  *  lpszDest [O] Destination for compacted path
1337  *  lpszPath [I] Source path
1338  *  cchMax   [I] Maximum size of compacted path
1339  *  dwFlags  [I] Reserved
1340  *
1341  * RETURNS
1342  *  Success: TRUE. The compacted path is written to lpszDest.
1343  *  Failure: FALSE. lpszPath is undefined.
1344  *
1345  * NOTES
1346  *  If cchMax is given as 0, lpszDest will still be NUL terminated.
1347  *
1348  *  The Win32 version of this function contains a bug: When cchMax == 7,
1349  *  8 bytes will be written to lpszDest. This bug is fixed in the Wine
1350  *  implementation.
1351  *
1352  *  Some relative paths will be different when cchMax == 5 or 6. This occurs
1353  *  because Win32 will insert a "\" in lpszDest, even if one is
1354  *  not present in the original path.
1355  */
1356 BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath,
1357                                UINT cchMax, DWORD dwFlags)
1358 {
1359   BOOL bRet = FALSE;
1360
1361   TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags);
1362
1363   if (lpszPath && lpszDest)
1364   {
1365     WCHAR szPath[MAX_PATH];
1366     WCHAR szDest[MAX_PATH];
1367
1368     MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH);
1369     szDest[0] = '\0';
1370     bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags);
1371     WideCharToMultiByte(0,0,szDest,-1,lpszDest,MAX_PATH,0,0);
1372   }
1373   return bRet;
1374 }
1375
1376 /*************************************************************************
1377  * PathCompactPathExW   [SHLWAPI.@]
1378  *
1379  * See PathCompactPathExA.
1380  */
1381 BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath,
1382                                UINT cchMax, DWORD dwFlags)
1383 {
1384   static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
1385   LPCWSTR lpszFile;
1386   DWORD dwLen, dwFileLen = 0;
1387
1388   TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags);
1389
1390   if (!lpszPath)
1391     return FALSE;
1392
1393   if (!lpszDest)
1394   {
1395     WARN("Invalid lpszDest would crash under Win32!\n");
1396     return FALSE;
1397   }
1398
1399   *lpszDest = '\0';
1400
1401   if (cchMax < 2)
1402     return TRUE;
1403
1404   dwLen = strlenW(lpszPath) + 1;
1405
1406   if (dwLen < cchMax)
1407   {
1408     /* Don't need to compact */
1409     memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
1410     return TRUE;
1411   }
1412
1413   /* Path must be compacted to fit into lpszDest */
1414   lpszFile = PathFindFileNameW(lpszPath);
1415   dwFileLen = lpszPath + dwLen - lpszFile;
1416
1417   if (dwFileLen == dwLen)
1418   {
1419     /* No root in psth */
1420     if (cchMax <= 4)
1421     {
1422       while (--cchMax > 0) /* No room left for anything but ellipses */
1423         *lpszDest++ = '.';
1424       *lpszDest = '\0';
1425       return TRUE;
1426     }
1427     /* Compact the file name with ellipses at the end */
1428     cchMax -= 4;
1429     memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
1430     strcpyW(lpszDest + cchMax, szEllipses);
1431     return TRUE;
1432   }
1433   /* We have a root in the path */
1434   lpszFile--; /* Start compacted filename with the path separator */
1435   dwFileLen++;
1436
1437   if (dwFileLen + 3 > cchMax)
1438   {
1439     /* Compact the file name */
1440     if (cchMax <= 4)
1441     {
1442       while (--cchMax > 0) /* No room left for anything but ellipses */
1443         *lpszDest++ = '.';
1444       *lpszDest = '\0';
1445       return TRUE;
1446     }
1447     strcpyW(lpszDest, szEllipses);
1448     lpszDest += 3;
1449     cchMax -= 4;
1450     *lpszDest++ = *lpszFile++;
1451     if (cchMax <= 4)
1452     {
1453       while (--cchMax > 0) /* No room left for anything but ellipses */
1454         *lpszDest++ = '.';
1455       *lpszDest = '\0';
1456       return TRUE;
1457     }
1458     cchMax -= 4;
1459     memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
1460     strcpyW(lpszDest + cchMax, szEllipses);
1461     return TRUE;
1462   }
1463
1464   /* Only the root needs to be Compacted */
1465   dwLen = cchMax - dwFileLen - 3;
1466   memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
1467   strcpyW(lpszDest + dwLen, szEllipses);
1468   strcpyW(lpszDest + dwLen + 3, lpszFile);
1469   return TRUE;
1470 }
1471
1472 /*************************************************************************
1473  * PathIsRelativeA      [SHLWAPI.@]
1474  *
1475  * Determine if a path is a relative path.
1476  *
1477  * PARAMS
1478  *  lpszPath [I] Path to check
1479  *
1480  * RETURNS
1481  *  TRUE:  The path is relative, or is invalid.
1482  *  FALSE: The path is not relative.
1483  */
1484 BOOL WINAPI PathIsRelativeA (LPCSTR lpszPath)
1485 {
1486   TRACE("(%s)\n",debugstr_a(lpszPath));
1487
1488   if (!lpszPath || !*lpszPath || IsDBCSLeadByte(*lpszPath))
1489     return TRUE;
1490   if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':'))
1491     return FALSE;
1492   return TRUE;
1493 }
1494
1495 /*************************************************************************
1496  *  PathIsRelativeW     [SHLWAPI.@]
1497  *
1498  * See PathIsRelativeA.
1499  */
1500 BOOL WINAPI PathIsRelativeW (LPCWSTR lpszPath)
1501 {
1502   TRACE("(%s)\n",debugstr_w(lpszPath));
1503
1504   if (!lpszPath || !*lpszPath)
1505     return TRUE;
1506   if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':'))
1507     return FALSE;
1508   return TRUE;
1509 }
1510
1511 /*************************************************************************
1512  * PathIsRootA          [SHLWAPI.@]
1513  *
1514  * Determine if a path is a root path.
1515  *
1516  * PARAMS
1517  *  lpszPath [I] Path to check
1518  *
1519  * RETURNS
1520  *  TRUE  If lpszPath is valid and a root path,
1521  *  FALSE Otherwise
1522  */
1523 BOOL WINAPI PathIsRootA(LPCSTR lpszPath)
1524 {
1525   TRACE("(%s)\n", debugstr_a(lpszPath));
1526
1527   if (lpszPath && *lpszPath)
1528   {
1529     if (*lpszPath == '\\')
1530     {
1531       if (!lpszPath[1])
1532         return TRUE; /* \ */
1533       else if (lpszPath[1]=='\\')
1534       {
1535         BOOL bSeenSlash = FALSE;
1536         lpszPath += 2;
1537
1538         /* Check for UNC root path */
1539         while (*lpszPath)
1540         {
1541           if (*lpszPath == '\\')
1542           {
1543             if (bSeenSlash)
1544               return FALSE;
1545             bSeenSlash = TRUE;
1546           }
1547           lpszPath = CharNextA(lpszPath);
1548         }
1549         return TRUE;
1550       }
1551     }
1552     else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0')
1553       return TRUE; /* X:\ */
1554   }
1555   return FALSE;
1556 }
1557
1558 /*************************************************************************
1559  * PathIsRootW          [SHLWAPI.@]
1560  *
1561  * See PathIsRootA.
1562  */
1563 BOOL WINAPI PathIsRootW(LPCWSTR lpszPath)
1564 {
1565   TRACE("(%s)\n", debugstr_w(lpszPath));
1566
1567   if (lpszPath && *lpszPath)
1568   {
1569     if (*lpszPath == '\\')
1570     {
1571       if (!lpszPath[1])
1572         return TRUE; /* \ */
1573       else if (lpszPath[1]=='\\')
1574       {
1575         BOOL bSeenSlash = FALSE;
1576         lpszPath += 2;
1577
1578         /* Check for UNC root path */
1579         while (*lpszPath)
1580         {
1581           if (*lpszPath == '\\')
1582           {
1583             if (bSeenSlash)
1584               return FALSE;
1585             bSeenSlash = TRUE;
1586           }
1587           lpszPath = CharNextW(lpszPath);
1588         }
1589         return TRUE;
1590       }
1591     }
1592     else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0')
1593       return TRUE; /* X:\ */
1594   }
1595   return FALSE;
1596 }
1597
1598 /*************************************************************************
1599  * PathIsDirectoryA     [SHLWAPI.@]
1600  *
1601  * Determine if a path is a valid directory
1602  *
1603  * PARAMS
1604  *  lpszPath [I] Path to check.
1605  *
1606  * RETURNS
1607  *  FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
1608  *  FALSE if lpszPath is invalid or not a directory.
1609  *
1610  * NOTES
1611  *  Although this function is prototyped as returning a BOOL, it returns
1612  *  FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
1613  *
1614  *|  if (PathIsDirectoryA("c:\\windows\\") == TRUE)
1615  *|    ...
1616  *
1617  *  will always fail.
1618  */
1619 BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath)
1620 {
1621   DWORD dwAttr;
1622
1623   TRACE("(%s)\n", debugstr_a(lpszPath));
1624
1625   if (!lpszPath || PathIsUNCServerA(lpszPath))
1626     return FALSE;
1627
1628   if (PathIsUNCServerShareA(lpszPath))
1629   {
1630     FIXME("UNC Server Share not yet supported - FAILING\n");
1631     return FALSE;
1632   }
1633
1634   if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES)
1635     return FALSE;
1636   return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
1637 }
1638
1639 /*************************************************************************
1640  * PathIsDirectoryW     [SHLWAPI.@]
1641  *
1642  * See PathIsDirectoryA.
1643  */
1644 BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
1645 {
1646   DWORD dwAttr;
1647
1648   TRACE("(%s)\n", debugstr_w(lpszPath));
1649
1650   if (!lpszPath || PathIsUNCServerW(lpszPath))
1651     return FALSE;
1652
1653   if (PathIsUNCServerShareW(lpszPath))
1654   {
1655     FIXME("UNC Server Share not yet supported - FAILING\n");
1656     return FALSE;
1657   }
1658
1659   if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
1660     return FALSE;
1661   return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
1662 }
1663
1664 /*************************************************************************
1665  * PathFileExistsA      [SHLWAPI.@]
1666  *
1667  * Determine if a file exists.
1668  *
1669  * PARAMS
1670  *  lpszPath [I] Path to check
1671  *
1672  * RETURNS
1673  *  TRUE  If the file exists and is readable
1674  *  FALSE Otherwise
1675  */
1676 BOOL WINAPI PathFileExistsA(LPCSTR lpszPath)
1677 {
1678   UINT iPrevErrMode;
1679   DWORD dwAttr;
1680
1681   TRACE("(%s)\n",debugstr_a(lpszPath));
1682
1683   if (!lpszPath)
1684     return FALSE;
1685
1686   iPrevErrMode = SetErrorMode(1);
1687   dwAttr = GetFileAttributesA(lpszPath);
1688   SetErrorMode(iPrevErrMode);
1689   return dwAttr == INVALID_FILE_ATTRIBUTES ? FALSE : TRUE;
1690 }
1691
1692 /*************************************************************************
1693  * PathFileExistsW      [SHLWAPI.@]
1694  *
1695  * See PathFileExistsA.
1696  */
1697 BOOL WINAPI PathFileExistsW(LPCWSTR lpszPath)
1698 {
1699   UINT iPrevErrMode;
1700   DWORD dwAttr;
1701
1702   TRACE("(%s)\n",debugstr_w(lpszPath));
1703
1704   if (!lpszPath)
1705     return FALSE;
1706
1707   iPrevErrMode = SetErrorMode(1);
1708   dwAttr = GetFileAttributesW(lpszPath);
1709   SetErrorMode(iPrevErrMode);
1710   return dwAttr == INVALID_FILE_ATTRIBUTES ? FALSE : TRUE;
1711 }
1712
1713 /*************************************************************************
1714  * PathMatchSingleMaskA [internal]
1715  */
1716 static BOOL WINAPI PathMatchSingleMaskA(LPCSTR name, LPCSTR mask)
1717 {
1718   while (*name && *mask && *mask!=';')
1719   {
1720     if (*mask == '*')
1721     {
1722       do
1723       {
1724         if (PathMatchSingleMaskA(name,mask+1))
1725           return TRUE;  /* try substrings */
1726       } while (*name++);
1727       return FALSE;
1728     }
1729
1730     if (toupper(*mask) != toupper(*name) && *mask != '?')
1731       return FALSE;
1732
1733     name = CharNextA(name);
1734     mask = CharNextA(mask);
1735   }
1736
1737   if (!*name)
1738   {
1739     while (*mask == '*')
1740       mask++;
1741     if (!*mask || *mask == ';')
1742       return TRUE;
1743   }
1744   return FALSE;
1745 }
1746
1747 /*************************************************************************
1748  * PathMatchSingleMaskW [internal]
1749  */
1750 static BOOL WINAPI PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask)
1751 {
1752   while (*name && *mask && *mask != ';')
1753   {
1754     if (*mask == '*')
1755     {
1756       do
1757       {
1758         if (PathMatchSingleMaskW(name,mask+1))
1759           return TRUE;  /* try substrings */
1760       } while (*name++);
1761       return FALSE;
1762     }
1763
1764     if (toupperW(*mask) != toupperW(*name) && *mask != '?')
1765       return FALSE;
1766
1767     name = CharNextW(name);
1768     mask = CharNextW(mask);
1769   }
1770   if (!*name)
1771   {
1772     while (*mask == '*')
1773       mask++;
1774     if (!*mask || *mask == ';')
1775       return TRUE;
1776   }
1777   return FALSE;
1778 }
1779
1780 /*************************************************************************
1781  * PathMatchSpecA       [SHLWAPI.@]
1782  *
1783  * Determine if a path matches one or more search masks.
1784  *
1785  * PARAMS
1786  *  lpszPath [I] Path to check
1787  *  lpszMask [I] Search mask(s)
1788  *
1789  * RETURNS
1790  *  TRUE  If lpszPath is valid and is matched
1791  *  FALSE Otherwise
1792  *
1793  * NOTES
1794  *  Multiple search masks may be given if they are separated by ";". The
1795  *  pattern "*.*" is treated specially in that it matches all paths (for
1796  *  backwards compatibility with DOS).
1797  */
1798 BOOL WINAPI PathMatchSpecA(LPCSTR lpszPath, LPCSTR lpszMask)
1799 {
1800   TRACE("(%s,%s)\n", lpszPath, lpszMask);
1801
1802   if (!lstrcmpA(lpszMask, "*.*"))
1803     return TRUE; /* Matches every path */
1804
1805   while (*lpszMask)
1806   {
1807     if (PathMatchSingleMaskA(lpszPath, lpszMask))
1808       return TRUE; /* Matches the current mask */
1809
1810     while (*lpszMask && *lpszMask != ';')
1811       lpszMask = CharNextA(lpszMask);
1812
1813     if (*lpszMask == ';')
1814     {
1815       lpszMask++;
1816       while (*lpszMask == ' ')
1817         lpszMask++; /*  masks may be separated by "; " */
1818     }
1819   }
1820   return FALSE;
1821 }
1822
1823 /*************************************************************************
1824  * PathMatchSpecW       [SHLWAPI.@]
1825  *
1826  * See PathMatchSpecA.
1827  */
1828 BOOL WINAPI PathMatchSpecW(LPCWSTR lpszPath, LPCWSTR lpszMask)
1829 {
1830   static const WCHAR szStarDotStar[] = { '*', '.', '*', '\0' };
1831
1832   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszMask));
1833
1834   if (!lstrcmpW(lpszMask, szStarDotStar))
1835     return TRUE; /* Matches every path */
1836
1837   while (*lpszMask)
1838   {
1839     if (PathMatchSingleMaskW(lpszPath, lpszMask))
1840       return TRUE; /* Matches the current path */
1841
1842     while (*lpszMask && *lpszMask != ';')
1843       lpszMask++;
1844
1845     if (*lpszMask == ';')
1846     {
1847       lpszMask++;
1848       while (*lpszMask == ' ')
1849         lpszMask++; /* Masks may be separated by "; " */
1850     }
1851   }
1852   return FALSE;
1853 }
1854
1855 /*************************************************************************
1856  * PathIsSameRootA      [SHLWAPI.@]
1857  *
1858  * Determine if two paths share the same root.
1859  *
1860  * PARAMS
1861  *  lpszPath1 [I] Source path
1862  *  lpszPath2 [I] Path to compare with
1863  *
1864  * RETURNS
1865  *  TRUE  If both paths are valid and share the same root.
1866  *  FALSE If either path is invalid or the paths do not share the same root.
1867  */
1868 BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2)
1869 {
1870   LPCSTR lpszStart;
1871   int dwLen;
1872
1873   TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2));
1874
1875   if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1)))
1876     return FALSE;
1877
1878   dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1;
1879   if (lpszStart - lpszPath1 > dwLen)
1880     return FALSE; /* Paths not common up to length of the root */
1881   return TRUE;
1882 }
1883
1884 /*************************************************************************
1885  * PathIsSameRootW      [SHLWAPI.@]
1886  *
1887  * See PathIsSameRootA.
1888  */
1889 BOOL WINAPI PathIsSameRootW(LPCWSTR lpszPath1, LPCWSTR lpszPath2)
1890 {
1891   LPCWSTR lpszStart;
1892   int dwLen;
1893
1894   TRACE("(%s,%s)\n", debugstr_w(lpszPath1), debugstr_w(lpszPath2));
1895
1896   if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootW(lpszPath1)))
1897     return FALSE;
1898
1899   dwLen = PathCommonPrefixW(lpszPath1, lpszPath2, NULL) + 1;
1900   if (lpszStart - lpszPath1 > dwLen)
1901     return FALSE; /* Paths not common up to length of the root */
1902   return TRUE;
1903 }
1904
1905 /*************************************************************************
1906  * PathIsContentTypeA   [SHLWAPI.@]
1907  *
1908  * Determine if a file is of a given registered content type.
1909  *
1910  * PARAMS
1911  *  lpszPath        [I] File to check
1912  *  lpszContentType [I] Content type to check for
1913  *
1914  * RETURNS
1915  *  TRUE  If lpszPath is a given registered content type,
1916  *  FALSE Otherwise.
1917  *
1918  * NOTES
1919  *  This function looks up the registered content type for lpszPath. If
1920  *  a content type is registered, it is compared (case insensitively) to
1921  *  lpszContentType. Only if this matches does the function succeed.
1922  */
1923 BOOL WINAPI PathIsContentTypeA(LPCSTR lpszPath, LPCSTR lpszContentType)
1924 {
1925   LPCSTR szExt;
1926   DWORD dwDummy;
1927   char szBuff[MAX_PATH];
1928
1929   TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszContentType));
1930
1931   if (lpszPath && (szExt = PathFindExtensionA(lpszPath)) && *szExt &&
1932       !SHGetValueA(HKEY_CLASSES_ROOT, szExt, "Content Type",
1933                    REG_NONE, szBuff, &dwDummy) &&
1934       !strcasecmp(lpszContentType, szBuff))
1935   {
1936     return TRUE;
1937   }
1938   return FALSE;
1939 }
1940
1941 /*************************************************************************
1942  * PathIsContentTypeW   [SHLWAPI.@]
1943  *
1944  * See PathIsContentTypeA.
1945  */
1946 BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType)
1947 {
1948   static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
1949   LPCWSTR szExt;
1950   DWORD dwDummy;
1951   WCHAR szBuff[MAX_PATH];
1952
1953   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType));
1954
1955   if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt &&
1956       !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType,
1957                    REG_NONE, szBuff, &dwDummy) &&
1958       !strcmpiW(lpszContentType, szBuff))
1959   {
1960     return TRUE;
1961   }
1962   return FALSE;
1963 }
1964
1965 /*************************************************************************
1966  * PathIsFileSpecA   [SHLWAPI.@]
1967  *
1968  * Determine if a path is a file specification.
1969  *
1970  * PARAMS
1971  *  lpszPath [I] Path to chack
1972  *
1973  * RETURNS
1974  *  TRUE  If lpszPath is a file specification (i.e. Contains no directories).
1975  *  FALSE Otherwise.
1976  */
1977 BOOL WINAPI PathIsFileSpecA(LPCSTR lpszPath)
1978 {
1979   TRACE("(%s)\n", debugstr_a(lpszPath));
1980
1981   if (!lpszPath)
1982     return FALSE;
1983
1984   while (*lpszPath)
1985   {
1986     if (*lpszPath == '\\' || *lpszPath == ':')
1987       return FALSE;
1988     lpszPath = CharNextA(lpszPath);
1989   }
1990   return TRUE;
1991 }
1992
1993 /*************************************************************************
1994  * PathIsFileSpecW   [SHLWAPI.@]
1995  *
1996  * See PathIsFileSpecA.
1997  */
1998 BOOL WINAPI PathIsFileSpecW(LPCWSTR lpszPath)
1999 {
2000   TRACE("(%s)\n", debugstr_w(lpszPath));
2001
2002   if (!lpszPath)
2003     return FALSE;
2004
2005   while (*lpszPath)
2006   {
2007     if (*lpszPath == '\\' || *lpszPath == ':')
2008       return FALSE;
2009     lpszPath = CharNextW(lpszPath);
2010   }
2011   return TRUE;
2012 }
2013
2014 /*************************************************************************
2015  * PathIsPrefixA   [SHLWAPI.@]
2016  *
2017  * Determine if a path is a prefix of another.
2018  *
2019  * PARAMS
2020  *  lpszPrefix [I] Prefix
2021  *  lpszPath   [I] Path to check
2022  *
2023  * RETURNS
2024  *  TRUE  If lpszPath has lpszPrefix as its prefix,
2025  *  FALSE If either path is NULL or lpszPrefix is not a prefix
2026  */
2027 BOOL WINAPI PathIsPrefixA (LPCSTR lpszPrefix, LPCSTR lpszPath)
2028 {
2029   TRACE("(%s,%s)\n", debugstr_a(lpszPrefix), debugstr_a(lpszPath));
2030
2031   if (lpszPrefix && lpszPath &&
2032       PathCommonPrefixA(lpszPath, lpszPrefix, NULL) == (int)strlen(lpszPrefix))
2033     return TRUE;
2034   return FALSE;
2035 }
2036
2037 /*************************************************************************
2038  *  PathIsPrefixW   [SHLWAPI.@]
2039  *
2040  *  See PathIsPrefixA.
2041  */
2042 BOOL WINAPI PathIsPrefixW(LPCWSTR lpszPrefix, LPCWSTR lpszPath)
2043 {
2044   TRACE("(%s,%s)\n", debugstr_w(lpszPrefix), debugstr_w(lpszPath));
2045
2046   if (lpszPrefix && lpszPath &&
2047       PathCommonPrefixW(lpszPath, lpszPrefix, NULL) == (int)strlenW(lpszPrefix))
2048     return TRUE;
2049   return FALSE;
2050 }
2051
2052 /*************************************************************************
2053  * PathIsSystemFolderA   [SHLWAPI.@]
2054  *
2055  * Determine if a path or file attributes are a system folder.
2056  *
2057  * PARAMS
2058  *  lpszPath  [I] Path to check.
2059  *  dwAttrib  [I] Attributes to check, if lpszPath is NULL.
2060  *
2061  * RETURNS
2062  *  TRUE   If lpszPath or dwAttrib are a system folder.
2063  *  FALSE  If GetFileAttributesA() fails or neither parameter is a system folder.
2064  */
2065 BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib)
2066 {
2067   TRACE("(%s,0x%08lx)\n", debugstr_a(lpszPath), dwAttrib);
2068
2069   if (lpszPath && *lpszPath)
2070     dwAttrib = GetFileAttributesA(lpszPath);
2071
2072   if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
2073       !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
2074     return FALSE;
2075   return TRUE;
2076 }
2077
2078 /*************************************************************************
2079  * PathIsSystemFolderW   [SHLWAPI.@]
2080  *
2081  * See PathIsSystemFolderA.
2082  */
2083 BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib)
2084 {
2085   TRACE("(%s,0x%08lx)\n", debugstr_w(lpszPath), dwAttrib);
2086
2087   if (lpszPath && *lpszPath)
2088     dwAttrib = GetFileAttributesW(lpszPath);
2089
2090   if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
2091       !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
2092     return FALSE;
2093   return TRUE;
2094 }
2095
2096 /*************************************************************************
2097  * PathIsUNCA           [SHLWAPI.@]
2098  *
2099  * Determine if a path is in UNC format.
2100  *
2101  * PARAMS
2102  *  lpszPath [I] Path to check
2103  *
2104  * RETURNS
2105  *  TRUE: The path is UNC.
2106  *  FALSE: The path is not UNC or is NULL.
2107  */
2108 BOOL WINAPI PathIsUNCA(LPCSTR lpszPath)
2109 {
2110   TRACE("(%s)\n",debugstr_a(lpszPath));
2111
2112   if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\'))
2113     return TRUE;
2114   return FALSE;
2115 }
2116
2117 /*************************************************************************
2118  * PathIsUNCW           [SHLWAPI.@]
2119  *
2120  * See PathIsUNCA.
2121  */
2122 BOOL WINAPI PathIsUNCW(LPCWSTR lpszPath)
2123 {
2124   TRACE("(%s)\n",debugstr_w(lpszPath));
2125
2126   if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\'))
2127     return TRUE;
2128   return FALSE;
2129 }
2130
2131 /*************************************************************************
2132  * PathIsUNCServerA   [SHLWAPI.@]
2133  *
2134  * Determine if a path is a UNC server name ("\\SHARENAME").
2135  *
2136  * PARAMS
2137  *  lpszPath  [I] Path to check.
2138  *
2139  * RETURNS
2140  *  TRUE   If lpszPath is a valid UNC server name.
2141  *  FALSE  Otherwise.
2142  *
2143  * NOTES
2144  *  This routine is bug compatible with Win32: Server names with a
2145  *  trailing backslash (e.g. "\\FOO\"), return FALSE incorrectly.
2146  *  Fixing this bug may break other shlwapi functions!
2147  */
2148 BOOL WINAPI PathIsUNCServerA(LPCSTR lpszPath)
2149 {
2150   TRACE("(%s)\n", debugstr_a(lpszPath));
2151
2152   if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2153   {
2154     while (*lpszPath)
2155     {
2156       if (*lpszPath == '\\')
2157         return FALSE;
2158       lpszPath = CharNextA(lpszPath);
2159     }
2160     return TRUE;
2161   }
2162   return FALSE;
2163 }
2164
2165 /*************************************************************************
2166  * PathIsUNCServerW   [SHLWAPI.@]
2167  *
2168  * See PathIsUNCServerA.
2169  */
2170 BOOL WINAPI PathIsUNCServerW(LPCWSTR lpszPath)
2171 {
2172   TRACE("(%s)\n", debugstr_w(lpszPath));
2173
2174   if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2175   {
2176     while (*lpszPath)
2177     {
2178       if (*lpszPath == '\\')
2179         return FALSE;
2180       lpszPath = CharNextW(lpszPath);
2181     }
2182     return TRUE;
2183   }
2184   return FALSE;
2185 }
2186
2187 /*************************************************************************
2188  * PathIsUNCServerShareA   [SHLWAPI.@]
2189  *
2190  * Determine if a path is a UNC server share ("\\SHARENAME\SHARE").
2191  *
2192  * PARAMS
2193  *  lpszPath  [I] Path to check.
2194  *
2195  * RETURNS
2196  *  TRUE   If lpszPath is a valid UNC server share.
2197  *  FALSE  Otherwise.
2198  *
2199  * NOTES
2200  *  This routine is bug compatible with Win32: Server shares with a
2201  *  trailing backslash (e.g. "\\FOO\BAR\"), return FALSE incorrectly.
2202  *  Fixing this bug may break other shlwapi functions!
2203  */
2204 BOOL WINAPI PathIsUNCServerShareA(LPCSTR lpszPath)
2205 {
2206   TRACE("(%s)\n", debugstr_a(lpszPath));
2207
2208   if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2209   {
2210     BOOL bSeenSlash = FALSE;
2211     while (*lpszPath)
2212     {
2213       if (*lpszPath == '\\')
2214       {
2215         if (bSeenSlash)
2216           return FALSE;
2217         bSeenSlash = TRUE;
2218       }
2219       lpszPath = CharNextA(lpszPath);
2220     }
2221     return bSeenSlash;
2222   }
2223   return FALSE;
2224 }
2225
2226 /*************************************************************************
2227  * PathIsUNCServerShareW   [SHLWAPI.@]
2228  *
2229  * See PathIsUNCServerShareA.
2230  */
2231 BOOL WINAPI PathIsUNCServerShareW(LPCWSTR lpszPath)
2232 {
2233   TRACE("(%s)\n", debugstr_w(lpszPath));
2234
2235   if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2236   {
2237     BOOL bSeenSlash = FALSE;
2238     while (*lpszPath)
2239     {
2240       if (*lpszPath == '\\')
2241       {
2242         if (bSeenSlash)
2243           return FALSE;
2244         bSeenSlash = TRUE;
2245       }
2246       lpszPath = CharNextW(lpszPath);
2247     }
2248     return bSeenSlash;
2249   }
2250   return FALSE;
2251 }
2252
2253 /*************************************************************************
2254  * PathCanonicalizeA   [SHLWAPI.@]
2255  *
2256  * Convert a path to its canonical form.
2257  *
2258  * PARAMS
2259  *  lpszBuf  [O] Output path
2260  *  lpszPath [I] Path to cnonicalize
2261  *
2262  * RETURNS
2263  *  Success: TRUE.  lpszBuf contains the output path,
2264  *  Failure: FALSE, If input path is invalid. lpszBuf is undefined
2265  */
2266 BOOL WINAPI PathCanonicalizeA(LPSTR lpszBuf, LPCSTR lpszPath)
2267 {
2268   BOOL bRet = FALSE;
2269
2270   TRACE("(%p,%s)\n", lpszBuf, debugstr_a(lpszPath));
2271
2272   if (lpszBuf)
2273     *lpszBuf = '\0';
2274
2275   if (!lpszBuf || !lpszPath)
2276     SetLastError(ERROR_INVALID_PARAMETER);
2277   else
2278   {
2279     WCHAR szPath[MAX_PATH];
2280     WCHAR szBuff[MAX_PATH];
2281     MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH);
2282     bRet = PathCanonicalizeW(szBuff, szPath);
2283     WideCharToMultiByte(0,0,szBuff,-1,lpszBuf,MAX_PATH,0,0);
2284   }
2285   return bRet;
2286 }
2287
2288
2289 /*************************************************************************
2290  * PathCanonicalizeW   [SHLWAPI.@]
2291  *
2292  * See PathCanonicalizeA.
2293  */
2294 BOOL WINAPI PathCanonicalizeW(LPWSTR lpszBuf, LPCWSTR lpszPath)
2295 {
2296   LPWSTR lpszDst = lpszBuf;
2297   LPCWSTR lpszSrc = lpszPath;
2298
2299   TRACE("(%p,%s)\n", lpszBuf, debugstr_w(lpszPath));
2300
2301   if (lpszBuf)
2302     *lpszDst = '\0';
2303
2304   if (!lpszBuf || !lpszPath)
2305   {
2306     SetLastError(ERROR_INVALID_PARAMETER);
2307     return FALSE;
2308   }
2309
2310   if (!*lpszPath)
2311   {
2312     *lpszBuf++ = '\\';
2313     *lpszBuf = '\0';
2314     return TRUE;
2315   }
2316
2317   /* Copy path root */
2318   if (*lpszSrc == '\\')
2319   {
2320     *lpszDst++ = *lpszSrc++;
2321   }
2322   else if (*lpszSrc && lpszSrc[1] == ':')
2323   {
2324     /* X:\ */
2325     *lpszDst++ = *lpszSrc++;
2326     *lpszDst++ = *lpszSrc++;
2327     if (*lpszSrc == '\\')
2328       *lpszDst++ = *lpszSrc++;
2329   }
2330
2331   /* Canonicalize the rest of the path */
2332   while (*lpszSrc)
2333   {
2334     if (*lpszSrc == '.')
2335     {
2336       if (lpszSrc[1] == '\\' && (lpszSrc == lpszPath || lpszSrc[-1] == '\\' || lpszSrc[-1] == ':'))
2337       {
2338         lpszSrc += 2; /* Skip .\ */
2339       }
2340       else if (lpszSrc[1] == '.' && (lpszDst == lpszBuf || lpszDst[-1] == '\\'))
2341       {
2342         /* \.. backs up a directory, over the root if it has no \ following X:.
2343          * .. is ignored if it would remove a UNC server name or inital \\
2344          */
2345         if (lpszDst != lpszBuf)
2346         {
2347           *lpszDst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */
2348           if (lpszDst > lpszBuf+1 && lpszDst[-1] == '\\' &&
2349              (lpszDst[-2] != '\\' || lpszDst > lpszBuf+2))
2350           {
2351             if (lpszDst[-2] == ':' && (lpszDst > lpszBuf+3 || lpszDst[-3] == ':'))
2352             {
2353               lpszDst -= 2;
2354               while (lpszDst > lpszBuf && *lpszDst != '\\')
2355                 lpszDst--;
2356               if (*lpszDst == '\\')
2357                 lpszDst++; /* Reset to last '\' */
2358               else
2359                 lpszDst = lpszBuf; /* Start path again from new root */
2360             }
2361             else if (lpszDst[-2] != ':' && !PathIsUNCServerShareW(lpszBuf))
2362               lpszDst -= 2;
2363           }
2364           while (lpszDst > lpszBuf && *lpszDst != '\\')
2365             lpszDst--;
2366           if (lpszDst == lpszBuf)
2367           {
2368             *lpszDst++ = '\\';
2369             lpszSrc++;
2370           }
2371         }
2372         lpszSrc += 2; /* Skip .. in src path */
2373       }
2374       else
2375         *lpszDst++ = *lpszSrc++;
2376     }
2377     else
2378       *lpszDst++ = *lpszSrc++;
2379   }
2380   /* Append \ to naked drive specs */
2381   if (lpszDst - lpszBuf == 2 && lpszDst[-1] == ':')
2382     *lpszDst++ = '\\';
2383   *lpszDst++ = '\0';
2384   return TRUE;
2385 }
2386
2387 /*************************************************************************
2388  * PathFindNextComponentA   [SHLWAPI.@]
2389  *
2390  * Find the next component in a path.
2391  *
2392  * PARAMS
2393  *   lpszPath [I] Path to find next component in
2394  *
2395  * RETURNS
2396  *  Success: A pointer to the next component, or the end of the string.
2397  *  Failure: NULL, If lpszPath is invalid
2398  *
2399  * NOTES
2400  *  A 'component' is either a backslash character (\) or UNC marker (\\).
2401  *  Because of this, relative paths (e.g "c:foo") are regarded as having
2402  *  only one component.
2403  */
2404 LPSTR WINAPI PathFindNextComponentA(LPCSTR lpszPath)
2405 {
2406   LPSTR lpszSlash;
2407
2408   TRACE("(%s)\n", debugstr_a(lpszPath));
2409
2410   if(!lpszPath || !*lpszPath)
2411     return NULL;
2412
2413   if ((lpszSlash = StrChrA(lpszPath, '\\')))
2414   {
2415     if (lpszSlash[1] == '\\')
2416       lpszSlash++;
2417     return lpszSlash + 1;
2418   }
2419   return (LPSTR)lpszPath + strlen(lpszPath);
2420 }
2421
2422 /*************************************************************************
2423  * PathFindNextComponentW   [SHLWAPI.@]
2424  *
2425  * See PathFindNextComponentA.
2426  */
2427 LPWSTR WINAPI PathFindNextComponentW(LPCWSTR lpszPath)
2428 {
2429   LPWSTR lpszSlash;
2430
2431   TRACE("(%s)\n", debugstr_w(lpszPath));
2432
2433   if(!lpszPath || !*lpszPath)
2434     return NULL;
2435
2436   if ((lpszSlash = StrChrW(lpszPath, '\\')))
2437   {
2438     if (lpszSlash[1] == '\\')
2439       lpszSlash++;
2440     return lpszSlash + 1;
2441   }
2442   return (LPWSTR)lpszPath + strlenW(lpszPath);
2443 }
2444
2445 /*************************************************************************
2446  * PathAddExtensionA   [SHLWAPI.@]
2447  *
2448  * Add a file extension to a path
2449  *
2450  * PARAMS
2451  *  lpszPath      [I/O] Path to add extension to
2452  *  lpszExtension [I]   Extension to add to lpszPath
2453  *
2454  * RETURNS
2455  *  TRUE  If the path was modified,
2456  *  FALSE If lpszPath or lpszExtension are invalid, lpszPath has an
2457  *        extension allready, or the new path length is too big.
2458  *
2459  * FIXME
2460  *  What version of shlwapi.dll adds "exe" if lpszExtension is NULL? Win2k
2461  *  does not do this, so the behaviour was removed.
2462  */
2463 BOOL WINAPI PathAddExtensionA(LPSTR lpszPath, LPCSTR lpszExtension)
2464 {
2465   size_t dwLen;
2466
2467   TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExtension));
2468
2469   if (!lpszPath || !lpszExtension || *(PathFindExtensionA(lpszPath)))
2470     return FALSE;
2471
2472   dwLen = strlen(lpszPath);
2473
2474   if (dwLen + strlen(lpszExtension) >= MAX_PATH)
2475     return FALSE;
2476
2477   strcpy(lpszPath + dwLen, lpszExtension);
2478   return TRUE;
2479 }
2480
2481 /*************************************************************************
2482  * PathAddExtensionW   [SHLWAPI.@]
2483  *
2484  * See PathAddExtensionA.
2485  */
2486 BOOL WINAPI PathAddExtensionW(LPWSTR lpszPath, LPCWSTR lpszExtension)
2487 {
2488   size_t dwLen;
2489
2490   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExtension));
2491
2492   if (!lpszPath || !lpszExtension || *(PathFindExtensionW(lpszPath)))
2493     return FALSE;
2494
2495   dwLen = strlenW(lpszPath);
2496
2497   if (dwLen + strlenW(lpszExtension) >= MAX_PATH)
2498     return FALSE;
2499
2500   strcpyW(lpszPath + dwLen, lpszExtension);
2501   return TRUE;
2502 }
2503
2504 /*************************************************************************
2505  * PathMakePrettyA   [SHLWAPI.@]
2506  *
2507  * Convert an uppercase DOS filename into lowercase.
2508  *
2509  * PARAMS
2510  *  lpszPath [I/O] Path to convert.
2511  *
2512  * RETURNS
2513  *  TRUE  If the path was an uppercase DOS path and was converted,
2514  *  FALSE Otherwise.
2515  */
2516 BOOL WINAPI PathMakePrettyA(LPSTR lpszPath)
2517 {
2518   LPSTR pszIter = lpszPath;
2519
2520   TRACE("(%s)\n", debugstr_a(lpszPath));
2521
2522   if (!pszIter || !*pszIter)
2523     return FALSE;
2524
2525   while (*pszIter)
2526   {
2527     if (islower(*pszIter) || IsDBCSLeadByte(*pszIter))
2528       return FALSE; /* Not DOS path */
2529     pszIter++;
2530   }
2531   pszIter = lpszPath + 1;
2532   while (*pszIter)
2533   {
2534     *pszIter = tolower(*pszIter);
2535     pszIter++;
2536   }
2537   return TRUE;
2538 }
2539
2540 /*************************************************************************
2541  * PathMakePrettyW   [SHLWAPI.@]
2542  *
2543  * See PathMakePrettyA.
2544  */
2545 BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath)
2546 {
2547   LPWSTR pszIter = lpszPath;
2548
2549   TRACE("(%s)\n", debugstr_w(lpszPath));
2550
2551   if (!pszIter || !*pszIter)
2552     return FALSE;
2553
2554   while (*pszIter)
2555   {
2556     if (islowerW(*pszIter))
2557       return FALSE; /* Not DOS path */
2558     pszIter++;
2559   }
2560   pszIter = lpszPath + 1;
2561   while (*pszIter)
2562   {
2563     *pszIter = tolowerW(*pszIter);
2564     pszIter++;
2565   }
2566   return TRUE;
2567 }
2568
2569 /*************************************************************************
2570  * PathCommonPrefixA   [SHLWAPI.@]
2571  *
2572  * Determine the length of the common prefix between two paths.
2573  *
2574  * PARAMS
2575  *  lpszFile1 [I] First path for comparison
2576  *  lpszFile2 [I] Second path for comparison
2577  *  achPath   [O] Destination for common prefix string
2578  *
2579  * RETURNS
2580  *  The length of the common prefix. This is 0 if there is no common
2581  *  prefix between the paths or if any parameters are invalid. If the prefix
2582  *  is non-zero and achPath is not NULL, achPath is filled with the common
2583  *  part of the prefix and NUL terminated.
2584  *
2585  * NOTES
2586  *  A common prefix of 2 is always returned as 3. It is thus possible for
2587  *  the length returned to be invalid (i.e. Longer than one or both of the
2588  *  strings given as parameters). This Win32 behaviour has been implemented
2589  *  here, and cannot be changed (fixed?) without breaking other SHLWAPI calls.
2590  *  To work around this when using this function, always check that the byte
2591  *  at [common_prefix_len-1] is not a NUL. If it is, deduct 1 from the prefix.
2592  */
2593 int WINAPI PathCommonPrefixA(LPCSTR lpszFile1, LPCSTR lpszFile2, LPSTR achPath)
2594 {
2595   size_t iLen = 0;
2596   LPCSTR lpszIter1 = lpszFile1;
2597   LPCSTR lpszIter2 = lpszFile2;
2598
2599   TRACE("(%s,%s,%p)\n", debugstr_a(lpszFile1), debugstr_a(lpszFile2), achPath);
2600
2601   if (achPath)
2602     *achPath = '\0';
2603
2604   if (!lpszFile1 || !lpszFile2)
2605     return 0;
2606
2607   /* Handle roots first */
2608   if (PathIsUNCA(lpszFile1))
2609   {
2610     if (!PathIsUNCA(lpszFile2))
2611       return 0;
2612     lpszIter1 += 2;
2613     lpszIter2 += 2;
2614   }
2615   else if (PathIsUNCA(lpszFile2))
2616       return 0; /* Know already lpszFile1 is not UNC */
2617
2618   do
2619   {
2620     /* Update len */
2621     if ((!*lpszIter1 || *lpszIter1 == '\\') &&
2622         (!*lpszIter2 || *lpszIter2 == '\\'))
2623       iLen = lpszIter1 - lpszFile1; /* Common to this point */
2624
2625     if (!*lpszIter1 || (tolower(*lpszIter1) != tolower(*lpszIter2)))
2626       break; /* Strings differ at this point */
2627
2628     lpszIter1++;
2629     lpszIter2++;
2630   } while (1);
2631
2632   if (iLen == 2)
2633     iLen++; /* Feature/Bug compatible with Win32 */
2634
2635   if (iLen && achPath)
2636   {
2637     memcpy(achPath,lpszFile1,iLen);
2638     achPath[iLen] = '\0';
2639   }
2640   return iLen;
2641 }
2642
2643 /*************************************************************************
2644  * PathCommonPrefixW   [SHLWAPI.@]
2645  *
2646  * See PathCommonPrefixA.
2647  */
2648 int WINAPI PathCommonPrefixW(LPCWSTR lpszFile1, LPCWSTR lpszFile2, LPWSTR achPath)
2649 {
2650   size_t iLen = 0;
2651   LPCWSTR lpszIter1 = lpszFile1;
2652   LPCWSTR lpszIter2 = lpszFile2;
2653
2654   TRACE("(%s,%s,%p)\n", debugstr_w(lpszFile1), debugstr_w(lpszFile2), achPath);
2655
2656   if (achPath)
2657     *achPath = '\0';
2658
2659   if (!lpszFile1 || !lpszFile2)
2660     return 0;
2661
2662   /* Handle roots first */
2663   if (PathIsUNCW(lpszFile1))
2664   {
2665     if (!PathIsUNCW(lpszFile2))
2666       return 0;
2667     lpszIter1 += 2;
2668     lpszIter2 += 2;
2669   }
2670   else if (PathIsUNCW(lpszFile2))
2671       return 0; /* Know already lpszFile1 is not UNC */
2672
2673   do
2674   {
2675     /* Update len */
2676     if ((!*lpszIter1 || *lpszIter1 == '\\') &&
2677         (!*lpszIter2 || *lpszIter2 == '\\'))
2678       iLen = lpszIter1 - lpszFile1; /* Common to this point */
2679
2680     if (!*lpszIter1 || (tolowerW(*lpszIter1) != tolowerW(*lpszIter2)))
2681       break; /* Strings differ at this point */
2682
2683     lpszIter1++;
2684     lpszIter2++;
2685   } while (1);
2686
2687   if (iLen == 2)
2688     iLen++; /* Feature/Bug compatible with Win32 */
2689
2690   if (iLen && achPath)
2691   {
2692     memcpy(achPath,lpszFile1,iLen * sizeof(WCHAR));
2693     achPath[iLen] = '\0';
2694   }
2695   return iLen;
2696 }
2697
2698 /*************************************************************************
2699  * PathCompactPathA   [SHLWAPI.@]
2700  *
2701  * Make a path fit into a given width when printed to a DC.
2702  *
2703  * PARAMS
2704  *  hDc      [I]   Destination DC
2705  *  lpszPath [I/O] Path to be printed to hDc
2706  *  dx       [I]   Desired width
2707  *
2708  * RETURNS
2709  *  TRUE  If the path was modified.
2710  *  FALSE Otherwise.
2711  */
2712 BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx)
2713 {
2714   BOOL bRet = FALSE;
2715
2716   TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx);
2717
2718   if (lpszPath)
2719   {
2720     WCHAR szPath[MAX_PATH];
2721     MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH);
2722     bRet = PathCompactPathW(hDC, szPath, dx);
2723     WideCharToMultiByte(0,0,szPath,-1,lpszPath,MAX_PATH,0,0);
2724   }
2725   return bRet;
2726 }
2727
2728 /*************************************************************************
2729  * PathCompactPathW   [SHLWAPI.@]
2730  *
2731  * See PathCompactPathA.
2732  */
2733 BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx)
2734 {
2735   static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
2736   BOOL bRet = TRUE;
2737   HDC hdc = 0;
2738   WCHAR buff[MAX_PATH];
2739   SIZE size;
2740   DWORD dwLen;
2741
2742   TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx);
2743
2744   if (!lpszPath)
2745     return bRet;
2746
2747   if (!hDC)
2748     hdc = hDC = GetDC(0);
2749
2750   /* Get the length of the whole path */
2751   dwLen = strlenW(lpszPath);
2752   GetTextExtentPointW(hDC, lpszPath, dwLen, &size);
2753
2754   if ((UINT)size.cx > dx)
2755   {
2756     /* Path too big, must reduce it */
2757     LPWSTR sFile;
2758     DWORD dwEllipsesLen = 0, dwPathLen = 0;
2759
2760     sFile = PathFindFileNameW(lpszPath);
2761     if (sFile != lpszPath)
2762       sFile = CharPrevW(lpszPath, sFile);
2763
2764     /* Get the size of ellipses */
2765     GetTextExtentPointW(hDC, szEllipses, 3, &size);
2766     dwEllipsesLen = size.cx;
2767     /* Get the size of the file name */
2768     GetTextExtentPointW(hDC, sFile, strlenW(sFile), &size);
2769     dwPathLen = size.cx;
2770
2771     if (sFile != lpszPath)
2772     {
2773       LPWSTR sPath = sFile;
2774       BOOL bEllipses = FALSE;
2775
2776       /* The path includes a file name. Include as much of the path prior to
2777        * the file name as possible, allowing for the ellipses, e.g:
2778        * c:\some very long path\filename ==> c:\some v...\filename
2779        */
2780       strncpyW(buff, sFile, MAX_PATH);
2781
2782       do
2783       {
2784         DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen;
2785
2786         GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size);
2787         dwTotalLen += size.cx;
2788         if (dwTotalLen <= dx)
2789           break;
2790         sPath = CharPrevW(lpszPath, sPath);
2791         if (!bEllipses)
2792         {
2793           bEllipses = TRUE;
2794           sPath = CharPrevW(lpszPath, sPath);
2795           sPath = CharPrevW(lpszPath, sPath);
2796         }
2797       } while (sPath > lpszPath);
2798
2799       if (sPath > lpszPath)
2800       {
2801         if (bEllipses)
2802         {
2803           strcpyW(sPath, szEllipses);
2804           strcpyW(sPath+3, buff);
2805         }
2806         bRet = TRUE;
2807         goto end;
2808       }
2809       strcpyW(lpszPath, szEllipses);
2810       strcpyW(lpszPath+3, buff);
2811       bRet = FALSE;
2812       goto end;
2813     }
2814
2815     /* Trim the path by adding ellipses to the end, e.g:
2816      * A very long file name.txt ==> A very...
2817      */
2818     dwLen = strlenW(lpszPath);
2819
2820     if (dwLen > MAX_PATH - 3)
2821       dwLen =  MAX_PATH - 3;
2822     strncpyW(buff, sFile, dwLen);
2823
2824     do {
2825       dwLen--;
2826       GetTextExtentPointW(hDC, buff, dwLen, &size);
2827     } while (dwLen && size.cx + dwEllipsesLen > dx);
2828
2829    if (!dwLen)
2830    {
2831      DWORD dwWritten = 0;
2832
2833      dwEllipsesLen /= 3; /* Size of a single '.' */
2834
2835      /* Write as much of the Ellipses string as possible */
2836      while (dwWritten + dwEllipsesLen < dx && dwLen < 3)
2837      {
2838        *lpszPath++ = '.';
2839        dwWritten += dwEllipsesLen;
2840        dwLen++;
2841      }
2842      *lpszPath = '\0';
2843      bRet = FALSE;
2844    }
2845    else
2846    {
2847      strcpyW(buff + dwLen, szEllipses);
2848      strcpyW(lpszPath, buff);
2849     }
2850   }
2851
2852 end:
2853   if (hdc)
2854     ReleaseDC(0, hdc);
2855
2856   return bRet;
2857 }
2858
2859 /*************************************************************************
2860  * PathGetCharTypeA   [SHLWAPI.@]
2861  *
2862  * Categorise a character from a file path.
2863  *
2864  * PARAMS
2865  *  ch [I] Character to get the type of
2866  *
2867  * RETURNS
2868  *  A set of GCT_ bit flags (from "shlwapi.h") indicating the character type.
2869  */
2870 UINT WINAPI PathGetCharTypeA(UCHAR ch)
2871 {
2872   return PathGetCharTypeW(ch);
2873 }
2874
2875 /*************************************************************************
2876  * PathGetCharTypeW   [SHLWAPI.@]
2877  *
2878  * See PathGetCharTypeA.
2879  */
2880 UINT WINAPI PathGetCharTypeW(WCHAR ch)
2881 {
2882   UINT flags = 0;
2883
2884   TRACE("(%d)\n", ch);
2885
2886   if (!ch || ch < ' ' || ch == '<' || ch == '>' ||
2887       ch == '"' || ch == '|' || ch == '/')
2888     flags = GCT_INVALID; /* Invalid */
2889   else if (ch == '*' || ch=='?')
2890     flags = GCT_WILD; /* Wildchars */
2891   else if ((ch == '\\') || (ch == ':'))
2892     return GCT_SEPARATOR; /* Path separators */
2893   else
2894   {
2895      if (ch < 126)
2896      {
2897        if ((ch & 0x1 && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' ||
2898             ch == '.' || ch == '@' || ch == '^' ||
2899             ch == '\'' || ch == 130 || ch == '`')
2900          flags |= GCT_SHORTCHAR; /* All these are valid for DOS */
2901      }
2902      else
2903        flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */
2904      flags |= GCT_LFNCHAR; /* Valid for long file names */
2905   }
2906   return flags;
2907 }
2908
2909 /*************************************************************************
2910  * SHLWAPI_UseSystemForSystemFolders
2911  *
2912  * Internal helper for PathMakeSystemFolderW.
2913  */
2914 static BOOL WINAPI SHLWAPI_UseSystemForSystemFolders()
2915 {
2916   static BOOL bCheckedReg = FALSE;
2917   static BOOL bUseSystemForSystemFolders = FALSE;
2918
2919   if (!bCheckedReg)
2920   {
2921     bCheckedReg = TRUE;
2922
2923     /* Key tells Win what file attributes to use on system folders */
2924     if (SHGetValueA(HKEY_LOCAL_MACHINE,
2925         "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
2926         "UseSystemForSystemFolders", 0, 0, 0))
2927       bUseSystemForSystemFolders = TRUE;
2928   }
2929   return bUseSystemForSystemFolders;
2930 }
2931
2932 /*************************************************************************
2933  * PathMakeSystemFolderA   [SHLWAPI.@]
2934  *
2935  * Set system folder attribute for a path.
2936  *
2937  * PARAMS
2938  *  lpszPath [I] The path to turn into a system folder
2939  *
2940  * RETURNS
2941  *  TRUE  If the path was changed to/already was a system folder
2942  *  FALSE If the path is invalid or SetFileAttributesA() fails
2943  */
2944 BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath)
2945 {
2946   BOOL bRet = FALSE;
2947
2948   TRACE("(%s)\n", debugstr_a(lpszPath));
2949
2950   if (lpszPath && *lpszPath)
2951   {
2952     WCHAR szPath[MAX_PATH];
2953     MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH);
2954     bRet = PathMakeSystemFolderW(szPath);
2955   }
2956   return bRet;
2957 }
2958
2959 /*************************************************************************
2960  * PathMakeSystemFolderW   [SHLWAPI.@]
2961  *
2962  * See PathMakeSystemFolderA.
2963  */
2964 BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath)
2965 {
2966   DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr;
2967   WCHAR buff[MAX_PATH];
2968
2969   TRACE("(%s)\n", debugstr_w(lpszPath));
2970
2971   if (!lpszPath || !*lpszPath)
2972     return FALSE;
2973
2974   /* If the directory is already a system directory, dont do anything */
2975   GetSystemDirectoryW(buff, MAX_PATH);
2976   if (!strcmpW(buff, lpszPath))
2977     return TRUE;
2978
2979   GetWindowsDirectoryW(buff, MAX_PATH);
2980   if (!strcmpW(buff, lpszPath))
2981     return TRUE;
2982
2983   /* "UseSystemForSystemFolders" Tells Win what attributes to use */
2984   if (SHLWAPI_UseSystemForSystemFolders())
2985     dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM;
2986
2987   if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
2988     return FALSE;
2989
2990   /* Change file attributes to system attributes */
2991   dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
2992   return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr);
2993 }
2994
2995 /*************************************************************************
2996  * PathRenameExtensionA   [SHLWAPI.@]
2997  *
2998  * Swap the file extension in a path with another extension.
2999  *
3000  * PARAMS
3001  *  lpszPath [I/O] Path to swap the extension in
3002  *  lpszExt  [I]   The new extension
3003  *
3004  * RETURNS
3005  *  TRUE  if lpszPath was modified,
3006  *  FALSE if lpszPath or lpszExt is NULL, or the new path is too long
3007  */
3008 BOOL WINAPI PathRenameExtensionA(LPSTR lpszPath, LPCSTR lpszExt)
3009 {
3010   LPSTR lpszExtension;
3011
3012   TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExt));
3013
3014   lpszExtension = PathFindExtensionA(lpszPath);
3015
3016   if (!lpszExtension || (lpszExtension - lpszPath + strlen(lpszExt) >= MAX_PATH))
3017     return FALSE;
3018
3019   strcpy(lpszExtension, lpszExt);
3020   return TRUE;
3021 }
3022
3023 /*************************************************************************
3024  * PathRenameExtensionW   [SHLWAPI.@]
3025  *
3026  * See PathRenameExtensionA.
3027  */
3028 BOOL WINAPI PathRenameExtensionW(LPWSTR lpszPath, LPCWSTR lpszExt)
3029 {
3030   LPWSTR lpszExtension;
3031
3032   TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExt));
3033
3034   lpszExtension = PathFindExtensionW(lpszPath);
3035
3036   if (!lpszExtension || (lpszExtension - lpszPath + strlenW(lpszExt) >= MAX_PATH))
3037     return FALSE;
3038
3039   strcpyW(lpszExtension, lpszExt);
3040   return TRUE;
3041 }
3042
3043 /*************************************************************************
3044  * PathSearchAndQualifyA   [SHLWAPI.@]
3045  *
3046  * Determine if a given path is correct and fully qualified.
3047  *
3048  * PARAMS
3049  *  lpszPath [I] Path to check
3050  *  lpszBuf  [O] Output for correct path
3051  *  cchBuf   [I] Size of lpszBuf
3052  *
3053  * RETURNS
3054  *  Unknown.
3055  */
3056 BOOL WINAPI PathSearchAndQualifyA(LPCSTR lpszPath, LPSTR lpszBuf, UINT cchBuf)
3057 {
3058   FIXME("(%s,%p,0x%08x)-stub\n", debugstr_a(lpszPath), lpszBuf, cchBuf);
3059   return FALSE;
3060 }
3061
3062 /*************************************************************************
3063  * PathSearchAndQualifyW   [SHLWAPI.@]
3064  *
3065  * See PathSearchAndQualifyA.
3066  */
3067 BOOL WINAPI PathSearchAndQualifyW(LPCWSTR lpszPath, LPWSTR lpszBuf, UINT cchBuf)
3068 {
3069   FIXME("(%s,%p,0x%08x)-stub\n", debugstr_w(lpszPath), lpszBuf, cchBuf);
3070   return FALSE;
3071 }
3072
3073 /*************************************************************************
3074  * PathSkipRootA   [SHLWAPI.@]
3075  *
3076  * Return the portion of a path following the drive letter or mount point.
3077  *
3078  * PARAMS
3079  *  lpszPath [I] The path to skip on
3080  *
3081  * RETURNS
3082  *  Success: A pointer to the next character after the root.
3083  *  Failure: NULL, if lpszPath is invalid, has no root or is a multibyte string.
3084  */
3085 LPSTR WINAPI PathSkipRootA(LPCSTR lpszPath)
3086 {
3087   TRACE("(%s)\n", debugstr_a(lpszPath));
3088
3089   if (!lpszPath || !*lpszPath)
3090     return NULL;
3091
3092   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3093   {
3094     /* Network share: skip share server and mount point */
3095     lpszPath += 2;
3096     if ((lpszPath = StrChrA(lpszPath, '\\')) &&
3097         (lpszPath = StrChrA(lpszPath + 1, '\\')))
3098       lpszPath++;
3099     return (LPSTR)lpszPath;
3100   }
3101
3102   if (IsDBCSLeadByte(*lpszPath))
3103     return NULL;
3104
3105   /* Check x:\ */
3106   if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\')
3107     return (LPSTR)lpszPath + 3;
3108   return NULL;
3109 }
3110
3111 /*************************************************************************
3112  * PathSkipRootW   [SHLWAPI.@]
3113  *
3114  * See PathSkipRootA.
3115  */
3116 LPWSTR WINAPI PathSkipRootW(LPCWSTR lpszPath)
3117 {
3118   TRACE("(%s)\n", debugstr_w(lpszPath));
3119
3120   if (!lpszPath || !*lpszPath)
3121     return NULL;
3122
3123   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3124   {
3125     /* Network share: skip share server and mount point */
3126     lpszPath += 2;
3127     if ((lpszPath = StrChrW(lpszPath, '\\')) &&
3128         (lpszPath = StrChrW(lpszPath + 1, '\\')))
3129      lpszPath++;
3130     return (LPWSTR)lpszPath;
3131   }
3132
3133   /* Check x:\ */
3134   if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\')
3135     return (LPWSTR)lpszPath + 3;
3136   return NULL;
3137 }
3138
3139 /*************************************************************************
3140  * PathCreateFromUrlA   [SHLWAPI.@]
3141  *
3142  * Create a path from a URL
3143  *
3144  * PARAMS
3145  *  lpszUrl  [I] URL to convert into a path
3146  *  lpszPath [O] Output buffer for the resulting Path
3147  *  pcchPath [I] Length of lpszPath
3148  *  dwFlags  [I] Flags controlling the conversion
3149  *
3150  * RETURNS
3151  *  Success: S_OK. lpszPath contains the URL in path format,
3152  *  Failure: An HRESULT error code such as E_INVALIDARG.
3153  */
3154 HRESULT WINAPI PathCreateFromUrlA(LPCSTR lpszUrl, LPSTR lpszPath,
3155                                   LPDWORD pcchPath, DWORD dwFlags)
3156 {
3157   LPSTR pszPathPart;
3158   TRACE("(%s,%p,%p,0x%08lx)\n", debugstr_a(lpszUrl), lpszPath, pcchPath, dwFlags);
3159
3160   if (!lpszUrl || !lpszPath || !pcchPath || !*pcchPath)
3161     return E_INVALIDARG;
3162
3163   pszPathPart = StrChrA(lpszUrl, ':');
3164   if ((((pszPathPart - lpszUrl) == 1) && isalpha(*lpszUrl)) ||
3165          !lstrcmpA(lpszUrl, "file:"))
3166   {
3167     return UrlUnescapeA(pszPathPart, lpszPath, pcchPath, dwFlags);
3168   }
3169     /* extracts thing prior to : in pszURL and checks against:
3170      *   https
3171      *   shell
3172      *   local
3173      *   about  - if match returns E_INVALIDARG
3174      */
3175
3176   return E_INVALIDARG;
3177 }
3178
3179 /*************************************************************************
3180  * PathCreateFromUrlW   [SHLWAPI.@]
3181  *
3182  * See PathCreateFromUrlA.
3183  */
3184 HRESULT WINAPI PathCreateFromUrlW(LPCWSTR lpszUrl, LPWSTR lpszPath,
3185                                   LPDWORD pcchPath, DWORD dwFlags)
3186 {
3187   static const WCHAR stemp[] = { 'f','i','l','e',':','/','/',0 };
3188   LPWSTR pwszPathPart;
3189   HRESULT hr;
3190
3191   TRACE("(%s,%p,%p,0x%08lx)\n", debugstr_w(lpszUrl), lpszPath, pcchPath, dwFlags);
3192
3193   if (!lpszUrl || !lpszPath || !pcchPath || !*pcchPath)
3194     return E_INVALIDARG;
3195
3196   /* Path of the form file://... */
3197   if (!strncmpW(lpszUrl, stemp, 7))
3198   {
3199     lpszUrl += 7;
3200   }
3201   /* Path of the form file:... */
3202   else if (!strncmpW(lpszUrl, stemp, 5))
3203   {
3204     lpszUrl += 5;
3205   }
3206
3207   /* Ensure that path is of the form c:... or c|... */
3208   if (lpszUrl[1] != ':' && lpszUrl[1] != '|' && isalphaW(*lpszUrl))
3209     return E_INVALIDARG;
3210
3211   hr = UrlUnescapeW(lpszUrl, lpszPath, pcchPath, dwFlags);
3212   if (lpszPath[1] == '|')
3213     lpszPath[1] = ':';
3214
3215   for (pwszPathPart = lpszPath; *pwszPathPart; pwszPathPart++)
3216     if (*pwszPathPart == '/')
3217       *pwszPathPart = '\\';
3218
3219   TRACE("Returning %s\n",debugstr_w(lpszPath));
3220
3221   return hr;
3222 }
3223
3224 /*************************************************************************
3225  * PathRelativePathToA   [SHLWAPI.@]
3226  *
3227  * Create a relative path from one path to another.
3228  *
3229  * PARAMS
3230  *  lpszPath   [O] Destination for relative path
3231  *  lpszFrom   [I] Source path
3232  *  dwAttrFrom [I] File attribute of source path
3233  *  lpszTo     [I] Destination path
3234  *  dwAttrTo   [I] File attributes of destination path
3235  *
3236  * RETURNS
3237  *  TRUE  If a relative path can be formed. lpszPath contains the new path
3238  *  FALSE If the paths are not relavtive or any parameters are invalid
3239  *
3240  * NOTES
3241  *  lpszTo should be at least MAX_PATH in length.
3242  *
3243  *  Calling this function with relative paths for lpszFrom or lpszTo may
3244  *  give erroneous results.
3245  *
3246  *  The Win32 version of this function contains a bug where the lpszTo string
3247  *  may be referenced 1 byte beyond the end of the string. As a result random
3248  *  garbage may be written to the output path, depending on what lies beyond
3249  *  the last byte of the string. This bug occurs because of the behaviour of
3250  *  PathCommonPrefix() (see notes for that function), and no workaround seems
3251  *  possible with Win32.
3252  *
3253  *  This bug has been fixed here, so for example the relative path from "\\"
3254  *  to "\\" is correctly determined as "." in this implementation.
3255  */
3256 BOOL WINAPI PathRelativePathToA(LPSTR lpszPath, LPCSTR lpszFrom, DWORD dwAttrFrom,
3257                                 LPCSTR lpszTo, DWORD dwAttrTo)
3258 {
3259   BOOL bRet = FALSE;
3260
3261   TRACE("(%p,%s,0x%08lx,%s,0x%08lx)\n", lpszPath, debugstr_a(lpszFrom),
3262         dwAttrFrom, debugstr_a(lpszTo), dwAttrTo);
3263
3264   if(lpszPath && lpszFrom && lpszTo)
3265   {
3266     WCHAR szPath[MAX_PATH];
3267     WCHAR szFrom[MAX_PATH];
3268     WCHAR szTo[MAX_PATH];
3269     MultiByteToWideChar(0,0,lpszFrom,-1,szFrom,MAX_PATH);
3270     MultiByteToWideChar(0,0,lpszTo,-1,szTo,MAX_PATH);
3271     bRet = PathRelativePathToW(szPath,szFrom,dwAttrFrom,szTo,dwAttrTo);
3272     WideCharToMultiByte(0,0,szPath,-1,lpszPath,MAX_PATH,0,0);
3273   }
3274   return bRet;
3275 }
3276
3277 /*************************************************************************
3278  * PathRelativePathToW   [SHLWAPI.@]
3279  *
3280  * See PathRelativePathToA.
3281  */
3282 BOOL WINAPI PathRelativePathToW(LPWSTR lpszPath, LPCWSTR lpszFrom, DWORD dwAttrFrom,
3283                                 LPCWSTR lpszTo, DWORD dwAttrTo)
3284 {
3285   static const WCHAR szPrevDirSlash[] = { '.', '.', '\\', '\0' };
3286   static const WCHAR szPrevDir[] = { '.', '.', '\0' };
3287   WCHAR szFrom[MAX_PATH];
3288   WCHAR szTo[MAX_PATH];
3289   DWORD dwLen;
3290
3291   TRACE("(%p,%s,0x%08lx,%s,0x%08lx)\n", lpszPath, debugstr_w(lpszFrom),
3292         dwAttrFrom, debugstr_w(lpszTo), dwAttrTo);
3293
3294   if(!lpszPath || !lpszFrom || !lpszTo)
3295     return FALSE;
3296
3297   *lpszPath = '\0';
3298   strncpyW(szFrom, lpszFrom, MAX_PATH);
3299   strncpyW(szTo, lpszTo, MAX_PATH);
3300
3301   if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY))
3302     PathRemoveFileSpecW(szFrom);
3303   if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY))
3304     PathRemoveFileSpecW(szTo);
3305
3306   /* Paths can only be relative if they have a common root */
3307   if(!(dwLen = PathCommonPrefixW(szFrom, szTo, 0)))
3308     return FALSE;
3309
3310   /* Strip off lpszFrom components to the root, by adding "..\" */
3311   lpszFrom = szFrom + dwLen;
3312   if (!*lpszFrom)
3313   {
3314     lpszPath[0] = '.';
3315     lpszPath[1] = '\0';
3316   }
3317   if (*lpszFrom == '\\')
3318     lpszFrom++;
3319
3320   while (*lpszFrom)
3321   {
3322     lpszFrom = PathFindNextComponentW(lpszFrom);
3323     strcatW(lpszPath, *lpszFrom ? szPrevDirSlash : szPrevDir);
3324   }
3325
3326   /* From the root add the components of lpszTo */
3327   lpszTo += dwLen;
3328   /* We check lpszTo[-1] to avoid skipping end of string. See the notes for
3329    * this function.
3330    */
3331   if (*lpszTo && lpszTo[-1])
3332   {
3333     if (*lpszTo != '\\')
3334       lpszTo--;
3335     dwLen = strlenW(lpszPath);
3336     if (dwLen + strlenW(lpszTo) >= MAX_PATH)
3337     {
3338       *lpszPath = '\0';
3339       return FALSE;
3340     }
3341     strcpyW(lpszPath + dwLen, lpszTo);
3342   }
3343   return TRUE;
3344 }
3345
3346 /*************************************************************************
3347  * PathUnmakeSystemFolderA   [SHLWAPI.@]
3348  *
3349  * Remove the system folder attributes from a path.
3350  *
3351  * PARAMS
3352  *  lpszPath [I] The path to remove attributes from
3353  *
3354  * RETURNS
3355  *  Success: TRUE.
3356  *  Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling
3357  *           SetFileAttributesA() fails.
3358  */
3359 BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath)
3360 {
3361   DWORD dwAttr;
3362
3363   TRACE("(%s)\n", debugstr_a(lpszPath));
3364
3365   if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
3366       !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
3367     return FALSE;
3368
3369   dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
3370   return SetFileAttributesA(lpszPath, dwAttr);
3371 }
3372
3373 /*************************************************************************
3374  * PathUnmakeSystemFolderW   [SHLWAPI.@]
3375  *
3376  * See PathUnmakeSystemFolderA.
3377  */
3378 BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath)
3379 {
3380   DWORD dwAttr;
3381
3382   TRACE("(%s)\n", debugstr_w(lpszPath));
3383
3384   if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
3385     !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
3386     return FALSE;
3387
3388   dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
3389   return SetFileAttributesW(lpszPath, dwAttr);
3390 }
3391
3392
3393 /*************************************************************************
3394  * PathSetDlgItemPathA   [SHLWAPI.@]
3395  *
3396  * Set the text of a dialog item to a path, shrinking the path to fit
3397  * if it is too big for the item.
3398  *
3399  * PARAMS
3400  *  hDlg     [I] Dialog handle
3401  *  id       [I] ID of item in the dialog
3402  *  lpszPath [I] Path to set as the items text
3403  *
3404  * RETURNS
3405  *  Nothing.
3406  *
3407  * NOTES
3408  *  If lpszPath is NULL, a blank string ("") is set (i.e. The previous
3409  *  window text is erased).
3410  */
3411 VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath)
3412 {
3413   WCHAR szPath[MAX_PATH];
3414
3415   TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath));
3416
3417   if (lpszPath)
3418     MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH);
3419   else
3420     szPath[0] = '\0';
3421   PathSetDlgItemPathW(hDlg, id, szPath);
3422 }
3423
3424 /*************************************************************************
3425  * PathSetDlgItemPathW   [SHLWAPI.@]
3426  *
3427  * See PathSetDlgItemPathA.
3428  */
3429 VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath)
3430 {
3431   WCHAR path[MAX_PATH + 1];
3432   HWND hwItem;
3433   RECT rect;
3434   HDC hdc;
3435   HGDIOBJ hPrevObj;
3436
3437   TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath));
3438
3439   if (!(hwItem = GetDlgItem(hDlg, id)))
3440     return;
3441
3442   if (lpszPath)
3443     strncpyW(path, lpszPath, sizeof(path));
3444   else
3445     path[0] = '\0';
3446
3447   GetClientRect(hwItem, &rect);
3448   hdc = GetDC(hDlg);
3449   hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0));
3450
3451   if (hPrevObj)
3452   {
3453     PathCompactPathW(hdc, path, rect.right);
3454     SelectObject(hdc, hPrevObj);
3455   }
3456
3457   ReleaseDC(hDlg, hdc);
3458   SetWindowTextW(hwItem, path);
3459 }
3460
3461 /*************************************************************************
3462  * PathIsNetworkPathA [SHLWAPI.@]
3463  *
3464  * Determine if the given path is a network path.
3465  *
3466  * PARAMS
3467  *  lpszPath [I] Path to check
3468  *
3469  * RETURNS
3470  *  TRUE  If lpszPath is a UNC share or mapped network drive, or
3471  *  FALSE If lpszPath is a local drive or cannot be determined
3472  */
3473 BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath)
3474 {
3475   int dwDriveNum;
3476
3477   TRACE("(%s)\n",debugstr_a(lpszPath));
3478
3479   if (!lpszPath)
3480     return FALSE;
3481   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3482     return TRUE;
3483   dwDriveNum = PathGetDriveNumberA(lpszPath);
3484   if (dwDriveNum == -1)
3485     return FALSE;
3486   GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
3487   return pIsNetDrive(dwDriveNum);
3488 }
3489
3490 /*************************************************************************
3491  * PathIsNetworkPathW [SHLWAPI.@]
3492  *
3493  * See PathIsNetworkPathA.
3494  */
3495 BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath)
3496 {
3497   int dwDriveNum;
3498
3499   TRACE("(%s)\n", debugstr_w(lpszPath));
3500
3501   if (!lpszPath)
3502     return FALSE;
3503   if (*lpszPath == '\\' && lpszPath[1] == '\\')
3504     return TRUE;
3505   dwDriveNum = PathGetDriveNumberW(lpszPath);
3506   if (dwDriveNum == -1)
3507     return FALSE;
3508   GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
3509   return pIsNetDrive(dwDriveNum);
3510 }
3511
3512 /*************************************************************************
3513  * PathIsLFNFileSpecA [SHLWAPI.@]
3514  *
3515  * Determine if the given path is a long file name
3516  *
3517  * PARAMS
3518  *  lpszPath [I] Path to check
3519  *
3520  * RETURNS
3521  *  TRUE  If path is a long file name,
3522  *  FALSE If path is a valid DOS short file name
3523  */
3524 BOOL WINAPI PathIsLFNFileSpecA(LPCSTR lpszPath)
3525 {
3526   DWORD dwNameLen = 0, dwExtLen = 0;
3527
3528   TRACE("(%s)\n",debugstr_a(lpszPath));
3529
3530   if (!lpszPath)
3531     return FALSE;
3532
3533   while (*lpszPath)
3534   {
3535     if (*lpszPath == ' ')
3536       return TRUE; /* DOS names cannot have spaces */
3537     if (*lpszPath == '.')
3538     {
3539       if (dwExtLen)
3540         return TRUE; /* DOS names have only one dot */
3541       dwExtLen = 1;
3542     }
3543     else if (dwExtLen)
3544     {
3545       dwExtLen++;
3546       if (dwExtLen > 4)
3547         return TRUE; /* DOS extensions are <= 3 chars*/
3548     }
3549     else
3550     {
3551       dwNameLen++;
3552       if (dwNameLen > 8)
3553         return TRUE; /* DOS names are <= 8 chars */
3554     }
3555     lpszPath += IsDBCSLeadByte(*lpszPath) ? 2 : 1;
3556   }
3557   return FALSE; /* Valid DOS path */
3558 }
3559
3560 /*************************************************************************
3561  * PathIsLFNFileSpecW [SHLWAPI.@]
3562  *
3563  * See PathIsLFNFileSpecA.
3564  */
3565 BOOL WINAPI PathIsLFNFileSpecW(LPCWSTR lpszPath)
3566 {
3567   DWORD dwNameLen = 0, dwExtLen = 0;
3568
3569   TRACE("(%s)\n",debugstr_w(lpszPath));
3570
3571   if (!lpszPath)
3572     return FALSE;
3573
3574   while (*lpszPath)
3575   {
3576     if (*lpszPath == ' ')
3577       return TRUE; /* DOS names cannot have spaces */
3578     if (*lpszPath == '.')
3579     {
3580       if (dwExtLen)
3581         return TRUE; /* DOS names have only one dot */
3582       dwExtLen = 1;
3583     }
3584     else if (dwExtLen)
3585     {
3586       dwExtLen++;
3587       if (dwExtLen > 4)
3588         return TRUE; /* DOS extensions are <= 3 chars*/
3589     }
3590     else
3591     {
3592       dwNameLen++;
3593       if (dwNameLen > 8)
3594         return TRUE; /* DOS names are <= 8 chars */
3595     }
3596     lpszPath++;
3597   }
3598   return FALSE; /* Valid DOS path */
3599 }
3600
3601 /*************************************************************************
3602  * PathIsDirectoryEmptyA [SHLWAPI.@]
3603  *
3604  * Determine if a given directory is empty.
3605  *
3606  * PARAMS
3607  *  lpszPath [I] Directory to check
3608  *
3609  * RETURNS
3610  *  TRUE  If the directory exists and contains no files,
3611  *  FALSE Otherwise
3612  */
3613 BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath)
3614 {
3615   BOOL bRet = FALSE;
3616
3617   TRACE("(%s)\n",debugstr_a(lpszPath));
3618
3619   if (lpszPath)
3620   {
3621     WCHAR szPath[MAX_PATH];
3622     MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH);
3623     bRet = PathIsDirectoryEmptyW(szPath);
3624   }
3625   return bRet;
3626 }
3627
3628 /*************************************************************************
3629  * PathIsDirectoryEmptyW [SHLWAPI.@]
3630  *
3631  * See PathIsDirectoryEmptyA.
3632  */
3633 BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath)
3634 {
3635   static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' };
3636   WCHAR szSearch[MAX_PATH];
3637   DWORD dwLen;
3638   HANDLE hfind;
3639   BOOL retVal = FALSE;
3640   WIN32_FIND_DATAW find_data;
3641
3642   TRACE("(%s)\n",debugstr_w(lpszPath));
3643
3644   if (!lpszPath || !PathIsDirectoryW(lpszPath))
3645       return FALSE;
3646
3647   strncpyW(szSearch, lpszPath, MAX_PATH);
3648   PathAddBackslashW(szSearch);
3649   dwLen = strlenW(szSearch);
3650   if (dwLen > MAX_PATH - 4)
3651     return FALSE;
3652
3653   strcpyW(szSearch + dwLen, szAllFiles);
3654   hfind = FindFirstFileW(szSearch, &find_data);
3655
3656   if (hfind != INVALID_HANDLE_VALUE &&
3657       find_data.cFileName[0] == '.' &&
3658       find_data.cFileName[1] == '.')
3659   {
3660     /* The only directory entry should be the parent */
3661     if (!FindNextFileW(hfind, &find_data))
3662       retVal = TRUE;
3663     FindClose(hfind);
3664   }
3665   return retVal;
3666 }
3667
3668
3669 /*************************************************************************
3670  * PathFindSuffixArrayA [SHLWAPI.@]
3671  *
3672  * Find a suffix string in an array of suffix strings
3673  *
3674  * PARAMS
3675  *  lpszSuffix [I] Suffix string to search for
3676  *  lppszArray [I] Array of suffix strings to search
3677  *  dwCount    [I] Number of elements in lppszArray
3678  *
3679  * RETURNS
3680  *  Success: The index of the position of lpszSuffix in lppszArray
3681  *  Failure: 0, if any parameters are invalid or lpszSuffix is not found
3682  *
3683  * NOTES
3684  *  The search is case sensitive.
3685  *  The match is made against the end of the suffix string, so for example:
3686  *  lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not.
3687  */
3688 int WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount)
3689 {
3690   size_t dwLen;
3691   int dwRet = 0;
3692
3693   TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount);
3694
3695   if (lpszSuffix && lppszArray && dwCount > 0)
3696   {
3697     dwLen = strlen(lpszSuffix);
3698
3699     while (dwRet < dwCount)
3700     {
3701       size_t dwCompareLen = strlen(*lppszArray);
3702       if (dwCompareLen < dwLen)
3703       {
3704         if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
3705           return dwRet; /* Found */
3706       }
3707       dwRet++;
3708       lppszArray++;
3709     }
3710   }
3711   return 0;
3712 }
3713
3714 /*************************************************************************
3715  * PathFindSuffixArrayW [SHLWAPI.@]
3716  *
3717  * See PathFindSuffixArrayA.
3718  */
3719 int WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount)
3720 {
3721   size_t dwLen;
3722   int dwRet = 0;
3723
3724   TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount);
3725
3726   if (lpszSuffix && lppszArray && dwCount > 0)
3727   {
3728     dwLen = strlenW(lpszSuffix);
3729
3730     while (dwRet < dwCount)
3731     {
3732       size_t dwCompareLen = strlenW(*lppszArray);
3733       if (dwCompareLen < dwLen)
3734       {
3735         if (!strcmpW(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
3736           return dwRet; /* Found */
3737       }
3738       dwRet++;
3739       lppszArray++;
3740     }
3741   }
3742   return 0;
3743 }
3744
3745 /*************************************************************************
3746  * PathUndecorateA [SHLWAPI.@]
3747  *
3748  * Undecorate a file path
3749  *
3750  * PARAMS
3751  *  lpszPath [I/O] Path to remove any decoration from
3752  *
3753  * RETURNS
3754  *  Nothing
3755  *
3756  * NOTES
3757  *  A decorations form is "path[n].ext" where "n" is an optional decimal number.
3758  */
3759 VOID WINAPI PathUndecorateA(LPSTR lpszPath)
3760 {
3761   TRACE("(%s)\n",debugstr_a(lpszPath));
3762
3763   if (lpszPath)
3764   {
3765     LPSTR lpszExt = PathFindExtensionA(lpszPath);
3766     if (lpszExt > lpszPath && lpszExt[-1] == ']')
3767     {
3768       LPSTR lpszSkip = lpszExt - 2;
3769       if (*lpszSkip == '[')
3770         lpszSkip++;  /* [] (no number) */
3771       else
3772         while (lpszSkip > lpszPath && isdigit(lpszSkip[-1]))
3773           lpszSkip--;
3774       if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\')
3775       {
3776         /* remove the [n] */
3777         lpszSkip--;
3778         while (*lpszExt)
3779           *lpszSkip++ = *lpszExt++;
3780         *lpszSkip = '\0';
3781       }
3782     }
3783   }
3784 }
3785
3786 /*************************************************************************
3787  * PathUndecorateW [SHLWAPI.@]
3788  *
3789  * See PathUndecorateA.
3790  */
3791 VOID WINAPI PathUndecorateW(LPWSTR lpszPath)
3792 {
3793   TRACE("(%s)\n",debugstr_w(lpszPath));
3794
3795   if (lpszPath)
3796   {
3797     LPWSTR lpszExt = PathFindExtensionW(lpszPath);
3798     if (lpszExt > lpszPath && lpszExt[-1] == ']')
3799     {
3800       LPWSTR lpszSkip = lpszExt - 2;
3801       if (*lpszSkip == '[')
3802         lpszSkip++; /* [] (no number) */
3803       else
3804         while (lpszSkip > lpszPath && isdigitW(lpszSkip[-1]))
3805           lpszSkip--;
3806       if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\')
3807       {
3808         /* remove the [n] */
3809         lpszSkip--;
3810         while (*lpszExt)
3811           *lpszSkip++ = *lpszExt++;
3812         *lpszSkip = '\0';
3813       }
3814     }
3815   }
3816 }
3817
3818 /*************************************************************************
3819  * @     [SHLWAPI.440]
3820  *
3821  * Find localised or default web content in "%WINDOWS%\web\".
3822  *
3823  * PARAMS
3824  *  lpszFile  [I] File name containing content to look for
3825  *  lpszPath  [O] Buffer to contain the full path to the file
3826  *  dwPathLen [I] Length of lpszPath
3827  *
3828  * RETURNS
3829  *  Success: S_OK. lpszPath contains the full path to the content.
3830  *  Failure: E_FAIL. The content does not exist or lpszPath is too short.
3831  */
3832 HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen)
3833 {
3834   WCHAR szFile[MAX_PATH], szPath[MAX_PATH];
3835   HRESULT hRet;
3836
3837   TRACE("(%s,%p,%ld)\n", lpszFile, lpszPath, dwPathLen);
3838
3839   MultiByteToWideChar(0, 0, lpszFile, -1, szFile, MAX_PATH);
3840   szPath[0] = '\0';
3841   hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen);
3842   WideCharToMultiByte(0, 0, szPath, -1, lpszPath, dwPathLen, 0, 0);
3843   return hRet;
3844 }
3845
3846 /*************************************************************************
3847  * @     [SHLWAPI.441]
3848  *
3849  * Unicode version of SHGetWebFolderFilePathA.
3850  */
3851 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen)
3852 {
3853   static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'};
3854   static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'};
3855 #define szWebLen (sizeof(szWeb)/sizeof(WCHAR))
3856 #define szWebMuiLen ((sizeof(szWebMui)+1)/sizeof(WCHAR))
3857   DWORD dwLen, dwFileLen;
3858   LANGID lidSystem, lidUser;
3859
3860   TRACE("(%s,%p,%ld)\n", debugstr_w(lpszFile), lpszPath, dwPathLen);
3861
3862   /* Get base directory for web content */
3863   dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen);
3864   if (dwLen > 0 && lpszPath[dwLen-1] == '\\')
3865     dwLen--;
3866
3867   dwFileLen = strlenW(lpszFile);
3868
3869   if (dwLen + dwFileLen + szWebLen >= dwPathLen)
3870     return E_FAIL; /* lpszPath too short */
3871
3872   strcpyW(lpszPath+dwLen, szWeb);
3873   dwLen += szWebLen;
3874   dwPathLen = dwPathLen - dwLen; /* Remaining space */
3875
3876   lidSystem = GetSystemDefaultUILanguage();
3877   lidUser = GetUserDefaultUILanguage();
3878
3879   if (lidSystem != lidUser)
3880   {
3881     if (dwFileLen + szWebMuiLen < dwPathLen)
3882     {
3883       /* Use localised content in the users UI language if present */
3884       wsprintfW(lpszPath + dwLen, szWebMui, lidUser);
3885       strcpyW(lpszPath + dwLen + szWebMuiLen, lpszFile);
3886       if (PathFileExistsW(lpszPath))
3887         return S_OK;
3888     }
3889   }
3890
3891   /* Fall back to OS default installed content */
3892   strcpyW(lpszPath + dwLen, lpszFile);
3893   if (PathFileExistsW(lpszPath))
3894     return S_OK;
3895   return E_FAIL;
3896 }