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