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