~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/shlwapi/path.c

Version: ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 20  */
 21 
 22 #include "config.h"
 23 #include "wine/port.h"
 24 
 25 #include <stdarg.h>
 26 #include <string.h>
 27 #include <stdlib.h>
 28 
 29 #include "wine/unicode.h"
 30 #include "windef.h"
 31 #include "winbase.h"
 32 #include "wingdi.h"
 33 #include "winuser.h"
 34 #include "winreg.h"
 35 #include "winternl.h"
 36 #define NO_SHLWAPI_STREAM
 37 #include "shlwapi.h"
 38 #include "wine/debug.h"
 39 
 40 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 41 
 42 /* Get a function pointer from a DLL handle */
 43 #define GET_FUNC(func, module, name, fail) \
 44   do { \
 45     if (!func) { \
 46       if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
 47       func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
 48       if (!func) return fail; \
 49     } \
 50   } while (0)
 51 
 52 /* DLL handles for late bound calls */
 53 static HMODULE SHLWAPI_hshell32;
 54 
 55 /* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
 56 typedef BOOL (WINAPI *fnpIsNetDrive)(int);
 57 static  fnpIsNetDrive pIsNetDrive;
 58 
 59 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD);
 60 
 61 /*************************************************************************
 62  * PathAppendA    [SHLWAPI.@]
 63  *
 64  * Append one path to another.
 65  *
 66  * PARAMS
 67  *  lpszPath   [I/O] Initial part of path, and destination for output
 68  *  lpszAppend [I]   Path to append
 69  *
 70  * RETURNS
 71  *  Success: TRUE. lpszPath contains the newly created path.
 72  *  Failure: FALSE, if either path is NULL, or PathCombineA() fails.
 73  *
 74  * NOTES
 75  *  lpszAppend must contain at least one backslash ('\') if not NULL.
 76  *  Because PathCombineA() is used to join the paths, the resulting
 77  *  path is also canonicalized.
 78  */
 79 BOOL WINAPI PathAppendA (LPSTR lpszPath, LPCSTR lpszAppend)
 80 {
 81   TRACE("(%s,%s)\n",debugstr_a(lpszPath), debugstr_a(lpszAppend));
 82 
 83   if (lpszPath && lpszAppend)
 84   {
 85     if (!PathIsUNCA(lpszAppend))
 86       while (*lpszAppend == '\\')
 87         lpszAppend++;
 88     if (PathCombineA(lpszPath, lpszPath, lpszAppend))
 89       return TRUE;
 90   }
 91   return FALSE;
 92 }
 93 
 94 /*************************************************************************
 95  * PathAppendW    [SHLWAPI.@]
 96  *
 97  * See PathAppendA.
 98  */
 99 BOOL WINAPI PathAppendW(LPWSTR lpszPath, LPCWSTR lpszAppend)
100 {
101   TRACE("(%s,%s)\n",debugstr_w(lpszPath), debugstr_w(lpszAppend));
102 
103   if (lpszPath && lpszAppend)
104   {
105     if (!PathIsUNCW(lpszAppend))
106       while (*lpszAppend == '\\')
107         lpszAppend++;
108     if (PathCombineW(lpszPath, lpszPath, lpszAppend))
109       return TRUE;
110   }
111   return FALSE;
112 }
113 
114 /*************************************************************************
115  * PathCombineA         [SHLWAPI.@]
116  *
117  * Combine two paths together.
118  *
119  * PARAMS
120  *  lpszDest [O] Destination for combined path
121  *  lpszDir  [I] Directory path
122  *  lpszFile [I] File path
123  *
124  * RETURNS
125  *  Success: The output path
126  *  Failure: NULL, if inputs are invalid.
127  *
128  * NOTES
129  *  lpszDest should be at least MAX_PATH in size, and may point to the same
130  *  memory location as lpszDir. The combined path is canonicalised.
131  */
132 LPSTR WINAPI PathCombineA(LPSTR lpszDest, LPCSTR lpszDir, LPCSTR lpszFile)
133 {
134   WCHAR szDest[MAX_PATH];
135   WCHAR szDir[MAX_PATH];
136   WCHAR szFile[MAX_PATH];
137   TRACE("(%p,%s,%s)\n", lpszDest, debugstr_a(lpszDir), debugstr_a(lpszFile));
138 
139   /* Invalid parameters */
140   if (!lpszDest)
141     return NULL;
142   if (!lpszDir && !lpszFile)
143   {
144     lpszDest[0] = 0;
145     return NULL;
146   }
147 
148   if (lpszDir)
149     MultiByteToWideChar(CP_ACP,0,lpszDir,-1,szDir,MAX_PATH);
150   if (lpszFile)
151     MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH);
152 
153   if (PathCombineW(szDest, lpszDir ? szDir : NULL, lpszFile ? szFile : NULL))
154     if (WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0))
155       return lpszDest;
156 
157   lpszDest[0] = 0;
158   return NULL;
159 }
160 
161 /*************************************************************************
162  * PathCombineW          [SHLWAPI.@]
163  *
164  * See PathCombineA.
165  */
166 LPWSTR WINAPI PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
167 {
168   WCHAR szTemp[MAX_PATH];
169   BOOL bUseBoth = FALSE, bStrip = FALSE;
170 
171   TRACE("(%p,%s,%s)\n", lpszDest, debugstr_w(lpszDir), debugstr_w(lpszFile));
172 
173   /* Invalid parameters */
174   if (!lpszDest)
175     return NULL;
176   if (!lpszDir && !lpszFile)
177   {
178     lpszDest[0] = 0;
179     return NULL;
180   }
181 
182   if ((!lpszFile || !*lpszFile) && lpszDir)
183   {
184     /* Use dir only */
185     lstrcpynW(szTemp, lpszDir, MAX_PATH);
186   }
187   else if (!lpszDir || !*lpszDir || !PathIsRelativeW(lpszFile))
188   {
189     if (!lpszDir || !*lpszDir || *lpszFile != '\\' || PathIsUNCW(lpszFile))
190     {
191       /* Use file only */
192       lstrcpynW(szTemp, lpszFile, MAX_PATH);
193     }
194     else
195     {
196       bUseBoth = TRUE;
197       bStrip = TRUE;
198     }
199   }
200   else
201     bUseBoth = TRUE;
202 
203   if (bUseBoth)
204   {
205     lstrcpynW(szTemp, lpszDir, MAX_PATH);
206     if (bStrip)
207     {
208       PathStripToRootW(szTemp);
209       lpszFile++; /* Skip '\' */
210     }
211     if (!PathAddBackslashW(szTemp) || strlenW(szTemp) + strlenW(lpszFile) >= MAX_PATH)
212     {
213       lpszDest[0] = 0;
214       return NULL;
215     }
216     strcatW(szTemp, lpszFile);
217   }
218 
219   PathCanonicalizeW(lpszDest, szTemp);
220   return lpszDest;
221 }
222 
223 /*************************************************************************
224  * PathAddBackslashA    [SHLWAPI.@]
225  *
226  * Append a backslash ('\') to a path if one doesn't exist.
227  *
228  * PARAMS
229  *  lpszPath [I/O] The path to append a backslash to.
230  *
231  * RETURNS
232  *  Success: The position of the last backslash in the path.
233  *  Failure: NULL, if lpszPath is NULL or the path is too large.
234  */
235 LPSTR WINAPI PathAddBackslashA(LPSTR lpszPath)
236 {
237   size_t iLen;
238   LPSTR prev = lpszPath;
239 
240   TRACE("(%s)\n",debugstr_a(lpszPath));
241 
242   if (!lpszPath || (iLen = strlen(lpszPath)) >= MAX_PATH)
243     return NULL;
244 
245   if (iLen)
246   {
247     do {
248       lpszPath = CharNextA(prev);
249       if (*lpszPath)
250         prev = lpszPath;
251     } while (*lpszPath);
252     if (*prev != '\\')
253     {
254       *lpszPath++ = '\\';
255       *lpszPath = '\0';
256     }
257   }
258   return lpszPath;
259 }
260 
261 /*************************************************************************
262  * PathAddBackslashW  [SHLWAPI.@]
263  *
264  * See PathAddBackslashA.
265  */
266 LPWSTR WINAPI PathAddBackslashW( LPWSTR lpszPath )
267 {
268   size_t iLen;
269 
270   TRACE("(%s)\n",debugstr_w(lpszPath));
271 
272   if (!lpszPath || (iLen = strlenW(lpszPath)) >= MAX_PATH)
273     return NULL;
274 
275   if (iLen)
276   {
277     lpszPath += iLen;
278     if (lpszPath[-1] != '\\')
279     {
280       *lpszPath++ = '\\';
281       *lpszPath = '\0';
282     }
283   }
284   return lpszPath;
285 }
286 
287 /*************************************************************************
288  * PathBuildRootA    [SHLWAPI.@]
289  *
290  * Create a root drive string (e.g. "A:\") from a drive number.
291  *
292  * PARAMS
293  *  lpszPath [O] Destination for the drive string
294  *
295  * RETURNS
296  *  lpszPath
297  *
298  * NOTES
299  *  If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
300  */
301 LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive)
302 {
303   TRACE("(%p,%d)\n", lpszPath, drive);
304 
305   if (lpszPath && drive >= 0 && drive < 26)
306   {
307     lpszPath[0] = 'A' + drive;
308     lpszPath[1] = ':';
309     lpszPath[2] = '\\';
310     lpszPath[3] = '\0';
311   }
312   return lpszPath;
313 }
314 
315 /*************************************************************************
316  * PathBuildRootW    [SHLWAPI.@]
317  *
318  * See PathBuildRootA.
319  */
320 LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive)
321 {
322   TRACE("(%p,%d)\n", lpszPath, drive);
323 
324   if (lpszPath && drive >= 0 && drive < 26)
325   {
326     lpszPath[0] = 'A' + drive;
327     lpszPath[1] = ':';
328     lpszPath[2] = '\\';
329     lpszPath[3] = '\0';
330   }
331   return lpszPath;
332 }
333 
334 /*************************************************************************
335  * PathFindFileNameA  [SHLWAPI.@]
336  *
337  * Locate the start of the file name in a path
338  *
339  * PARAMS
340  *  lpszPath [I] Path to search
341  *
342  * RETURNS
343  *  A pointer to the first character of the file name
344  */
345 LPSTR WINAPI PathFindFileNameA(LPCSTR lpszPath)
346 {
347   LPCSTR lastSlash = lpszPath;
348 
349   TRACE("(%s)\n",debugstr_a(lpszPath));
350 
351   while (lpszPath && *lpszPath)
352   {
353     if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') &&
354         lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/')
355       lastSlash = lpszPath + 1;
356     lpszPath = CharNextA(lpszPath);
357   }
358   return (LPSTR)lastSlash;
359 }
360 
361 /*************************************************************************
362  * PathFindFileNameW  [SHLWAPI.@]
363  *
364  * See PathFindFileNameA.
365  */
366 LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath)
367 {
368   LPCWSTR lastSlash = lpszPath;
369 
370   TRACE("(%s)\n",debugstr_w(lpszPath));
371 
372   while (lpszPath && *lpszPath)
373   {
374     if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') &&
375         lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/')
376       lastSlash = lpszPath + 1;
377     lpszPath++;
378   }
379   return (LPWSTR)lastSlash;
380 }
381 
382 /*************************************************************************
383  * PathFindExtensionA  [SHLWAPI.@]
384  *
385  * Locate the start of the file extension in a path
386  *
387  * PARAMS
388  *  lpszPath [I] The path to search
389  *
390  * RETURNS
391  *  A pointer to the first character of the extension, the end of
392  *  the string if the path has no extension, or NULL If lpszPath is NULL
393  */
394 LPSTR WINAPI PathFindExtensionA( LPCSTR lpszPath )
395 {
396   LPCSTR lastpoint = NULL;
397 
398   TRACE("(%s)\n", debugstr_a(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 = CharNextA(lpszPath);
409     }
410   }
411   return (LPSTR)(lastpoint ? lastpoint : lpszPath);
412 }
413 
414 /*************************************************************************
415  * PathFindExtensionW  [SHLWAPI.@]
416  *
417  * See PathFindExtensionA.
418  */
419 LPWSTR WINAPI PathFindExtensionW( LPCWSTR lpszPath )
420 {
421   LPCWSTR lastpoint = NULL;
422 
423   TRACE("(%s)\n", debugstr_w(lpszPath));
424 
425   if (lpszPath)
426   {
427     while (*lpszPath)
428     {
429       if (*lpszPath == '\\' || *lpszPath==' ')
430         lastpoint = NULL;
431       else if (*lpszPath == '.')
432         lastpoint = lpszPath;
433       lpszPath++;
434     }
435   }
436   return (LPWSTR)(lastpoint ? lastpoint : lpszPath);
437 }
438 
439 /*************************************************************************
440  * PathGetArgsA    [SHLWAPI.@]
441  *
442  * Find the next argument in a string delimited by spaces.
443  *
444  * PARAMS
445  *  lpszPath [I] The string to search for arguments in
446  *
447  * RETURNS
448  *  The start of the next argument in lpszPath, or NULL if lpszPath is NULL
449  *
450  * NOTES
451  *  Spaces in quoted strings are ignored as delimiters.
452  */
453 LPSTR WINAPI PathGetArgsA(LPCSTR lpszPath)
454 {
455   BOOL bSeenQuote = FALSE;
456 
457   TRACE("(%s)\n",debugstr_a(lpszPath));
458 
459   if (lpszPath)
460   {
461     while (*lpszPath)
462     {
463       if ((*lpszPath==' ') && !bSeenQuote)
464         return (LPSTR)lpszPath + 1;
465       if (*lpszPath == '"')
466         bSeenQuote = !bSeenQuote;
467       lpszPath = CharNextA(lpszPath);
468     }
469   }
470   return (LPSTR)lpszPath;
471 }
472 
473 /*************************************************************************
474  * PathGetArgsW    [SHLWAPI.@]
475  *
476  * See PathGetArgsA.
477  */
478 LPWSTR WINAPI PathGetArgsW(LPCWSTR lpszPath)
479 {
480   BOOL bSeenQuote = FALSE;
481 
482   TRACE("(%s)\n",debugstr_w(lpszPath));
483 
484   if (lpszPath)
485   {
486     while (*lpszPath)
487     {
488       if ((*lpszPath==' ') && !bSeenQuote)
489         return (LPWSTR)lpszPath + 1;
490       if (*lpszPath == '"')
491         bSeenQuote = !bSeenQuote;
492       lpszPath++;
493     }
494   }
495   return (LPWSTR)lpszPath;
496 }
497 
498 /*************************************************************************
499  * PathGetDriveNumberA  [SHLWAPI.@]
500  *
501  * Return the drive number from a path
502  *
503  * PARAMS
504  *  lpszPath [I] Path to get the drive number from
505  *
506  * RETURNS
507  *  Success: The drive number corresponding to the drive in the path
508  *  Failure: -1, if lpszPath contains no valid drive
509  */
510 int WINAPI PathGetDriveNumberA(LPCSTR lpszPath)
511 {
512   TRACE ("(%s)\n",debugstr_a(lpszPath));
513 
514   if (lpszPath && !IsDBCSLeadByte(*lpszPath) && lpszPath[1] == ':' &&
515       tolower(*lpszPath) >= 'a' && tolower(*lpszPath) <= 'z')
516     return tolower(*lpszPath) - 'a';
517   return -1;
518 }
519 
520 /*************************************************************************
521  * PathGetDriveNumberW  [SHLWAPI.@]
522  *
523  * See PathGetDriveNumberA.
524  */
525 int WINAPI PathGetDriveNumberW(LPCWSTR lpszPath)
526 {
527   TRACE ("(%s)\n",debugstr_w(lpszPath));
528 
529   if (lpszPath)
530   {
531       WCHAR tl = tolowerW(lpszPath[0]);
532       if (tl >= 'a' && tl <= 'z' && lpszPath[1] == ':')
533           return tl - 'a';
534   }
535   return -1;
536 }
537 
538 /*************************************************************************
539  * PathRemoveFileSpecA  [SHLWAPI.@]
540  *
541  * Remove the file specification from a path.
542  *
543  * PARAMS
544  *  lpszPath [I/O] Path to remove the file spec from
545  *
546  * RETURNS
547  *  TRUE  If the path was valid and modified
548  *  FALSE Otherwise
549  */
550 BOOL WINAPI PathRemoveFileSpecA(LPSTR lpszPath)
551 {
552   LPSTR lpszFileSpec = lpszPath;
553   BOOL bModified = FALSE;
554 
555   TRACE("(%s)\n",debugstr_a(lpszPath));
556 
557   if(lpszPath)
558   {
559     /* Skip directory or UNC path */
560     if (*lpszPath == '\\')
561       lpszFileSpec = ++lpszPath;
562     if (*lpszPath == '\\')
563       lpszFileSpec = ++lpszPath;
564 
565     while (*lpszPath)
566     {
567       if(*lpszPath == '\\')
568         lpszFileSpec = lpszPath; /* Skip dir */
569       else if(*lpszPath == ':')
570       {
571         lpszFileSpec = ++lpszPath; /* Skip drive */
572         if (*lpszPath == '\\')
573           lpszFileSpec++;
574       }
575       if (!(lpszPath = CharNextA(lpszPath)))
576         break;
577     }
578 
579     if (*lpszFileSpec)
580     {
581       *lpszFileSpec = '\0';
582       bModified = TRUE;
583     }
584   }
585   return bModified;
586 }
587 
588 /*************************************************************************
589  * PathRemoveFileSpecW  [SHLWAPI.@]
590  *
591  * See PathRemoveFileSpecA.
592  */
593 BOOL WINAPI PathRemoveFileSpecW(LPWSTR lpszPath)
594 {
595   LPWSTR lpszFileSpec = lpszPath;
596   BOOL bModified = FALSE;
597 
598   TRACE("(%s)\n",debugstr_w(lpszPath));
599 
600   if(lpszPath)
601   {
602     /* Skip directory or UNC path */
603     if (*lpszPath == '\\')
604       lpszFileSpec = ++lpszPath;
605     if (*lpszPath == '\\')
606       lpszFileSpec = ++lpszPath;
607 
608     while (*lpszPath)
609     {
610       if(*lpszPath == '\\')
611         lpszFileSpec = lpszPath; /* Skip dir */
612       else if(*lpszPath == ':')
613       {
614         lpszFileSpec = ++lpszPath; /* Skip drive */
615         if (*lpszPath == '\\')
616           lpszFileSpec++;
617       }
618       lpszPath++;
619     }
620 
621     if (*lpszFileSpec)
622     {
623       *lpszFileSpec = '\0';
624       bModified = TRUE;
625     }
626   }
627   return bModified;
628 }
629 
630 /*************************************************************************
631  * PathStripPathA       [SHLWAPI.@]
632  *
633  * Remove the initial path from the beginning of a filename
634  *
635  * PARAMS
636  *  lpszPath [I/O] Path to remove the initial path from
637  *
638  * RETURNS
639  *  Nothing.
640  */
641 void WINAPI PathStripPathA(LPSTR lpszPath)
642 {
643   TRACE("(%s)\n", debugstr_a(lpszPath));
644 
645   if (lpszPath)
646   {
647     LPSTR lpszFileName = PathFindFileNameA(lpszPath);
648     if(lpszFileName)
649       RtlMoveMemory(lpszPath, lpszFileName, strlen(lpszFileName)+1);
650   }
651 }
652 
653 /*************************************************************************
654  * PathStripPathW       [SHLWAPI.@]
655  *
656  * See PathStripPathA.
657  */
658 void WINAPI PathStripPathW(LPWSTR lpszPath)
659 {
660   LPWSTR lpszFileName;
661 
662   TRACE("(%s)\n", debugstr_w(lpszPath));
663   lpszFileName = PathFindFileNameW(lpszPath);
664   if(lpszFileName)
665     RtlMoveMemory(lpszPath, lpszFileName, (strlenW(lpszFileName)+1)*sizeof(WCHAR));
666 }
667 
668 /*************************************************************************
669  * PathStripToRootA     [SHLWAPI.@]
670  *
671  * Reduce a path to its root.
672  *
673  * PARAMS
674  *  lpszPath [I/O] the path to reduce
675  *
676  * RETURNS
677  *  Success: TRUE if the stripped path is a root path
678  *  Failure: FALSE if the path cannot be stripped or is NULL
679  */
680 BOOL WINAPI PathStripToRootA(LPSTR lpszPath)
681 {
682   TRACE("(%s)\n", debugstr_a(lpszPath));
683 
684   if (!lpszPath)
685     return FALSE;
686   while(!PathIsRootA(lpszPath))
687     if (!PathRemoveFileSpecA(lpszPath))
688       return FALSE;
689   return TRUE;
690 }
691 
692 /*************************************************************************
693  * PathStripToRootW     [SHLWAPI.@]
694  *
695  * See PathStripToRootA.
696  */
697 BOOL WINAPI PathStripToRootW(LPWSTR lpszPath)
698 {
699   TRACE("(%s)\n", debugstr_w(lpszPath));
700 
701   if (!lpszPath)
702     return FALSE;
703   while(!PathIsRootW(lpszPath))
704     if (!PathRemoveFileSpecW(lpszPath))
705       return FALSE;
706   return TRUE;
707 }
708 
709 /*************************************************************************
710  * PathRemoveArgsA      [SHLWAPI.@]
711  *
712  * Strip space separated arguments from a path.
713  *
714  * PARAMS
715  *  lpszPath [I/O] Path to remove arguments from
716  *
717  * RETURNS
718  *  Nothing.
719  */
720 void WINAPI PathRemoveArgsA(LPSTR lpszPath)
721 {
722   TRACE("(%s)\n",debugstr_a(lpszPath));
723 
724   if(lpszPath)
725   {
726     LPSTR lpszArgs = PathGetArgsA(lpszPath);
727     if (*lpszArgs)
728       lpszArgs[-1] = '\0';
729     else
730     {
731       LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs);
732       if(*lpszLastChar == ' ')
733         *lpszLastChar = '\0';
734     }
735   }
736 }
737 
738 /*************************************************************************
739  * PathRemoveArgsW      [SHLWAPI.@]
740  *
741  * See PathRemoveArgsA.
742  */
743 void WINAPI PathRemoveArgsW(LPWSTR lpszPath)
744 {
745   TRACE("(%s)\n",debugstr_w(lpszPath));
746 
747   if(lpszPath)
748   {
749     LPWSTR lpszArgs = PathGetArgsW(lpszPath);
750     if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' '))
751       lpszArgs[-1] = '\0';
752   }
753 }
754 
755 /*************************************************************************
756  * PathRemoveExtensionA         [SHLWAPI.@]
757  *
758  * Remove the file extension from a path
759  *
760  * PARAMS
761  *  lpszPath [I/O] Path to remove the extension from
762  *
763  * RETURNS
764  *  Nothing.
765  */
766 void WINAPI PathRemoveExtensionA(LPSTR lpszPath)
767 {
768   TRACE("(%s)\n", debugstr_a(lpszPath));
769 
770   if (lpszPath)
771   {
772     lpszPath = PathFindExtensionA(lpszPath);
773     *lpszPath = '\0';
774   }
775 }
776 
777 /*************************************************************************
778  * PathRemoveExtensionW         [SHLWAPI.@]
779  *
780  * See PathRemoveExtensionA.
781 */
782 void WINAPI PathRemoveExtensionW(LPWSTR lpszPath)
783 {
784   TRACE("(%s)\n", debugstr_w(lpszPath));
785 
786   if (lpszPath)
787   {
788     lpszPath = PathFindExtensionW(lpszPath);
789     *lpszPath = '\0';
790   }
791 }
792 
793 /*************************************************************************
794  * PathRemoveBackslashA [SHLWAPI.@]
795  *
796  * Remove a trailing backslash from a path.
797  *
798  * PARAMS
799  *  lpszPath [I/O] Path to remove backslash from
800  *
801  * RETURNS
802  *  Success: A pointer to the end of the path
803  *  Failure: NULL, if lpszPath is NULL
804  */
805 LPSTR WINAPI PathRemoveBackslashA( LPSTR lpszPath )
806 {
807   LPSTR szTemp = NULL;
808 
809   TRACE("(%s)\n", debugstr_a(lpszPath));
810 
811   if(lpszPath)
812   {
813     szTemp = CharPrevA(lpszPath, lpszPath + strlen(lpszPath));
814     if (!PathIsRootA(lpszPath) && *szTemp == '\\')
815       *szTemp = '\0';
816   }
817   return szTemp;
818 }
819 
820 /*************************************************************************
821  * PathRemoveBackslashW [SHLWAPI.@]
822  *
823  * See PathRemoveBackslashA.
824  */
825 LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpszPath )
826 {
827   LPWSTR szTemp = NULL;
828 
829   TRACE("(%s)\n", debugstr_w(lpszPath));
830 
831   if(lpszPath)
832   {
833     szTemp = lpszPath + strlenW(lpszPath);
834     if (szTemp > lpszPath) szTemp--;
835     if (!PathIsRootW(lpszPath) && *szTemp == '\\')
836       *szTemp = '\0';
837   }
838   return szTemp;
839 }
840 
841 /*************************************************************************
842  * PathRemoveBlanksA [SHLWAPI.@]
843  *
844  * Remove Spaces from the start and end of a path.
845  *
846  * PARAMS
847  *  lpszPath [I/O] Path to strip blanks from
848  *
849  * RETURNS
850  *  Nothing.
851  */
852 VOID WINAPI PathRemoveBlanksA(LPSTR lpszPath)
853 {
854   TRACE("(%s)\n", debugstr_a(lpszPath));
855 
856   if(lpszPath && *lpszPath)
857   {
858     LPSTR start = lpszPath;
859 
860     while (*lpszPath == ' ')
861       lpszPath = CharNextA(lpszPath);
862 
863     while(*lpszPath)
864       *start++ = *lpszPath++;
865 
866     if (start != lpszPath)
867       while (start[-1] == ' ')
868         start--;
869     *start = '\0';
870   }
871 }
872 
873 /*************************************************************************
874  * PathRemoveBlanksW [SHLWAPI.@]
875  *
876  * See PathRemoveBlanksA.
877  */
878 VOID WINAPI PathRemoveBlanksW(LPWSTR lpszPath)
879 {
880   TRACE("(%s)\n", debugstr_w(lpszPath));
881 
882   if(lpszPath && *lpszPath)
883   {
884     LPWSTR start = lpszPath;
885 
886     while (*lpszPath == ' ')
887       lpszPath++;
888 
889     while(*lpszPath)
890       *start++ = *lpszPath++;
891 
892     if (start != lpszPath)
893       while (start[-1] == ' ')
894         start--;
895     *start = '\0';
896   }
897 }
898 
899 /*************************************************************************
900  * PathQuoteSpacesA [SHLWAPI.@]
901  *
902  * Surround a path containing spaces in quotes.
903  *
904  * PARAMS
905  *  lpszPath [I/O] Path to quote
906  *
907  * RETURNS
908  *  Nothing.
909  *
910  * NOTES
911  *  The path is not changed if it is invalid or has no spaces.
912  */
913 VOID WINAPI PathQuoteSpacesA(LPSTR lpszPath)
914 {
915   TRACE("(%s)\n", debugstr_a(lpszPath));
916 
917   if(lpszPath && StrChrA(lpszPath,' '))
918   {
919     size_t iLen = strlen(lpszPath) + 1;
920 
921     if (iLen + 2 < MAX_PATH)
922     {
923       memmove(lpszPath + 1, lpszPath, iLen);
924       lpszPath[0] = '"';
925       lpszPath[iLen] = '"';
926       lpszPath[iLen + 1] = '\0';
927     }
928   }
929 }
930 
931 /*************************************************************************
932  * PathQuoteSpacesW [SHLWAPI.@]
933  *
934  * See PathQuoteSpacesA.
935  */
936 VOID WINAPI PathQuoteSpacesW(LPWSTR lpszPath)
937 {
938   TRACE("(%s)\n", debugstr_w(lpszPath));
939 
940   if(lpszPath && StrChrW(lpszPath,' '))
941   {
942     int iLen = strlenW(lpszPath) + 1;
943 
944     if (iLen + 2 < MAX_PATH)
945     {
946       memmove(lpszPath + 1, lpszPath, iLen * sizeof(WCHAR));
947       lpszPath[0] = '"';
948       lpszPath[iLen] = '"';
949       lpszPath[iLen + 1] = '\0';
950     }
951   }
952 }
953 
954 /*************************************************************************
955  * PathUnquoteSpacesA [SHLWAPI.@]
956  *
957  * Remove quotes ("") from around a path, if present.
958  *
959  * PARAMS
960  *  lpszPath [I/O] Path to strip quotes from
961  *
962  * RETURNS
963  *  Nothing
964  *
965  * NOTES
966  *  If the path contains a single quote only, an empty string will result.
967  *  Otherwise quotes are only removed if they appear at the start and end
968  *  of the path.
969  */
970 VOID WINAPI PathUnquoteSpacesA(LPSTR lpszPath)
971 {
972   TRACE("(%s)\n", debugstr_a(lpszPath));
973 
974   if (lpszPath && *lpszPath == '"')
975   {
976     DWORD dwLen = strlen(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  * PathUnquoteSpacesW [SHLWAPI.@]
989  *
990  * See PathUnquoteSpacesA.
991  */
992 VOID WINAPI PathUnquoteSpacesW(LPWSTR lpszPath)
993 {
994   TRACE("(%s)\n", debugstr_w(lpszPath));
995 
996   if (lpszPath && *lpszPath == '"')
997   {
998     DWORD dwLen = strlenW(lpszPath) - 1;
999 
1000     if (lpszPath[dwLen] == '"')
1001     {
1002       lpszPath[dwLen] = '\0';
1003       for (; *lpszPath; lpszPath++)
1004         *lpszPath = lpszPath[1];
1005     }
1006   }
1007 }
1008 
1009 /*************************************************************************
1010  * PathParseIconLocationA  [SHLWAPI.@]
1011  *
1012  * Parse the location of an icon from a path.
1013  *
1014  * PARAMS
1015  *  lpszPath [I/O] The path to parse the icon location from.
1016  *
1017  * RETURNS
1018  *  Success: The number of the icon
1019  *  Failure: 0 if the path does not contain an icon location or is NULL
1020  *
1021  * NOTES
1022  *  The path has surrounding quotes and spaces removed regardless
1023  *  of whether the call succeeds or not.
1024  */
1025 int WINAPI PathParseIconLocationA(LPSTR lpszPath)
1026 {
1027   int iRet = 0;
1028   LPSTR lpszComma;
1029 
1030   TRACE("(%s)\n", debugstr_a(lpszPath));
1031 
1032   if (lpszPath)
1033   {
1034     if ((lpszComma = strchr(lpszPath, ',')))
1035     {
1036       *lpszComma++ = '\0';
1037       iRet = StrToIntA(lpszComma);
1038     }
1039     PathUnquoteSpacesA(lpszPath);
1040     PathRemoveBlanksA(lpszPath);
1041   }
1042   return iRet;
1043 }
1044 
1045 /*************************************************************************
1046  * PathParseIconLocationW  [SHLWAPI.@]
1047  *
1048  * See PathParseIconLocationA.
1049  */
1050 int WINAPI PathParseIconLocationW(LPWSTR lpszPath)
1051 {
1052   int iRet = 0;
1053   LPWSTR lpszComma;
1054 
1055   TRACE("(%s)\n", debugstr_w(lpszPath));
1056 
1057   if (lpszPath)
1058   {
1059     if ((lpszComma = StrChrW(lpszPath, ',')))
1060     {
1061       *lpszComma++ = '\0';
1062       iRet = StrToIntW(lpszComma);
1063     }
1064     PathUnquoteSpacesW(lpszPath);
1065     PathRemoveBlanksW(lpszPath);
1066   }
1067   return iRet;
1068 }
1069 
1070 /*************************************************************************
1071  * @    [SHLWAPI.4]
1072  *
1073  * Unicode version of PathFileExistsDefExtA.
1074  */
1075 BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)
1076 {
1077   static const WCHAR pszExts[7][5] = { { '.', 'p', 'i', 'f', 0},
1078                                        { '.', 'c', 'o', 'm', 0},
1079                                        { '.', 'e', 'x', 'e', 0},
1080                                        { '.', 'b', 'a', 't', 0},
1081                                        { '.', 'l', 'n', 'k', 0},
1082                                        { '.', 'c', 'm', 'd', 0},
1083                                        { 0, 0, 0, 0, 0} };
1084 
1085   TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich);
1086 
1087   if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath))
1088     return FALSE;
1089 
1090   if (dwWhich)
1091   {
1092     LPCWSTR szExt = PathFindExtensionW(lpszPath);
1093     if (!*szExt || dwWhich & 0x40)
1094     {
1095       size_t iChoose = 0;
1096       int iLen = lstrlenW(lpszPath);
1097       if (iLen > (MAX_PATH - 5))
1098         return FALSE;
1099       while ( (dwWhich & 0x1) && pszExts[iChoose][0] )
1100       {
1101         lstrcpyW(lpszPath + iLen, pszExts[iChoose]);
1102         if (PathFileExistsW(lpszPath))
1103           return TRUE;
1104         iChoose++;
1105         dwWhich >>= 1;
1106       }
1107       *(lpszPath + iLen) = (WCHAR)'\0';
1108       return FALSE;
1109     }
1110   }
1111   return PathFileExistsW(lpszPath);
1112 }
1113 
1114 /*************************************************************************
1115  * @    [SHLWAPI.3]
1116  *
1117  * Determine if a file exists locally and is of an executable type.
1118  *
1119  * PARAMS
1120  *  lpszPath       [I/O] File to search for
1121  *  dwWhich        [I]   Type of executable to search for
1122  *
1123  * RETURNS
1124  *  TRUE  If the file was found. lpszPath contains the file name.
1125  *  FALSE Otherwise.
1126  *
1127  * NOTES
1128  *  lpszPath is modified in place and must be at least MAX_PATH in length.
1129  *  If the function returns FALSE, the path is modified to its original state.
1130  *  If the given path contains an extension or dwWhich is 0, executable
1131  *  extensions are not checked.
1132  *
1133  *  Ordinals 3-6 are a classic case of MS exposing limited functionality to
1134  *  users (here through PathFindOnPathA()) and keeping advanced functionality for
1135  *  their own developers exclusive use. Monopoly, anyone?
1136  */
1137 BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)
1138 {
1139   BOOL bRet = FALSE;
1140 
1141   TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich);
1142 
1143   if (lpszPath)
1144   {
1145     WCHAR szPath[MAX_PATH];
1146     MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1147     bRet = PathFileExistsDefExtW(szPath, dwWhich);
1148     if (bRet)
1149       WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
1150   }
1151   return bRet;
1152 }
1153