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