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