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