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