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