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