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