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