[SHLWAPI] Sync with Wine Staging 1.7.55. CORE-10536
[reactos.git] / reactos / dll / win32 / shlwapi / path.c
1 /*
2 * Path Functions
3 *
4 * Copyright 1999, 2000 Juergen Schmied
5 * Copyright 2001, 2002 Jon Griffiths
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include "precomp.h"
23
24 #ifdef __REACTOS__
25 int WINAPI IsNetDrive(int drive);
26 #else
27
28 /* Get a function pointer from a DLL handle */
29 #define GET_FUNC(func, module, name, fail) \
30 do { \
31 if (!func) { \
32 if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
33 func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
34 if (!func) return fail; \
35 } \
36 } while (0)
37
38 /* DLL handles for late bound calls */
39 static HMODULE SHLWAPI_hshell32;
40
41 /* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
42 typedef BOOL (WINAPI *fnpIsNetDrive)(int);
43 static fnpIsNetDrive pIsNetDrive;
44
45 #endif /* __REACTOS__ */
46
47
48 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD);
49
50 static inline WCHAR* heap_strdupAtoW(LPCSTR str)
51 {
52 WCHAR *ret = NULL;
53
54 if (str)
55 {
56 DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
57 ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
58 if (ret)
59 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
60 }
61
62 return ret;
63 }
64
65 /*************************************************************************
66 * PathAppendA [SHLWAPI.@]
67 *
68 * Append one path to another.
69 *
70 * PARAMS
71 * lpszPath [I/O] Initial part of path, and destination for output
72 * lpszAppend [I] Path to append
73 *
74 * RETURNS
75 * Success: TRUE. lpszPath contains the newly created path.
76 * Failure: FALSE, if either path is NULL, or PathCombineA() fails.
77 *
78 * NOTES
79 * lpszAppend must contain at least one backslash ('\') if not NULL.
80 * Because PathCombineA() is used to join the paths, the resulting
81 * path is also canonicalized.
82 */
83 BOOL WINAPI PathAppendA (LPSTR lpszPath, LPCSTR lpszAppend)
84 {
85 TRACE("(%s,%s)\n",debugstr_a(lpszPath), debugstr_a(lpszAppend));
86
87 if (lpszPath && lpszAppend)
88 {
89 if (!PathIsUNCA(lpszAppend))
90 while (*lpszAppend == '\\')
91 lpszAppend++;
92 if (PathCombineA(lpszPath, lpszPath, lpszAppend))
93 return TRUE;
94 }
95 return FALSE;
96 }
97
98 /*************************************************************************
99 * PathAppendW [SHLWAPI.@]
100 *
101 * See PathAppendA.
102 */
103 BOOL WINAPI PathAppendW(LPWSTR lpszPath, LPCWSTR lpszAppend)
104 {
105 TRACE("(%s,%s)\n",debugstr_w(lpszPath), debugstr_w(lpszAppend));
106
107 if (lpszPath && lpszAppend)
108 {
109 if (!PathIsUNCW(lpszAppend))
110 while (*lpszAppend == '\\')
111 lpszAppend++;
112 if (PathCombineW(lpszPath, lpszPath, lpszAppend))
113 return TRUE;
114 }
115 return FALSE;
116 }
117
118 /*************************************************************************
119 * PathCombineA [SHLWAPI.@]
120 *
121 * Combine two paths together.
122 *
123 * PARAMS
124 * lpszDest [O] Destination for combined path
125 * lpszDir [I] Directory path
126 * lpszFile [I] File path
127 *
128 * RETURNS
129 * Success: The output path
130 * Failure: NULL, if inputs are invalid.
131 *
132 * NOTES
133 * lpszDest should be at least MAX_PATH in size, and may point to the same
134 * memory location as lpszDir. The combined path is canonicalised.
135 */
136 LPSTR WINAPI PathCombineA(LPSTR lpszDest, LPCSTR lpszDir, LPCSTR lpszFile)
137 {
138 WCHAR szDest[MAX_PATH];
139 WCHAR szDir[MAX_PATH];
140 WCHAR szFile[MAX_PATH];
141 TRACE("(%p,%s,%s)\n", lpszDest, debugstr_a(lpszDir), debugstr_a(lpszFile));
142
143 /* Invalid parameters */
144 if (!lpszDest)
145 return NULL;
146 if (!lpszDir && !lpszFile)
147 goto fail;
148
149 if (lpszDir)
150 if (!MultiByteToWideChar(CP_ACP,0,lpszDir,-1,szDir,MAX_PATH))
151 goto fail;
152
153 if (lpszFile)
154 if (!MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH))
155 goto fail;
156
157 if (PathCombineW(szDest, lpszDir ? szDir : NULL, lpszFile ? szFile : NULL))
158 if (WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0))
159 return lpszDest;
160
161 fail:
162 lpszDest[0] = 0;
163 return NULL;
164 }
165
166 /*************************************************************************
167 * PathCombineW [SHLWAPI.@]
168 *
169 * See PathCombineA.
170 */
171 LPWSTR WINAPI PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
172 {
173 WCHAR szTemp[MAX_PATH];
174 BOOL bUseBoth = FALSE, bStrip = FALSE;
175
176 TRACE("(%p,%s,%s)\n", lpszDest, debugstr_w(lpszDir), debugstr_w(lpszFile));
177
178 /* Invalid parameters */
179 if (!lpszDest)
180 return NULL;
181 if (!lpszDir && !lpszFile)
182 {
183 lpszDest[0] = 0;
184 return NULL;
185 }
186
187 if ((!lpszFile || !*lpszFile) && lpszDir)
188 {
189 /* Use dir only */
190 lstrcpynW(szTemp, lpszDir, MAX_PATH);
191 }
192 else if (!lpszDir || !*lpszDir || !PathIsRelativeW(lpszFile))
193 {
194 if (!lpszDir || !*lpszDir || *lpszFile != '\\' || PathIsUNCW(lpszFile))
195 {
196 /* Use file only */
197 lstrcpynW(szTemp, lpszFile, MAX_PATH);
198 }
199 else
200 {
201 bUseBoth = TRUE;
202 bStrip = TRUE;
203 }
204 }
205 else
206 bUseBoth = TRUE;
207
208 if (bUseBoth)
209 {
210 lstrcpynW(szTemp, lpszDir, MAX_PATH);
211 if (bStrip)
212 {
213 PathStripToRootW(szTemp);
214 lpszFile++; /* Skip '\' */
215 }
216 if (!PathAddBackslashW(szTemp) || strlenW(szTemp) + strlenW(lpszFile) >= MAX_PATH)
217 {
218 lpszDest[0] = 0;
219 return NULL;
220 }
221 strcatW(szTemp, lpszFile);
222 }
223
224 PathCanonicalizeW(lpszDest, szTemp);
225 return lpszDest;
226 }
227
228 /*************************************************************************
229 * PathAddBackslashA [SHLWAPI.@]
230 *
231 * Append a backslash ('\') to a path if one doesn't exist.
232 *
233 * PARAMS
234 * lpszPath [I/O] The path to append a backslash to.
235 *
236 * RETURNS
237 * Success: The position of the last backslash in the path.
238 * Failure: NULL, if lpszPath is NULL or the path is too large.
239 */
240 LPSTR WINAPI PathAddBackslashA(LPSTR lpszPath)
241 {
242 size_t iLen;
243 LPSTR prev = lpszPath;
244
245 TRACE("(%s)\n",debugstr_a(lpszPath));
246
247 if (!lpszPath || (iLen = strlen(lpszPath)) >= MAX_PATH)
248 return NULL;
249
250 if (iLen)
251 {
252 do {
253 lpszPath = CharNextA(prev);
254 if (*lpszPath)
255 prev = lpszPath;
256 } while (*lpszPath);
257 if (*prev != '\\')
258 {
259 *lpszPath++ = '\\';
260 *lpszPath = '\0';
261 }
262 }
263 return lpszPath;
264 }
265
266 /*************************************************************************
267 * PathAddBackslashW [SHLWAPI.@]
268 *
269 * See PathAddBackslashA.
270 */
271 LPWSTR WINAPI PathAddBackslashW( LPWSTR lpszPath )
272 {
273 size_t iLen;
274
275 TRACE("(%s)\n",debugstr_w(lpszPath));
276
277 if (!lpszPath || (iLen = strlenW(lpszPath)) >= MAX_PATH)
278 return NULL;
279
280 if (iLen)
281 {
282 lpszPath += iLen;
283 if (lpszPath[-1] != '\\')
284 {
285 *lpszPath++ = '\\';
286 *lpszPath = '\0';
287 }
288 }
289 return lpszPath;
290 }
291
292 /*************************************************************************
293 * PathBuildRootA [SHLWAPI.@]
294 *
295 * Create a root drive string (e.g. "A:\") from a drive number.
296 *
297 * PARAMS
298 * lpszPath [O] Destination for the drive string
299 *
300 * RETURNS
301 * lpszPath
302 *
303 * NOTES
304 * If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
305 */
306 LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive)
307 {
308 TRACE("(%p,%d)\n", lpszPath, drive);
309
310 if (lpszPath && drive >= 0 && drive < 26)
311 {
312 lpszPath[0] = 'A' + drive;
313 lpszPath[1] = ':';
314 lpszPath[2] = '\\';
315 lpszPath[3] = '\0';
316 }
317 return lpszPath;
318 }
319
320 /*************************************************************************
321 * PathBuildRootW [SHLWAPI.@]
322 *
323 * See PathBuildRootA.
324 */
325 LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive)
326 {
327 TRACE("(%p,%d)\n", lpszPath, drive);
328
329 if (lpszPath && drive >= 0 && drive < 26)
330 {
331 lpszPath[0] = 'A' + drive;
332 lpszPath[1] = ':';
333 lpszPath[2] = '\\';
334 lpszPath[3] = '\0';
335 }
336 return lpszPath;
337 }
338
339 /*************************************************************************
340 * PathFindFileNameA [SHLWAPI.@]
341 *
342 * Locate the start of the file name in a path
343 *
344 * PARAMS
345 * lpszPath [I] Path to search
346 *
347 * RETURNS
348 * A pointer to the first character of the file name
349 */
350 LPSTR WINAPI PathFindFileNameA(LPCSTR lpszPath)
351 {
352 LPCSTR lastSlash = lpszPath;
353
354 TRACE("(%s)\n",debugstr_a(lpszPath));
355
356 while (lpszPath && *lpszPath)
357 {
358 if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') &&
359 lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/')
360 lastSlash = lpszPath + 1;
361 lpszPath = CharNextA(lpszPath);
362 }
363 return (LPSTR)lastSlash;
364 }
365
366 /*************************************************************************
367 * PathFindFileNameW [SHLWAPI.@]
368 *
369 * See PathFindFileNameA.
370 */
371 LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath)
372 {
373 LPCWSTR lastSlash = lpszPath;
374
375 TRACE("(%s)\n",debugstr_w(lpszPath));
376
377 while (lpszPath && *lpszPath)
378 {
379 if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') &&
380 lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/')
381 lastSlash = lpszPath + 1;
382 lpszPath++;
383 }
384 return (LPWSTR)lastSlash;
385 }
386
387 /*************************************************************************
388 * PathFindExtensionA [SHLWAPI.@]
389 *
390 * Locate the start of the file extension in a path
391 *
392 * PARAMS
393 * lpszPath [I] The path to search
394 *
395 * RETURNS
396 * A pointer to the first character of the extension, the end of
397 * the string if the path has no extension, or NULL If lpszPath is NULL
398 */
399 LPSTR WINAPI PathFindExtensionA( LPCSTR lpszPath )
400 {
401 LPCSTR lastpoint = NULL;
402
403 TRACE("(%s)\n", debugstr_a(lpszPath));
404
405 if (lpszPath)
406 {
407 while (*lpszPath)
408 {
409 if (*lpszPath == '\\' || *lpszPath==' ')
410 lastpoint = NULL;
411 else if (*lpszPath == '.')
412 lastpoint = lpszPath;
413 lpszPath = CharNextA(lpszPath);
414 }
415 }
416 return (LPSTR)(lastpoint ? lastpoint : lpszPath);
417 }
418
419 /*************************************************************************
420 * PathFindExtensionW [SHLWAPI.@]
421 *
422 * See PathFindExtensionA.
423 */
424 LPWSTR WINAPI PathFindExtensionW( LPCWSTR lpszPath )
425 {
426 LPCWSTR lastpoint = NULL;
427
428 TRACE("(%s)\n", debugstr_w(lpszPath));
429
430 if (lpszPath)
431 {
432 while (*lpszPath)
433 {
434 if (*lpszPath == '\\' || *lpszPath==' ')
435 lastpoint = NULL;
436 else if (*lpszPath == '.')
437 lastpoint = lpszPath;
438 lpszPath++;
439 }
440 }
441 return (LPWSTR)(lastpoint ? lastpoint : lpszPath);
442 }
443
444 /*************************************************************************
445 * PathGetArgsA [SHLWAPI.@]
446 *
447 * Find the next argument in a string delimited by spaces.
448 *
449 * PARAMS
450 * lpszPath [I] The string to search for arguments in
451 *
452 * RETURNS
453 * The start of the next argument in lpszPath, or NULL if lpszPath is NULL
454 *
455 * NOTES
456 * Spaces in quoted strings are ignored as delimiters.
457 */
458 LPSTR WINAPI PathGetArgsA(LPCSTR lpszPath)
459 {
460 BOOL bSeenQuote = FALSE;
461
462 TRACE("(%s)\n",debugstr_a(lpszPath));
463
464 if (lpszPath)
465 {
466 while (*lpszPath)
467 {
468 if ((*lpszPath==' ') && !bSeenQuote)
469 return (LPSTR)lpszPath + 1;
470 if (*lpszPath == '"')
471 bSeenQuote = !bSeenQuote;
472 lpszPath = CharNextA(lpszPath);
473 }
474 }
475 return (LPSTR)lpszPath;
476 }
477
478 /*************************************************************************
479 * PathGetArgsW [SHLWAPI.@]
480 *
481 * See PathGetArgsA.
482 */
483 LPWSTR WINAPI PathGetArgsW(LPCWSTR lpszPath)
484 {
485 BOOL bSeenQuote = FALSE;
486
487 TRACE("(%s)\n",debugstr_w(lpszPath));
488
489 if (lpszPath)
490 {
491 while (*lpszPath)
492 {
493 if ((*lpszPath==' ') && !bSeenQuote)
494 return (LPWSTR)lpszPath + 1;
495 if (*lpszPath == '"')
496 bSeenQuote = !bSeenQuote;
497 lpszPath++;
498 }
499 }
500 return (LPWSTR)lpszPath;
501 }
502
503 /*************************************************************************
504 * PathGetDriveNumberA [SHLWAPI.@]
505 *
506 * Return the drive number from a path
507 *
508 * PARAMS
509 * lpszPath [I] Path to get the drive number from
510 *
511 * RETURNS
512 * Success: The drive number corresponding to the drive in the path
513 * Failure: -1, if lpszPath contains no valid drive
514 */
515 int WINAPI PathGetDriveNumberA(LPCSTR lpszPath)
516 {
517 TRACE ("(%s)\n",debugstr_a(lpszPath));
518
519 if (lpszPath && !IsDBCSLeadByte(*lpszPath) && lpszPath[1] == ':' &&
520 tolower(*lpszPath) >= 'a' && tolower(*lpszPath) <= 'z')
521 return tolower(*lpszPath) - 'a';
522 return -1;
523 }
524
525 /*************************************************************************
526 * PathGetDriveNumberW [SHLWAPI.@]
527 *
528 * See PathGetDriveNumberA.
529 */
530 int WINAPI PathGetDriveNumberW(LPCWSTR lpszPath)
531 {
532 TRACE ("(%s)\n",debugstr_w(lpszPath));
533
534 if (lpszPath)
535 {
536 WCHAR tl = tolowerW(lpszPath[0]);
537 if (tl >= 'a' && tl <= 'z' && lpszPath[1] == ':')
538 return tl - 'a';
539 }
540 return -1;
541 }
542
543 /*************************************************************************
544 * PathRemoveFileSpecA [SHLWAPI.@]
545 *
546 * Remove the file specification from a path.
547 *
548 * PARAMS
549 * lpszPath [I/O] Path to remove the file spec from
550 *
551 * RETURNS
552 * TRUE If the path was valid and modified
553 * FALSE Otherwise
554 */
555 BOOL WINAPI PathRemoveFileSpecA(LPSTR lpszPath)
556 {
557 LPSTR lpszFileSpec = lpszPath;
558 BOOL bModified = FALSE;
559
560 TRACE("(%s)\n",debugstr_a(lpszPath));
561
562 if(lpszPath)
563 {
564 /* Skip directory or UNC path */
565 if (*lpszPath == '\\')
566 lpszFileSpec = ++lpszPath;
567 if (*lpszPath == '\\')
568 lpszFileSpec = ++lpszPath;
569
570 while (*lpszPath)
571 {
572 if(*lpszPath == '\\')
573 lpszFileSpec = lpszPath; /* Skip dir */
574 else if(*lpszPath == ':')
575 {
576 lpszFileSpec = ++lpszPath; /* Skip drive */
577 if (*lpszPath == '\\')
578 lpszFileSpec++;
579 }
580 if (!(lpszPath = CharNextA(lpszPath)))
581 break;
582 }
583
584 if (*lpszFileSpec)
585 {
586 *lpszFileSpec = '\0';
587 bModified = TRUE;
588 }
589 }
590 return bModified;
591 }
592
593 /*************************************************************************
594 * PathRemoveFileSpecW [SHLWAPI.@]
595 *
596 * See PathRemoveFileSpecA.
597 */
598 BOOL WINAPI PathRemoveFileSpecW(LPWSTR lpszPath)
599 {
600 LPWSTR lpszFileSpec = lpszPath;
601 BOOL bModified = FALSE;
602
603 TRACE("(%s)\n",debugstr_w(lpszPath));
604
605 if(lpszPath)
606 {
607 /* Skip directory or UNC path */
608 if (*lpszPath == '\\')
609 lpszFileSpec = ++lpszPath;
610 if (*lpszPath == '\\')
611 lpszFileSpec = ++lpszPath;
612
613 while (*lpszPath)
614 {
615 if(*lpszPath == '\\')
616 lpszFileSpec = lpszPath; /* Skip dir */
617 else if(*lpszPath == ':')
618 {
619 lpszFileSpec = ++lpszPath; /* Skip drive */
620 if (*lpszPath == '\\')
621 lpszFileSpec++;
622 }
623 lpszPath++;
624 }
625
626 if (*lpszFileSpec)
627 {
628 *lpszFileSpec = '\0';
629 bModified = TRUE;
630 }
631 }
632 return bModified;
633 }
634
635 /*************************************************************************
636 * PathStripPathA [SHLWAPI.@]
637 *
638 * Remove the initial path from the beginning of a filename
639 *
640 * PARAMS
641 * lpszPath [I/O] Path to remove the initial path from
642 *
643 * RETURNS
644 * Nothing.
645 */
646 void WINAPI PathStripPathA(LPSTR lpszPath)
647 {
648 TRACE("(%s)\n", debugstr_a(lpszPath));
649
650 if (lpszPath)
651 {
652 LPSTR lpszFileName = PathFindFileNameA(lpszPath);
653 if(lpszFileName != lpszPath)
654 RtlMoveMemory(lpszPath, lpszFileName, strlen(lpszFileName)+1);
655 }
656 }
657
658 /*************************************************************************
659 * PathStripPathW [SHLWAPI.@]
660 *
661 * See PathStripPathA.
662 */
663 void WINAPI PathStripPathW(LPWSTR lpszPath)
664 {
665 LPWSTR lpszFileName;
666
667 TRACE("(%s)\n", debugstr_w(lpszPath));
668 lpszFileName = PathFindFileNameW(lpszPath);
669 if(lpszFileName != lpszPath)
670 RtlMoveMemory(lpszPath, lpszFileName, (strlenW(lpszFileName)+1)*sizeof(WCHAR));
671 }
672
673 /*************************************************************************
674 * PathStripToRootA [SHLWAPI.@]
675 *
676 * Reduce a path to its root.
677 *
678 * PARAMS
679 * lpszPath [I/O] the path to reduce
680 *
681 * RETURNS
682 * Success: TRUE if the stripped path is a root path
683 * Failure: FALSE if the path cannot be stripped or is NULL
684 */
685 BOOL WINAPI PathStripToRootA(LPSTR lpszPath)
686 {
687 TRACE("(%s)\n", debugstr_a(lpszPath));
688
689 if (!lpszPath)
690 return FALSE;
691 while(!PathIsRootA(lpszPath))
692 if (!PathRemoveFileSpecA(lpszPath))
693 return FALSE;
694 return TRUE;
695 }
696
697 /*************************************************************************
698 * PathStripToRootW [SHLWAPI.@]
699 *
700 * See PathStripToRootA.
701 */
702 BOOL WINAPI PathStripToRootW(LPWSTR lpszPath)
703 {
704 TRACE("(%s)\n", debugstr_w(lpszPath));
705
706 if (!lpszPath)
707 return FALSE;
708 while(!PathIsRootW(lpszPath))
709 if (!PathRemoveFileSpecW(lpszPath))
710 return FALSE;
711 return TRUE;
712 }
713
714 /*************************************************************************
715 * PathRemoveArgsA [SHLWAPI.@]
716 *
717 * Strip space separated arguments from a path.
718 *
719 * PARAMS
720 * lpszPath [I/O] Path to remove arguments from
721 *
722 * RETURNS
723 * Nothing.
724 */
725 void WINAPI PathRemoveArgsA(LPSTR lpszPath)
726 {
727 TRACE("(%s)\n",debugstr_a(lpszPath));
728
729 if(lpszPath)
730 {
731 LPSTR lpszArgs = PathGetArgsA(lpszPath);
732 if (*lpszArgs)
733 lpszArgs[-1] = '\0';
734 else
735 {
736 LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs);
737 if(*lpszLastChar == ' ')
738 *lpszLastChar = '\0';
739 }
740 }
741 }
742
743 /*************************************************************************
744 * PathRemoveArgsW [SHLWAPI.@]
745 *
746 * See PathRemoveArgsA.
747 */
748 void WINAPI PathRemoveArgsW(LPWSTR lpszPath)
749 {
750 TRACE("(%s)\n",debugstr_w(lpszPath));
751
752 if(lpszPath)
753 {
754 LPWSTR lpszArgs = PathGetArgsW(lpszPath);
755 if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' '))
756 lpszArgs[-1] = '\0';
757 }
758 }
759
760 /*************************************************************************
761 * PathRemoveExtensionA [SHLWAPI.@]
762 *
763 * Remove the file extension from a path
764 *
765 * PARAMS
766 * lpszPath [I/O] Path to remove the extension from
767 *
768 * NOTES
769 * The NUL terminator must be written only if extension exists
770 * and if the pointed character is not already NUL.
771 *
772 * RETURNS
773 * Nothing.
774 */
775 void WINAPI PathRemoveExtensionA(LPSTR lpszPath)
776 {
777 TRACE("(%s)\n", debugstr_a(lpszPath));
778
779 if (lpszPath)
780 {
781 lpszPath = PathFindExtensionA(lpszPath);
782 if (lpszPath && *lpszPath != '\0')
783 *lpszPath = '\0';
784 }
785 }
786
787 /*************************************************************************
788 * PathRemoveExtensionW [SHLWAPI.@]
789 *
790 * See PathRemoveExtensionA.
791 */
792 void WINAPI PathRemoveExtensionW(LPWSTR lpszPath)
793 {
794 TRACE("(%s)\n", debugstr_w(lpszPath));
795
796 if (lpszPath)
797 {
798 lpszPath = PathFindExtensionW(lpszPath);
799 if (lpszPath && *lpszPath != '\0')
800 *lpszPath = '\0';
801 }
802 }
803
804 /*************************************************************************
805 * PathRemoveBackslashA [SHLWAPI.@]
806 *
807 * Remove a trailing backslash from a path.
808 *
809 * PARAMS
810 * lpszPath [I/O] Path to remove backslash from
811 *
812 * RETURNS
813 * Success: A pointer to the end of the path
814 * Failure: NULL, if lpszPath is NULL
815 */
816 LPSTR WINAPI PathRemoveBackslashA( LPSTR lpszPath )
817 {
818 LPSTR szTemp = NULL;
819
820 TRACE("(%s)\n", debugstr_a(lpszPath));
821
822 if(lpszPath)
823 {
824 szTemp = CharPrevA(lpszPath, lpszPath + strlen(lpszPath));
825 if (!PathIsRootA(lpszPath) && *szTemp == '\\')
826 *szTemp = '\0';
827 }
828 return szTemp;
829 }
830
831 /*************************************************************************
832 * PathRemoveBackslashW [SHLWAPI.@]
833 *
834 * See PathRemoveBackslashA.
835 */
836 LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpszPath )
837 {
838 LPWSTR szTemp = NULL;
839
840 TRACE("(%s)\n", debugstr_w(lpszPath));
841
842 if(lpszPath)
843 {
844 szTemp = lpszPath + strlenW(lpszPath);
845 if (szTemp > lpszPath) szTemp--;
846 if (!PathIsRootW(lpszPath) && *szTemp == '\\')
847 *szTemp = '\0';
848 }
849 return szTemp;
850 }
851
852 /*************************************************************************
853 * PathRemoveBlanksA [SHLWAPI.@]
854 *
855 * Remove Spaces from the start and end of a path.
856 *
857 * PARAMS
858 * lpszPath [I/O] Path to strip blanks from
859 *
860 * RETURNS
861 * Nothing.
862 */
863 VOID WINAPI PathRemoveBlanksA(LPSTR lpszPath)
864 {
865 TRACE("(%s)\n", debugstr_a(lpszPath));
866
867 if(lpszPath && *lpszPath)
868 {
869 LPSTR start = lpszPath;
870
871 while (*lpszPath == ' ')
872 lpszPath = CharNextA(lpszPath);
873
874 while(*lpszPath)
875 *start++ = *lpszPath++;
876
877 if (start != lpszPath)
878 while (start[-1] == ' ')
879 start--;
880 *start = '\0';
881 }
882 }
883
884 /*************************************************************************
885 * PathRemoveBlanksW [SHLWAPI.@]
886 *
887 * See PathRemoveBlanksA.
888 */
889 VOID WINAPI PathRemoveBlanksW(LPWSTR lpszPath)
890 {
891 TRACE("(%s)\n", debugstr_w(lpszPath));
892
893 if(lpszPath && *lpszPath)
894 {
895 LPWSTR start = lpszPath;
896
897 while (*lpszPath == ' ')
898 lpszPath++;
899
900 while(*lpszPath)
901 *start++ = *lpszPath++;
902
903 if (start != lpszPath)
904 while (start[-1] == ' ')
905 start--;
906 *start = '\0';
907 }
908 }
909
910 /*************************************************************************
911 * PathQuoteSpacesA [SHLWAPI.@]
912 *
913 * Surround a path containing spaces in quotes.
914 *
915 * PARAMS
916 * lpszPath [I/O] Path to quote
917 *
918 * RETURNS
919 * Nothing.
920 *
921 * NOTES
922 * The path is not changed if it is invalid or has no spaces.
923 */
924 VOID WINAPI PathQuoteSpacesA(LPSTR lpszPath)
925 {
926 TRACE("(%s)\n", debugstr_a(lpszPath));
927
928 if(lpszPath && StrChrA(lpszPath,' '))
929 {
930 size_t iLen = strlen(lpszPath) + 1;
931
932 if (iLen + 2 < MAX_PATH)
933 {
934 memmove(lpszPath + 1, lpszPath, iLen);
935 lpszPath[0] = '"';
936 lpszPath[iLen] = '"';
937 lpszPath[iLen + 1] = '\0';
938 }
939 }
940 }
941
942 /*************************************************************************
943 * PathQuoteSpacesW [SHLWAPI.@]
944 *
945 * See PathQuoteSpacesA.
946 */
947 VOID WINAPI PathQuoteSpacesW(LPWSTR lpszPath)
948 {
949 TRACE("(%s)\n", debugstr_w(lpszPath));
950
951 if(lpszPath && StrChrW(lpszPath,' '))
952 {
953 int iLen = strlenW(lpszPath) + 1;
954
955 if (iLen + 2 < MAX_PATH)
956 {
957 memmove(lpszPath + 1, lpszPath, iLen * sizeof(WCHAR));
958 lpszPath[0] = '"';
959 lpszPath[iLen] = '"';
960 lpszPath[iLen + 1] = '\0';
961 }
962 }
963 }
964
965 /*************************************************************************
966 * PathUnquoteSpacesA [SHLWAPI.@]
967 *
968 * Remove quotes ("") from around a path, if present.
969 *
970 * PARAMS
971 * lpszPath [I/O] Path to strip quotes from
972 *
973 * RETURNS
974 * Nothing
975 *
976 * NOTES
977 * If the path contains a single quote only, an empty string will result.
978 * Otherwise quotes are only removed if they appear at the start and end
979 * of the path.
980 */
981 VOID WINAPI PathUnquoteSpacesA(LPSTR lpszPath)
982 {
983 TRACE("(%s)\n", debugstr_a(lpszPath));
984
985 if (lpszPath && *lpszPath == '"')
986 {
987 DWORD dwLen = strlen(lpszPath) - 1;
988
989 if (lpszPath[dwLen] == '"')
990 {
991 lpszPath[dwLen] = '\0';
992 for (; *lpszPath; lpszPath++)
993 *lpszPath = lpszPath[1];
994 }
995 }
996 }
997
998 /*************************************************************************
999 * PathUnquoteSpacesW [SHLWAPI.@]
1000 *
1001 * See PathUnquoteSpacesA.
1002 */
1003 VOID WINAPI PathUnquoteSpacesW(LPWSTR lpszPath)
1004 {
1005 TRACE("(%s)\n", debugstr_w(lpszPath));
1006
1007 if (lpszPath && *lpszPath == '"')
1008 {
1009 DWORD dwLen = strlenW(lpszPath) - 1;
1010
1011 if (lpszPath[dwLen] == '"')
1012 {
1013 lpszPath[dwLen] = '\0';
1014 for (; *lpszPath; lpszPath++)
1015 *lpszPath = lpszPath[1];
1016 }
1017 }
1018 }
1019
1020 /*************************************************************************
1021 * PathParseIconLocationA [SHLWAPI.@]
1022 *
1023 * Parse the location of an icon from a path.
1024 *
1025 * PARAMS
1026 * lpszPath [I/O] The path to parse the icon location from.
1027 *
1028 * RETURNS
1029 * Success: The number of the icon
1030 * Failure: 0 if the path does not contain an icon location or is NULL
1031 *
1032 * NOTES
1033 * The path has surrounding quotes and spaces removed regardless
1034 * of whether the call succeeds or not.
1035 */
1036 int WINAPI PathParseIconLocationA(LPSTR lpszPath)
1037 {
1038 int iRet = 0;
1039 LPSTR lpszComma;
1040
1041 TRACE("(%s)\n", debugstr_a(lpszPath));
1042
1043 if (lpszPath)
1044 {
1045 if ((lpszComma = strchr(lpszPath, ',')))
1046 {
1047 *lpszComma++ = '\0';
1048 iRet = StrToIntA(lpszComma);
1049 }
1050 PathUnquoteSpacesA(lpszPath);
1051 PathRemoveBlanksA(lpszPath);
1052 }
1053 return iRet;
1054 }
1055
1056 /*************************************************************************
1057 * PathParseIconLocationW [SHLWAPI.@]
1058 *
1059 * See PathParseIconLocationA.
1060 */
1061 int WINAPI PathParseIconLocationW(LPWSTR lpszPath)
1062 {
1063 int iRet = 0;
1064 LPWSTR lpszComma;
1065
1066 TRACE("(%s)\n", debugstr_w(lpszPath));
1067
1068 if (lpszPath)
1069 {
1070 if ((lpszComma = StrChrW(lpszPath, ',')))
1071 {
1072 *lpszComma++ = '\0';
1073 iRet = StrToIntW(lpszComma);
1074 }
1075 PathUnquoteSpacesW(lpszPath);
1076 PathRemoveBlanksW(lpszPath);
1077 }
1078 return iRet;
1079 }
1080
1081 /*************************************************************************
1082 * @ [SHLWAPI.4]
1083 *
1084 * Unicode version of PathFileExistsDefExtA.
1085 */
1086 BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)
1087 {
1088 static const WCHAR pszExts[][5] = { { '.', 'p', 'i', 'f', 0},
1089 { '.', 'c', 'o', 'm', 0},
1090 { '.', 'e', 'x', 'e', 0},
1091 { '.', 'b', 'a', 't', 0},
1092 { '.', 'l', 'n', 'k', 0},
1093 { '.', 'c', 'm', 'd', 0},
1094 { 0, 0, 0, 0, 0} };
1095
1096 TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich);
1097
1098 if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath))
1099 return FALSE;
1100
1101 if (dwWhich)
1102 {
1103 LPCWSTR szExt = PathFindExtensionW(lpszPath);
1104 if (!*szExt || dwWhich & 0x40)
1105 {
1106 size_t iChoose = 0;
1107 int iLen = lstrlenW(lpszPath);
1108 if (iLen > (MAX_PATH - 5))
1109 return FALSE;
1110 while ( (dwWhich & 0x1) && pszExts[iChoose][0] )
1111 {
1112 lstrcpyW(lpszPath + iLen, pszExts[iChoose]);
1113 if (PathFileExistsW(lpszPath))
1114 return TRUE;
1115 iChoose++;
1116 dwWhich >>= 1;
1117 }
1118 *(lpszPath + iLen) = (WCHAR)'\0';
1119 return FALSE;
1120 }
1121 }
1122 return PathFileExistsW(lpszPath);
1123 }
1124
1125 /*************************************************************************
1126 * @ [SHLWAPI.3]
1127 *
1128 * Determine if a file exists locally and is of an executable type.
1129 *
1130 * PARAMS
1131 * lpszPath [I/O] File to search for
1132 * dwWhich [I] Type of executable to search for
1133 *
1134 * RETURNS
1135 * TRUE If the file was found. lpszPath contains the file name.
1136 * FALSE Otherwise.
1137 *
1138 * NOTES
1139 * lpszPath is modified in place and must be at least MAX_PATH in length.
1140 * If the function returns FALSE, the path is modified to its original state.
1141 * If the given path contains an extension or dwWhich is 0, executable
1142 * extensions are not checked.
1143 *
1144 * Ordinals 3-6 are a classic case of MS exposing limited functionality to
1145 * users (here through PathFindOnPathA()) and keeping advanced functionality for
1146 * their own developers exclusive use. Monopoly, anyone?
1147 */
1148 BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)
1149 {
1150 BOOL bRet = FALSE;
1151
1152 TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich);
1153
1154 if (lpszPath)
1155 {
1156 WCHAR szPath[MAX_PATH];
1157 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1158 bRet = PathFileExistsDefExtW(szPath, dwWhich);
1159 if (bRet)
1160 WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
1161 }
1162 return bRet;
1163 }
1164
1165 /*************************************************************************
1166 * SHLWAPI_PathFindInOtherDirs
1167 *
1168 * Internal helper for SHLWAPI_PathFindOnPathExA/W.
1169 */
1170 static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich)
1171 {
1172 static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'};
1173 static const WCHAR szPath[] = { 'P','A','T','H','\0'};
1174 DWORD dwLenPATH;
1175 LPCWSTR lpszCurr;
1176 WCHAR *lpszPATH;
1177 WCHAR buff[MAX_PATH];
1178
1179 TRACE("(%s,%08x)\n", debugstr_w(lpszFile), dwWhich);
1180
1181 /* Try system directories */
1182 GetSystemDirectoryW(buff, MAX_PATH);
1183 if (!PathAppendW(buff, lpszFile))
1184 return FALSE;
1185 if (PathFileExistsDefExtW(buff, dwWhich))
1186 {
1187 strcpyW(lpszFile, buff);
1188 return TRUE;
1189 }
1190 GetWindowsDirectoryW(buff, MAX_PATH);
1191 if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile))
1192 return FALSE;
1193 if (PathFileExistsDefExtW(buff, dwWhich))
1194 {
1195 strcpyW(lpszFile, buff);
1196 return TRUE;
1197 }
1198 GetWindowsDirectoryW(buff, MAX_PATH);
1199 if (!PathAppendW(buff, lpszFile))
1200 return FALSE;
1201 if (PathFileExistsDefExtW(buff, dwWhich))
1202 {
1203 strcpyW(lpszFile, buff);
1204 return TRUE;
1205 }
1206 /* Try dirs listed in %PATH% */
1207 dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH);
1208
1209 if (!dwLenPATH || !(lpszPATH = HeapAlloc(GetProcessHeap(), 0, (dwLenPATH + 1) * sizeof (WCHAR))))
1210 return FALSE;
1211
1212 GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1);
1213 lpszCurr = lpszPATH;
1214 while (lpszCurr)
1215 {
1216 LPCWSTR lpszEnd = lpszCurr;
1217 LPWSTR pBuff = buff;
1218
1219 while (*lpszEnd == ' ')
1220 lpszEnd++;
1221 while (*lpszEnd && *lpszEnd != ';')
1222 *pBuff++ = *lpszEnd++;
1223 *pBuff = '\0';
1224
1225 if (*lpszEnd)
1226 lpszCurr = lpszEnd + 1;
1227 else
1228 lpszCurr = NULL; /* Last Path, terminate after this */
1229
1230 if (!PathAppendW(buff, lpszFile))
1231 {
1232 HeapFree(GetProcessHeap(), 0, lpszPATH);
1233 return FALSE;
1234 }
1235 if (PathFileExistsDefExtW(buff, dwWhich))
1236 {
1237 strcpyW(lpszFile, buff);
1238 HeapFree(GetProcessHeap(), 0, lpszPATH);
1239 return TRUE;
1240 }
1241 }
1242 HeapFree(GetProcessHeap(), 0, lpszPATH);
1243 return FALSE;
1244 }
1245
1246 /*************************************************************************
1247 * @ [SHLWAPI.5]
1248 *
1249 * Search a range of paths for a specific type of executable.
1250 *
1251 * PARAMS
1252 * lpszFile [I/O] File to search for
1253 * lppszOtherDirs [I] Other directories to look in
1254 * dwWhich [I] Type of executable to search for
1255 *
1256 * RETURNS
1257 * Success: TRUE. The path to the executable is stored in lpszFile.
1258 * Failure: FALSE. The path to the executable is unchanged.
1259 */
1260 BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich)
1261 {
1262 WCHAR szFile[MAX_PATH];
1263 WCHAR buff[MAX_PATH];
1264
1265 TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich);
1266
1267 if (!lpszFile || !PathIsFileSpecA(lpszFile))
1268 return FALSE;
1269
1270 MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH);
1271
1272 /* Search provided directories first */
1273 if (lppszOtherDirs && *lppszOtherDirs)
1274 {
1275 WCHAR szOther[MAX_PATH];
1276 LPCSTR *lpszOtherPath = lppszOtherDirs;
1277
1278 while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
1279 {
1280 MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH);
1281 PathCombineW(buff, szOther, szFile);
1282 if (PathFileExistsDefExtW(buff, dwWhich))
1283 {
1284 WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0);
1285 return TRUE;
1286 }
1287 lpszOtherPath++;
1288 }
1289 }
1290 /* Not found, try system and path dirs */
1291 if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich))
1292 {
1293 WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0);
1294 return TRUE;
1295 }
1296 return FALSE;
1297 }
1298
1299 /*************************************************************************
1300 * @ [SHLWAPI.6]
1301 *
1302 * Unicode version of PathFindOnPathExA.
1303 */
1304 BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich)
1305 {
1306 WCHAR buff[MAX_PATH];
1307
1308 TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich);
1309
1310 if (!lpszFile || !PathIsFileSpecW(lpszFile))
1311 return FALSE;
1312
1313 /* Search provided directories first */
1314 if (lppszOtherDirs && *lppszOtherDirs)
1315 {
1316 LPCWSTR *lpszOtherPath = lppszOtherDirs;
1317 while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
1318 {
1319 PathCombineW(buff, *lpszOtherPath, lpszFile);
1320 if (PathFileExistsDefExtW(buff, dwWhich))
1321 {
1322 strcpyW(lpszFile, buff);
1323 return TRUE;
1324 }
1325 lpszOtherPath++;
1326 }
1327 }
1328 /* Not found, try system and path dirs */
1329 return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich);
1330 }
1331
1332 /*************************************************************************
1333 * PathFindOnPathA [SHLWAPI.@]
1334 *
1335 * Search a range of paths for an executable.
1336 *
1337 * PARAMS
1338 * lpszFile [I/O] File to search for
1339 * lppszOtherDirs [I] Other directories to look in
1340 *
1341 * RETURNS
1342 * Success: TRUE. The path to the executable is stored in lpszFile.
1343 * Failure: FALSE. The path to the executable is unchanged.
1344 */
1345 BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs)
1346 {
1347 TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs);
1348 return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0);
1349 }
1350
1351 /*************************************************************************
1352 * PathFindOnPathW [SHLWAPI.@]
1353 *
1354 * See PathFindOnPathA.
1355 */
1356 BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs)
1357 {
1358 TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs);
1359 return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0);
1360 }
1361
1362 /*************************************************************************
1363 * PathCompactPathExA [SHLWAPI.@]
1364 *
1365 * Compact a path into a given number of characters.
1366 *
1367 * PARAMS
1368 * lpszDest [O] Destination for compacted path
1369 * lpszPath [I] Source path
1370 * cchMax [I] Maximum size of compacted path
1371 * dwFlags [I] Reserved
1372 *
1373 * RETURNS
1374 * Success: TRUE. The compacted path is written to lpszDest.
1375 * Failure: FALSE. lpszPath is undefined.
1376 *
1377 * NOTES
1378 * If cchMax is given as 0, lpszDest will still be NUL terminated.
1379 *
1380 * The Win32 version of this function contains a bug: When cchMax == 7,
1381 * 8 bytes will be written to lpszDest. This bug is fixed in the Wine
1382 * implementation.
1383 *
1384 * Some relative paths will be different when cchMax == 5 or 6. This occurs
1385 * because Win32 will insert a "\" in lpszDest, even if one is
1386 * not present in the original path.
1387 */
1388 BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath,
1389 UINT cchMax, DWORD dwFlags)
1390 {
1391 BOOL bRet = FALSE;
1392
1393 TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags);
1394
1395 if (lpszPath && lpszDest)
1396 {
1397 WCHAR szPath[MAX_PATH];
1398 WCHAR szDest[MAX_PATH];
1399
1400 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
1401 szDest[0] = '\0';
1402 bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags);
1403 WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0);
1404 }
1405 return bRet;
1406 }
1407
1408 /*************************************************************************
1409 * PathCompactPathExW [SHLWAPI.@]
1410 *
1411 * See PathCompactPathExA.
1412 */
1413 BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath,
1414 UINT cchMax, DWORD dwFlags)
1415 {
1416 static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
1417 LPCWSTR lpszFile;
1418 DWORD dwLen, dwFileLen = 0;
1419
1420 TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags);
1421
1422 if (!lpszPath)
1423 return FALSE;
1424
1425 if (!lpszDest)
1426 {
1427 WARN("Invalid lpszDest would crash under Win32!\n");
1428 return FALSE;
1429 }
1430
1431 *lpszDest = '\0';
1432
1433 if (cchMax < 2)
1434 return TRUE;
1435
1436 dwLen = strlenW(lpszPath) + 1;
1437
1438 if (dwLen < cchMax)
1439 {
1440 /* Don't need to compact */
1441 memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
1442 return TRUE;
1443 }
1444
1445 /* Path must be compacted to fit into lpszDest */
1446 lpszFile = PathFindFileNameW(lpszPath);
1447 dwFileLen = lpszPath + dwLen - lpszFile;
1448
1449 if (dwFileLen == dwLen)
1450 {
1451 /* No root in psth */
1452 if (cchMax <= 4)
1453 {
1454 while (--cchMax > 0) /* No room left for anything but ellipses */
1455 *lpszDest++ = '.';
1456 *lpszDest = '\0';
1457 return TRUE;
1458 }
1459 /* Compact the file name with ellipses at the end */
1460 cchMax -= 4;
1461 memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
1462 strcpyW(lpszDest + cchMax, szEllipses);
1463 return TRUE;
1464 }
1465 /* We have a root in the path */
1466 lpszFile--; /* Start compacted filename with the path separator */
1467 dwFileLen++;
1468
1469 if (dwFileLen + 3 > cchMax)
1470 {
1471 /* Compact the file name */
1472 if (cchMax <= 4)
1473 {
1474 while (--cchMax > 0) /* No room left for anything but ellipses */
1475 *lpszDest++ = '.';
1476 *lpszDest = '\0';
1477 return TRUE;
1478 }
1479 strcpyW(lpszDest, szEllipses);
1480 lpszDest += 3;
1481 cchMax -= 4;
1482 *lpszDest++ = *lpszFile++;
1483 if (cchMax <= 4)
1484 {
1485 while (--cchMax > 0) /* No room left for anything but ellipses */
1486 *lpszDest++ = '.';
1487 *lpszDest = '\0';
1488 return TRUE;
1489 }
1490 cchMax -= 4;
1491 memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
1492 strcpyW(lpszDest + cchMax, szEllipses);
1493 return TRUE;
1494 }
1495
1496 /* Only the root needs to be Compacted */
1497 dwLen = cchMax - dwFileLen - 3;
1498 memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
1499 strcpyW(lpszDest + dwLen, szEllipses);
1500 strcpyW(lpszDest + dwLen + 3, lpszFile);
1501 return TRUE;
1502 }
1503
1504 /*************************************************************************
1505 * PathIsRelativeA [SHLWAPI.@]
1506 *
1507 * Determine if a path is a relative path.
1508 *
1509 * PARAMS
1510 * lpszPath [I] Path to check
1511 *
1512 * RETURNS
1513 * TRUE: The path is relative, or is invalid.
1514 * FALSE: The path is not relative.
1515 */
1516 BOOL WINAPI PathIsRelativeA (LPCSTR lpszPath)
1517 {
1518 TRACE("(%s)\n",debugstr_a(lpszPath));
1519
1520 if (!lpszPath || !*lpszPath || IsDBCSLeadByte(*lpszPath))
1521 return TRUE;
1522 if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':'))
1523 return FALSE;
1524 return TRUE;
1525 }
1526
1527 /*************************************************************************
1528 * PathIsRelativeW [SHLWAPI.@]
1529 *
1530 * See PathIsRelativeA.
1531 */
1532 BOOL WINAPI PathIsRelativeW (LPCWSTR lpszPath)
1533 {
1534 TRACE("(%s)\n",debugstr_w(lpszPath));
1535
1536 if (!lpszPath || !*lpszPath)
1537 return TRUE;
1538 if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':'))
1539 return FALSE;
1540 return TRUE;
1541 }
1542
1543 /*************************************************************************
1544 * PathIsRootA [SHLWAPI.@]
1545 *
1546 * Determine if a path is a root path.
1547 *
1548 * PARAMS
1549 * lpszPath [I] Path to check
1550 *
1551 * RETURNS
1552 * TRUE If lpszPath is valid and a root path,
1553 * FALSE Otherwise
1554 */
1555 BOOL WINAPI PathIsRootA(LPCSTR lpszPath)
1556 {
1557 TRACE("(%s)\n", debugstr_a(lpszPath));
1558
1559 if (lpszPath && *lpszPath)
1560 {
1561 if (*lpszPath == '\\')
1562 {
1563 if (!lpszPath[1])
1564 return TRUE; /* \ */
1565 else if (lpszPath[1]=='\\')
1566 {
1567 BOOL bSeenSlash = FALSE;
1568 lpszPath += 2;
1569
1570 /* Check for UNC root path */
1571 while (*lpszPath)
1572 {
1573 if (*lpszPath == '\\')
1574 {
1575 if (bSeenSlash)
1576 return FALSE;
1577 bSeenSlash = TRUE;
1578 }
1579 lpszPath = CharNextA(lpszPath);
1580 }
1581 return TRUE;
1582 }
1583 }
1584 else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0')
1585 return TRUE; /* X:\ */
1586 }
1587 return FALSE;
1588 }
1589
1590 /*************************************************************************
1591 * PathIsRootW [SHLWAPI.@]
1592 *
1593 * See PathIsRootA.
1594 */
1595 BOOL WINAPI PathIsRootW(LPCWSTR lpszPath)
1596 {
1597 TRACE("(%s)\n", debugstr_w(lpszPath));
1598
1599 if (lpszPath && *lpszPath)
1600 {
1601 if (*lpszPath == '\\')
1602 {
1603 if (!lpszPath[1])
1604 return TRUE; /* \ */
1605 else if (lpszPath[1]=='\\')
1606 {
1607 BOOL bSeenSlash = FALSE;
1608 lpszPath += 2;
1609
1610 /* Check for UNC root path */
1611 while (*lpszPath)
1612 {
1613 if (*lpszPath == '\\')
1614 {
1615 if (bSeenSlash)
1616 return FALSE;
1617 bSeenSlash = TRUE;
1618 }
1619 lpszPath++;
1620 }
1621 return TRUE;
1622 }
1623 }
1624 else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0')
1625 return TRUE; /* X:\ */
1626 }
1627 return FALSE;
1628 }
1629
1630 /*************************************************************************
1631 * PathIsDirectoryA [SHLWAPI.@]
1632 *
1633 * Determine if a path is a valid directory
1634 *
1635 * PARAMS
1636 * lpszPath [I] Path to check.
1637 *
1638 * RETURNS
1639 * FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
1640 * FALSE if lpszPath is invalid or not a directory.
1641 *
1642 * NOTES
1643 * Although this function is prototyped as returning a BOOL, it returns
1644 * FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
1645 *
1646 *| if (PathIsDirectoryA("c:\\windows\\") == TRUE)
1647 *| ...
1648 *
1649 * will always fail.
1650 */
1651 BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath)
1652 {
1653 DWORD dwAttr;
1654
1655 TRACE("(%s)\n", debugstr_a(lpszPath));
1656
1657 if (!lpszPath || PathIsUNCServerA(lpszPath))
1658 return FALSE;
1659
1660 if (PathIsUNCServerShareA(lpszPath))
1661 {
1662 FIXME("UNC Server Share not yet supported - FAILING\n");
1663 return FALSE;
1664 }
1665
1666 if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES)
1667 return FALSE;
1668 return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
1669 }
1670
1671 /*************************************************************************
1672 * PathIsDirectoryW [SHLWAPI.@]
1673 *
1674 * See PathIsDirectoryA.
1675 */
1676 BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
1677 {
1678 DWORD dwAttr;
1679
1680 TRACE("(%s)\n", debugstr_w(lpszPath));
1681
1682 if (!lpszPath || PathIsUNCServerW(lpszPath))
1683 return FALSE;
1684
1685 if (PathIsUNCServerShareW(lpszPath))
1686 {
1687 FIXME("UNC Server Share not yet supported - FAILING\n");
1688 return FALSE;
1689 }
1690
1691 if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
1692 return FALSE;
1693 return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
1694 }
1695
1696 /*************************************************************************
1697 * PathFileExistsA [SHLWAPI.@]
1698 *
1699 * Determine if a file exists.
1700 *
1701 * PARAMS
1702 * lpszPath [I] Path to check
1703 *
1704 * RETURNS
1705 * TRUE If the file exists and is readable
1706 * FALSE Otherwise
1707 */
1708 BOOL WINAPI PathFileExistsA(LPCSTR lpszPath)
1709 {
1710 UINT iPrevErrMode;
1711 DWORD dwAttr;
1712
1713 TRACE("(%s)\n",debugstr_a(lpszPath));
1714
1715 if (!lpszPath)
1716 return FALSE;
1717
1718 /* Prevent a dialog box if path is on a disk that has been ejected. */
1719 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1720 dwAttr = GetFileAttributesA(lpszPath);
1721 SetErrorMode(iPrevErrMode);
1722 return dwAttr != INVALID_FILE_ATTRIBUTES;
1723 }
1724
1725 /*************************************************************************
1726 * PathFileExistsW [SHLWAPI.@]
1727 *
1728 * See PathFileExistsA.
1729 */
1730 BOOL WINAPI PathFileExistsW(LPCWSTR lpszPath)
1731 {
1732 UINT iPrevErrMode;
1733 DWORD dwAttr;
1734
1735 TRACE("(%s)\n",debugstr_w(lpszPath));
1736
1737 if (!lpszPath)
1738 return FALSE;
1739
1740 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1741 dwAttr = GetFileAttributesW(lpszPath);
1742 SetErrorMode(iPrevErrMode);
1743 return dwAttr != INVALID_FILE_ATTRIBUTES;
1744 }
1745
1746 /*************************************************************************
1747 * PathFileExistsAndAttributesA [SHLWAPI.445]
1748 *
1749 * Determine if a file exists.
1750 *
1751 * PARAMS
1752 * lpszPath [I] Path to check
1753 * dwAttr [O] attributes of file
1754 *
1755 * RETURNS
1756 * TRUE If the file exists and is readable
1757 * FALSE Otherwise
1758 */
1759 BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr)
1760 {
1761 UINT iPrevErrMode;
1762 DWORD dwVal = 0;
1763
1764 TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr);
1765
1766 if (dwAttr)
1767 *dwAttr = INVALID_FILE_ATTRIBUTES;
1768
1769 if (!lpszPath)
1770 return FALSE;
1771
1772 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1773 dwVal = GetFileAttributesA(lpszPath);
1774 SetErrorMode(iPrevErrMode);
1775 if (dwAttr)
1776 *dwAttr = dwVal;
1777 return (dwVal != INVALID_FILE_ATTRIBUTES);
1778 }
1779
1780 /*************************************************************************
1781 * PathFileExistsAndAttributesW [SHLWAPI.446]
1782 *
1783 * See PathFileExistsA.
1784 */
1785 BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr)
1786 {
1787 UINT iPrevErrMode;
1788 DWORD dwVal;
1789
1790 TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr);
1791
1792 if (!lpszPath)
1793 return FALSE;
1794
1795 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1796 dwVal = GetFileAttributesW(lpszPath);
1797 SetErrorMode(iPrevErrMode);
1798 if (dwAttr)
1799 *dwAttr = dwVal;
1800 return (dwVal != INVALID_FILE_ATTRIBUTES);
1801 }
1802
1803 /*************************************************************************
1804 * PathMatchSingleMaskA [internal]
1805 */
1806 static BOOL PathMatchSingleMaskA(LPCSTR name, LPCSTR mask)
1807 {
1808 while (*name && *mask && *mask!=';')
1809 {
1810 if (*mask == '*')
1811 {
1812 do
1813 {
1814 if (PathMatchSingleMaskA(name,mask+1))
1815 return TRUE; /* try substrings */
1816 } while (*name++);
1817 return FALSE;
1818 }
1819
1820 if (toupper(*mask) != toupper(*name) && *mask != '?')
1821 return FALSE;
1822
1823 name = CharNextA(name);
1824 mask = CharNextA(mask);
1825 }
1826
1827 if (!*name)
1828 {
1829 while (*mask == '*')
1830 mask++;
1831 if (!*mask || *mask == ';')
1832 return TRUE;
1833 }
1834 return FALSE;
1835 }
1836
1837 /*************************************************************************
1838 * PathMatchSingleMaskW [internal]
1839 */
1840 static BOOL PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask)
1841 {
1842 while (*name && *mask && *mask != ';')
1843 {
1844 if (*mask == '*')
1845 {
1846 do
1847 {
1848 if (PathMatchSingleMaskW(name,mask+1))
1849 return TRUE; /* try substrings */
1850 } while (*name++);
1851 return FALSE;
1852 }
1853
1854 if (toupperW(*mask) != toupperW(*name) && *mask != '?')
1855 return FALSE;
1856
1857 name++;
1858 mask++;
1859 }
1860 if (!*name)
1861 {
1862 while (*mask == '*')
1863 mask++;
1864 if (!*mask || *mask == ';')
1865 return TRUE;
1866 }
1867 return FALSE;
1868 }
1869
1870 /*************************************************************************
1871 * PathMatchSpecA [SHLWAPI.@]
1872 *
1873 * Determine if a path matches one or more search masks.
1874 *
1875 * PARAMS
1876 * lpszPath [I] Path to check
1877 * lpszMask [I] Search mask(s)
1878 *
1879 * RETURNS
1880 * TRUE If lpszPath is valid and is matched
1881 * FALSE Otherwise
1882 *
1883 * NOTES
1884 * Multiple search masks may be given if they are separated by ";". The
1885 * pattern "*.*" is treated specially in that it matches all paths (for
1886 * backwards compatibility with DOS).
1887 */
1888 BOOL WINAPI PathMatchSpecA(LPCSTR lpszPath, LPCSTR lpszMask)
1889 {
1890 TRACE("(%s,%s)\n", lpszPath, lpszMask);
1891
1892 if (!lstrcmpA(lpszMask, "*.*"))
1893 return TRUE; /* Matches every path */
1894
1895 while (*lpszMask)
1896 {
1897 while (*lpszMask == ' ')
1898 lpszMask++; /* Eat leading spaces */
1899
1900 if (PathMatchSingleMaskA(lpszPath, lpszMask))
1901 return TRUE; /* Matches the current mask */
1902
1903 while (*lpszMask && *lpszMask != ';')
1904 lpszMask = CharNextA(lpszMask); /* masks separated by ';' */
1905
1906 if (*lpszMask == ';')
1907 lpszMask++;
1908 }
1909 return FALSE;
1910 }
1911
1912 /*************************************************************************
1913 * PathMatchSpecW [SHLWAPI.@]
1914 *
1915 * See PathMatchSpecA.
1916 */
1917 BOOL WINAPI PathMatchSpecW(LPCWSTR lpszPath, LPCWSTR lpszMask)
1918 {
1919 static const WCHAR szStarDotStar[] = { '*', '.', '*', '\0' };
1920
1921 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszMask));
1922
1923 if (!lstrcmpW(lpszMask, szStarDotStar))
1924 return TRUE; /* Matches every path */
1925
1926 while (*lpszMask)
1927 {
1928 while (*lpszMask == ' ')
1929 lpszMask++; /* Eat leading spaces */
1930
1931 if (PathMatchSingleMaskW(lpszPath, lpszMask))
1932 return TRUE; /* Matches the current path */
1933
1934 while (*lpszMask && *lpszMask != ';')
1935 lpszMask++; /* masks separated by ';' */
1936
1937 if (*lpszMask == ';')
1938 lpszMask++;
1939 }
1940 return FALSE;
1941 }
1942
1943 /*************************************************************************
1944 * PathIsSameRootA [SHLWAPI.@]
1945 *
1946 * Determine if two paths share the same root.
1947 *
1948 * PARAMS
1949 * lpszPath1 [I] Source path
1950 * lpszPath2 [I] Path to compare with
1951 *
1952 * RETURNS
1953 * TRUE If both paths are valid and share the same root.
1954 * FALSE If either path is invalid or the paths do not share the same root.
1955 */
1956 BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2)
1957 {
1958 LPCSTR lpszStart;
1959 int dwLen;
1960
1961 TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2));
1962
1963 if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1)))
1964 return FALSE;
1965
1966 dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1;
1967 if (lpszStart - lpszPath1 > dwLen)
1968 return FALSE; /* Paths not common up to length of the root */
1969 return TRUE;
1970 }
1971
1972 /*************************************************************************
1973 * PathIsSameRootW [SHLWAPI.@]
1974 *
1975 * See PathIsSameRootA.
1976 */
1977 BOOL WINAPI PathIsSameRootW(LPCWSTR lpszPath1, LPCWSTR lpszPath2)
1978 {
1979 LPCWSTR lpszStart;
1980 int dwLen;
1981
1982 TRACE("(%s,%s)\n", debugstr_w(lpszPath1), debugstr_w(lpszPath2));
1983
1984 if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootW(lpszPath1)))
1985 return FALSE;
1986
1987 dwLen = PathCommonPrefixW(lpszPath1, lpszPath2, NULL) + 1;
1988 if (lpszStart - lpszPath1 > dwLen)
1989 return FALSE; /* Paths not common up to length of the root */
1990 return TRUE;
1991 }
1992
1993 /*************************************************************************
1994 * PathIsContentTypeA [SHLWAPI.@]
1995 *
1996 * Determine if a file is of a given registered content type.
1997 *
1998 * PARAMS
1999 * lpszPath [I] File to check
2000 * lpszContentType [I] Content type to check for
2001 *
2002 * RETURNS
2003 * TRUE If lpszPath is a given registered content type,
2004 * FALSE Otherwise.
2005 *
2006 * NOTES
2007 * This function looks up the registered content type for lpszPath. If
2008 * a content type is registered, it is compared (case insensitively) to
2009 * lpszContentType. Only if this matches does the function succeed.
2010 */
2011 BOOL WINAPI PathIsContentTypeA(LPCSTR lpszPath, LPCSTR lpszContentType)
2012 {
2013 LPCSTR szExt;
2014 DWORD dwDummy;
2015 char szBuff[MAX_PATH];
2016
2017 TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszContentType));
2018
2019 if (lpszPath && (szExt = PathFindExtensionA(lpszPath)) && *szExt &&
2020 !SHGetValueA(HKEY_CLASSES_ROOT, szExt, "Content Type",
2021 REG_NONE, szBuff, &dwDummy) &&
2022 !strcasecmp(lpszContentType, szBuff))
2023 {
2024 return TRUE;
2025 }
2026 return FALSE;
2027 }
2028
2029 /*************************************************************************
2030 * PathIsContentTypeW [SHLWAPI.@]
2031 *
2032 * See PathIsContentTypeA.
2033 */
2034 BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType)
2035 {
2036 static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
2037 LPCWSTR szExt;
2038 DWORD dwDummy;
2039 WCHAR szBuff[MAX_PATH];
2040
2041 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType));
2042
2043 if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt &&
2044 !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType,
2045 REG_NONE, szBuff, &dwDummy) &&
2046 !strcmpiW(lpszContentType, szBuff))
2047 {
2048 return TRUE;
2049 }
2050 return FALSE;
2051 }
2052
2053 /*************************************************************************
2054 * PathIsFileSpecA [SHLWAPI.@]
2055 *
2056 * Determine if a path is a file specification.
2057 *
2058 * PARAMS
2059 * lpszPath [I] Path to check
2060 *
2061 * RETURNS
2062 * TRUE If lpszPath is a file specification (i.e. Contains no directories).
2063 * FALSE Otherwise.
2064 */
2065 BOOL WINAPI PathIsFileSpecA(LPCSTR lpszPath)
2066 {
2067 TRACE("(%s)\n", debugstr_a(lpszPath));
2068
2069 if (!lpszPath)
2070 return FALSE;
2071
2072 while (*lpszPath)
2073 {
2074 if (*lpszPath == '\\' || *lpszPath == ':')
2075 return FALSE;
2076 lpszPath = CharNextA(lpszPath);
2077 }
2078 return TRUE;
2079 }
2080
2081 /*************************************************************************
2082 * PathIsFileSpecW [SHLWAPI.@]
2083 *
2084 * See PathIsFileSpecA.
2085 */
2086 BOOL WINAPI PathIsFileSpecW(LPCWSTR lpszPath)
2087 {
2088 TRACE("(%s)\n", debugstr_w(lpszPath));
2089
2090 if (!lpszPath)
2091 return FALSE;
2092
2093 while (*lpszPath)
2094 {
2095 if (*lpszPath == '\\' || *lpszPath == ':')
2096 return FALSE;
2097 lpszPath++;
2098 }
2099 return TRUE;
2100 }
2101
2102 /*************************************************************************
2103 * PathIsPrefixA [SHLWAPI.@]
2104 *
2105 * Determine if a path is a prefix of another.
2106 *
2107 * PARAMS
2108 * lpszPrefix [I] Prefix
2109 * lpszPath [I] Path to check
2110 *
2111 * RETURNS
2112 * TRUE If lpszPath has lpszPrefix as its prefix,
2113 * FALSE If either path is NULL or lpszPrefix is not a prefix
2114 */
2115 BOOL WINAPI PathIsPrefixA (LPCSTR lpszPrefix, LPCSTR lpszPath)
2116 {
2117 TRACE("(%s,%s)\n", debugstr_a(lpszPrefix), debugstr_a(lpszPath));
2118
2119 if (lpszPrefix && lpszPath &&
2120 PathCommonPrefixA(lpszPath, lpszPrefix, NULL) == (int)strlen(lpszPrefix))
2121 return TRUE;
2122 return FALSE;
2123 }
2124
2125 /*************************************************************************
2126 * PathIsPrefixW [SHLWAPI.@]
2127 *
2128 * See PathIsPrefixA.
2129 */
2130 BOOL WINAPI PathIsPrefixW(LPCWSTR lpszPrefix, LPCWSTR lpszPath)
2131 {
2132 TRACE("(%s,%s)\n", debugstr_w(lpszPrefix), debugstr_w(lpszPath));
2133
2134 if (lpszPrefix && lpszPath &&
2135 PathCommonPrefixW(lpszPath, lpszPrefix, NULL) == (int)strlenW(lpszPrefix))
2136 return TRUE;
2137 return FALSE;
2138 }
2139
2140 /*************************************************************************
2141 * PathIsSystemFolderA [SHLWAPI.@]
2142 *
2143 * Determine if a path or file attributes are a system folder.
2144 *
2145 * PARAMS
2146 * lpszPath [I] Path to check.
2147 * dwAttrib [I] Attributes to check, if lpszPath is NULL.
2148 *
2149 * RETURNS
2150 * TRUE If lpszPath or dwAttrib are a system folder.
2151 * FALSE If GetFileAttributesA() fails or neither parameter is a system folder.
2152 */
2153 BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib)
2154 {
2155 TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath), dwAttrib);
2156
2157 if (lpszPath && *lpszPath)
2158 dwAttrib = GetFileAttributesA(lpszPath);
2159
2160 if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
2161 !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
2162 return FALSE;
2163 return TRUE;
2164 }
2165
2166 /*************************************************************************
2167 * PathIsSystemFolderW [SHLWAPI.@]
2168 *
2169 * See PathIsSystemFolderA.
2170 */
2171 BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib)
2172 {
2173 TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath), dwAttrib);
2174
2175 if (lpszPath && *lpszPath)
2176 dwAttrib = GetFileAttributesW(lpszPath);
2177
2178 if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
2179 !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
2180 return FALSE;
2181 return TRUE;
2182 }
2183
2184 /*************************************************************************
2185 * PathIsUNCA [SHLWAPI.@]
2186 *
2187 * Determine if a path is in UNC format.
2188 *
2189 * PARAMS
2190 * lpszPath [I] Path to check
2191 *
2192 * RETURNS
2193 * TRUE: The path is UNC.
2194 * FALSE: The path is not UNC or is NULL.
2195 */
2196 BOOL WINAPI PathIsUNCA(LPCSTR lpszPath)
2197 {
2198 TRACE("(%s)\n",debugstr_a(lpszPath));
2199
2200 #ifdef __REACTOS__
2201 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?'))
2202 #else
2203 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\'))
2204 #endif
2205 return TRUE;
2206 return FALSE;
2207 }
2208
2209 /*************************************************************************
2210 * PathIsUNCW [SHLWAPI.@]
2211 *
2212 * See PathIsUNCA.
2213 */
2214 BOOL WINAPI PathIsUNCW(LPCWSTR lpszPath)
2215 {
2216 TRACE("(%s)\n",debugstr_w(lpszPath));
2217
2218 #ifdef __REACTOS__
2219 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?'))
2220 #else
2221 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\'))
2222 #endif
2223 return TRUE;
2224 return FALSE;
2225 }
2226
2227 /*************************************************************************
2228 * PathIsUNCServerA [SHLWAPI.@]
2229 *
2230 * Determine if a path is a UNC server name ("\\SHARENAME").
2231 *
2232 * PARAMS
2233 * lpszPath [I] Path to check.
2234 *
2235 * RETURNS
2236 * TRUE If lpszPath is a valid UNC server name.
2237 * FALSE Otherwise.
2238 *
2239 * NOTES
2240 * This routine is bug compatible with Win32: Server names with a
2241 * trailing backslash (e.g. "\\FOO\"), return FALSE incorrectly.
2242 * Fixing this bug may break other shlwapi functions!
2243 */
2244 BOOL WINAPI PathIsUNCServerA(LPCSTR lpszPath)
2245 {
2246 TRACE("(%s)\n", debugstr_a(lpszPath));
2247
2248 if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2249 {
2250 while (*lpszPath)
2251 {
2252 if (*lpszPath == '\\')
2253 return FALSE;
2254 lpszPath = CharNextA(lpszPath);
2255 }
2256 return TRUE;
2257 }
2258 return FALSE;
2259 }
2260
2261 /*************************************************************************
2262 * PathIsUNCServerW [SHLWAPI.@]
2263 *
2264 * See PathIsUNCServerA.
2265 */
2266 BOOL WINAPI PathIsUNCServerW(LPCWSTR lpszPath)
2267 {
2268 TRACE("(%s)\n", debugstr_w(lpszPath));
2269
2270 if (lpszPath && lpszPath[0] == '\\' && lpszPath[1] == '\\')
2271 {
2272 return !strchrW( lpszPath + 2, '\\' );
2273 }
2274 return FALSE;
2275 }
2276
2277 /*************************************************************************
2278 * PathIsUNCServerShareA [SHLWAPI.@]
2279 *
2280 * Determine if a path is a UNC server share ("\\SHARENAME\SHARE").
2281 *
2282 * PARAMS
2283 * lpszPath [I] Path to check.
2284 *
2285 * RETURNS
2286 * TRUE If lpszPath is a valid UNC server share.
2287 * FALSE Otherwise.
2288 *
2289 * NOTES
2290 * This routine is bug compatible with Win32: Server shares with a
2291 * trailing backslash (e.g. "\\FOO\BAR\"), return FALSE incorrectly.
2292 * Fixing this bug may break other shlwapi functions!
2293 */
2294 BOOL WINAPI PathIsUNCServerShareA(LPCSTR lpszPath)
2295 {
2296 TRACE("(%s)\n", debugstr_a(lpszPath));
2297
2298 if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2299 {
2300 BOOL bSeenSlash = FALSE;
2301 while (*lpszPath)
2302 {
2303 if (*lpszPath == '\\')
2304 {
2305 if (bSeenSlash)
2306 return FALSE;
2307 bSeenSlash = TRUE;
2308 }
2309 lpszPath = CharNextA(lpszPath);
2310 }
2311 return bSeenSlash;
2312 }
2313 return FALSE;
2314 }
2315
2316 /*************************************************************************
2317 * PathIsUNCServerShareW [SHLWAPI.@]
2318 *
2319 * See PathIsUNCServerShareA.
2320 */
2321 BOOL WINAPI PathIsUNCServerShareW(LPCWSTR lpszPath)
2322 {
2323 TRACE("(%s)\n", debugstr_w(lpszPath));
2324
2325 if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\')
2326 {
2327 BOOL bSeenSlash = FALSE;
2328 while (*lpszPath)
2329 {
2330 if (*lpszPath == '\\')
2331 {
2332 if (bSeenSlash)
2333 return FALSE;
2334 bSeenSlash = TRUE;
2335 }
2336 lpszPath++;
2337 }
2338 return bSeenSlash;
2339 }
2340 return FALSE;
2341 }
2342
2343 /*************************************************************************
2344 * PathCanonicalizeA [SHLWAPI.@]
2345 *
2346 * Convert a path to its canonical form.
2347 *
2348 * PARAMS
2349 * lpszBuf [O] Output path
2350 * lpszPath [I] Path to canonicalize
2351 *
2352 * RETURNS
2353 * Success: TRUE. lpszBuf contains the output path,
2354 * Failure: FALSE, If input path is invalid. lpszBuf is undefined
2355 */
2356 BOOL WINAPI PathCanonicalizeA(LPSTR lpszBuf, LPCSTR lpszPath)
2357 {
2358 BOOL bRet = FALSE;
2359
2360 TRACE("(%p,%s)\n", lpszBuf, debugstr_a(lpszPath));
2361
2362 if (lpszBuf)
2363 *lpszBuf = '\0';
2364
2365 if (!lpszBuf || !lpszPath)
2366 SetLastError(ERROR_INVALID_PARAMETER);
2367 else
2368 {
2369 WCHAR szPath[MAX_PATH];
2370 WCHAR szBuff[MAX_PATH];
2371 int ret = MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
2372
2373 if (!ret) {
2374 WARN("Failed to convert string to widechar (too long?), LE %d.\n", GetLastError());
2375 return FALSE;
2376 }
2377 bRet = PathCanonicalizeW(szBuff, szPath);
2378 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszBuf,MAX_PATH,0,0);
2379 }
2380 return bRet;
2381 }
2382
2383
2384 /*************************************************************************
2385 * PathCanonicalizeW [SHLWAPI.@]
2386 *
2387 * See PathCanonicalizeA.
2388 */
2389 BOOL WINAPI PathCanonicalizeW(LPWSTR lpszBuf, LPCWSTR lpszPath)
2390 {
2391 LPWSTR lpszDst = lpszBuf;
2392 LPCWSTR lpszSrc = lpszPath;
2393
2394 TRACE("(%p,%s)\n", lpszBuf, debugstr_w(lpszPath));
2395
2396 if (lpszBuf)
2397 *lpszDst = '\0';
2398
2399 if (!lpszBuf || !lpszPath)
2400 {
2401 SetLastError(ERROR_INVALID_PARAMETER);
2402 return FALSE;
2403 }
2404
2405 if (!*lpszPath)
2406 {
2407 *lpszBuf++ = '\\';
2408 *lpszBuf = '\0';
2409 return TRUE;
2410 }
2411
2412 /* Copy path root */
2413 if (*lpszSrc == '\\')
2414 {
2415 *lpszDst++ = *lpszSrc++;
2416 }
2417 else if (*lpszSrc && lpszSrc[1] == ':')
2418 {
2419 /* X:\ */
2420 *lpszDst++ = *lpszSrc++;
2421 *lpszDst++ = *lpszSrc++;
2422 if (*lpszSrc == '\\')
2423 *lpszDst++ = *lpszSrc++;
2424 }
2425
2426 /* Canonicalize the rest of the path */
2427 while (*lpszSrc)
2428 {
2429 if (*lpszSrc == '.')
2430 {
2431 if (lpszSrc[1] == '\\' && (lpszSrc == lpszPath || lpszSrc[-1] == '\\' || lpszSrc[-1] == ':'))
2432 {
2433 lpszSrc += 2; /* Skip .\ */
2434 }
2435 else if (lpszSrc[1] == '.' && (lpszDst == lpszBuf || lpszDst[-1] == '\\'))
2436 {
2437 /* \.. backs up a directory, over the root if it has no \ following X:.
2438 * .. is ignored if it would remove a UNC server name or initial \\
2439 */
2440 if (lpszDst != lpszBuf)
2441 {
2442 *lpszDst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */
2443 if (lpszDst > lpszBuf+1 && lpszDst[-1] == '\\' &&
2444 (lpszDst[-2] != '\\' || lpszDst > lpszBuf+2))
2445 {
2446 if (lpszDst[-2] == ':' && (lpszDst > lpszBuf+3 || lpszDst[-3] == ':'))
2447 {
2448 lpszDst -= 2;
2449 while (lpszDst > lpszBuf && *lpszDst != '\\')
2450 lpszDst--;
2451 if (*lpszDst == '\\')
2452 lpszDst++; /* Reset to last '\' */
2453 else
2454 lpszDst = lpszBuf; /* Start path again from new root */
2455 }
2456 else if (lpszDst[-2] != ':' && !PathIsUNCServerShareW(lpszBuf))
2457 lpszDst -= 2;
2458 }
2459 while (lpszDst > lpszBuf && *lpszDst != '\\')
2460 lpszDst--;
2461 if (lpszDst == lpszBuf)
2462 {
2463 *lpszDst++ = '\\';
2464 lpszSrc++;
2465 }
2466 }
2467 lpszSrc += 2; /* Skip .. in src path */
2468 }
2469 else
2470 *lpszDst++ = *lpszSrc++;
2471 }
2472 else
2473 *lpszDst++ = *lpszSrc++;
2474 }
2475 /* Append \ to naked drive specs */
2476 if (lpszDst - lpszBuf == 2 && lpszDst[-1] == ':')
2477 *lpszDst++ = '\\';
2478 *lpszDst++ = '\0';
2479 return TRUE;
2480 }
2481
2482 /*************************************************************************
2483 * PathFindNextComponentA [SHLWAPI.@]
2484 *
2485 * Find the next component in a path.
2486 *
2487 * PARAMS
2488 * lpszPath [I] Path to find next component in
2489 *
2490 * RETURNS
2491 * Success: A pointer to the next component, or the end of the string.
2492 * Failure: NULL, If lpszPath is invalid
2493 *
2494 * NOTES
2495 * A 'component' is either a backslash character (\) or UNC marker (\\).
2496 * Because of this, relative paths (e.g "c:foo") are regarded as having
2497 * only one component.
2498 */
2499 LPSTR WINAPI PathFindNextComponentA(LPCSTR lpszPath)
2500 {
2501 LPSTR lpszSlash;
2502
2503 TRACE("(%s)\n", debugstr_a(lpszPath));
2504
2505 if(!lpszPath || !*lpszPath)
2506 return NULL;
2507
2508 if ((lpszSlash = StrChrA(lpszPath, '\\')))
2509 {
2510 if (lpszSlash[1] == '\\')
2511 lpszSlash++;
2512 return lpszSlash + 1;
2513 }
2514 return (LPSTR)lpszPath + strlen(lpszPath);
2515 }
2516
2517 /*************************************************************************
2518 * PathFindNextComponentW [SHLWAPI.@]
2519 *
2520 * See PathFindNextComponentA.
2521 */
2522 LPWSTR WINAPI PathFindNextComponentW(LPCWSTR lpszPath)
2523 {
2524 LPWSTR lpszSlash;
2525
2526 TRACE("(%s)\n", debugstr_w(lpszPath));
2527
2528 if(!lpszPath || !*lpszPath)
2529 return NULL;
2530
2531 if ((lpszSlash = StrChrW(lpszPath, '\\')))
2532 {
2533 if (lpszSlash[1] == '\\')
2534 lpszSlash++;
2535 return lpszSlash + 1;
2536 }
2537 return (LPWSTR)lpszPath + strlenW(lpszPath);
2538 }
2539
2540 /*************************************************************************
2541 * PathAddExtensionA [SHLWAPI.@]
2542 *
2543 * Add a file extension to a path
2544 *
2545 * PARAMS
2546 * lpszPath [I/O] Path to add extension to
2547 * lpszExtension [I] Extension to add to lpszPath
2548 *
2549 * RETURNS
2550 * TRUE If the path was modified,
2551 * FALSE If lpszPath or lpszExtension are invalid, lpszPath has an
2552 * extension already, or the new path length is too big.
2553 *
2554 * FIXME
2555 * What version of shlwapi.dll adds "exe" if lpszExtension is NULL? Win2k
2556 * does not do this, so the behaviour was removed.
2557 */
2558 BOOL WINAPI PathAddExtensionA(LPSTR lpszPath, LPCSTR lpszExtension)
2559 {
2560 size_t dwLen;
2561
2562 TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExtension));
2563
2564 if (!lpszPath || !lpszExtension || *(PathFindExtensionA(lpszPath)))
2565 return FALSE;
2566
2567 dwLen = strlen(lpszPath);
2568
2569 if (dwLen + strlen(lpszExtension) >= MAX_PATH)
2570 return FALSE;
2571
2572 strcpy(lpszPath + dwLen, lpszExtension);
2573 return TRUE;
2574 }
2575
2576 /*************************************************************************
2577 * PathAddExtensionW [SHLWAPI.@]
2578 *
2579 * See PathAddExtensionA.
2580 */
2581 BOOL WINAPI PathAddExtensionW(LPWSTR lpszPath, LPCWSTR lpszExtension)
2582 {
2583 size_t dwLen;
2584
2585 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExtension));
2586
2587 if (!lpszPath || !lpszExtension || *(PathFindExtensionW(lpszPath)))
2588 return FALSE;
2589
2590 dwLen = strlenW(lpszPath);
2591
2592 if (dwLen + strlenW(lpszExtension) >= MAX_PATH)
2593 return FALSE;
2594
2595 strcpyW(lpszPath + dwLen, lpszExtension);
2596 return TRUE;
2597 }
2598
2599 /*************************************************************************
2600 * PathMakePrettyA [SHLWAPI.@]
2601 *
2602 * Convert an uppercase DOS filename into lowercase.
2603 *
2604 * PARAMS
2605 * lpszPath [I/O] Path to convert.
2606 *
2607 * RETURNS
2608 * TRUE If the path was an uppercase DOS path and was converted,
2609 * FALSE Otherwise.
2610 */
2611 BOOL WINAPI PathMakePrettyA(LPSTR lpszPath)
2612 {
2613 LPSTR pszIter = lpszPath;
2614
2615 TRACE("(%s)\n", debugstr_a(lpszPath));
2616
2617 if (!pszIter)
2618 return FALSE;
2619
2620 if (*pszIter)
2621 {
2622 do
2623 {
2624 if (islower(*pszIter) || IsDBCSLeadByte(*pszIter))
2625 return FALSE; /* Not DOS path */
2626 pszIter++;
2627 } while (*pszIter);
2628 pszIter = lpszPath + 1;
2629 while (*pszIter)
2630 {
2631 *pszIter = tolower(*pszIter);
2632 pszIter++;
2633 }
2634 }
2635 return TRUE;
2636 }
2637
2638 /*************************************************************************
2639 * PathMakePrettyW [SHLWAPI.@]
2640 *
2641 * See PathMakePrettyA.
2642 */
2643 BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath)
2644 {
2645 LPWSTR pszIter = lpszPath;
2646
2647 TRACE("(%s)\n", debugstr_w(lpszPath));
2648
2649 if (!pszIter)
2650 return FALSE;
2651
2652 if (*pszIter)
2653 {
2654 do
2655 {
2656 if (islowerW(*pszIter))
2657 return FALSE; /* Not DOS path */
2658 pszIter++;
2659 } while (*pszIter);
2660 pszIter = lpszPath + 1;
2661 while (*pszIter)
2662 {
2663 *pszIter = tolowerW(*pszIter);
2664 pszIter++;
2665 }
2666 }
2667 return TRUE;
2668 }
2669
2670 /*************************************************************************
2671 * PathCommonPrefixA [SHLWAPI.@]
2672 *
2673 * Determine the length of the common prefix between two paths.
2674 *
2675 * PARAMS
2676 * lpszFile1 [I] First path for comparison
2677 * lpszFile2 [I] Second path for comparison
2678 * achPath [O] Destination for common prefix string
2679 *
2680 * RETURNS
2681 * The length of the common prefix. This is 0 if there is no common
2682 * prefix between the paths or if any parameters are invalid. If the prefix
2683 * is non-zero and achPath is not NULL, achPath is filled with the common
2684 * part of the prefix and NUL terminated.
2685 *
2686 * NOTES
2687 * A common prefix of 2 is always returned as 3. It is thus possible for
2688 * the length returned to be invalid (i.e. Longer than one or both of the
2689 * strings given as parameters). This Win32 behaviour has been implemented
2690 * here, and cannot be changed (fixed?) without breaking other SHLWAPI calls.
2691 * To work around this when using this function, always check that the byte
2692 * at [common_prefix_len-1] is not a NUL. If it is, deduct 1 from the prefix.
2693 */
2694 int WINAPI PathCommonPrefixA(LPCSTR lpszFile1, LPCSTR lpszFile2, LPSTR achPath)
2695 {
2696 size_t iLen = 0;
2697 LPCSTR lpszIter1 = lpszFile1;
2698 LPCSTR lpszIter2 = lpszFile2;
2699
2700 TRACE("(%s,%s,%p)\n", debugstr_a(lpszFile1), debugstr_a(lpszFile2), achPath);
2701
2702 if (achPath)
2703 *achPath = '\0';
2704
2705 if (!lpszFile1 || !lpszFile2)
2706 return 0;
2707
2708 /* Handle roots first */
2709 if (PathIsUNCA(lpszFile1))
2710 {
2711 if (!PathIsUNCA(lpszFile2))
2712 return 0;
2713 lpszIter1 += 2;
2714 lpszIter2 += 2;
2715 }
2716 else if (PathIsUNCA(lpszFile2))
2717 return 0; /* Know already lpszFile1 is not UNC */
2718
2719 do
2720 {
2721 /* Update len */
2722 if ((!*lpszIter1 || *lpszIter1 == '\\') &&
2723 (!*lpszIter2 || *lpszIter2 == '\\'))
2724 iLen = lpszIter1 - lpszFile1; /* Common to this point */
2725
2726 if (!*lpszIter1 || (tolower(*lpszIter1) != tolower(*lpszIter2)))
2727 break; /* Strings differ at this point */
2728
2729 lpszIter1++;
2730 lpszIter2++;
2731 } while (1);
2732
2733 if (iLen == 2)
2734 iLen++; /* Feature/Bug compatible with Win32 */
2735
2736 if (iLen && achPath)
2737 {
2738 memcpy(achPath,lpszFile1,iLen);
2739 achPath[iLen] = '\0';
2740 }
2741 return iLen;
2742 }
2743
2744 /*************************************************************************
2745 * PathCommonPrefixW [SHLWAPI.@]
2746 *
2747 * See PathCommonPrefixA.
2748 */
2749 int WINAPI PathCommonPrefixW(LPCWSTR lpszFile1, LPCWSTR lpszFile2, LPWSTR achPath)
2750 {
2751 size_t iLen = 0;
2752 LPCWSTR lpszIter1 = lpszFile1;
2753 LPCWSTR lpszIter2 = lpszFile2;
2754
2755 TRACE("(%s,%s,%p)\n", debugstr_w(lpszFile1), debugstr_w(lpszFile2), achPath);
2756
2757 if (achPath)
2758 *achPath = '\0';
2759
2760 if (!lpszFile1 || !lpszFile2)
2761 return 0;
2762
2763 /* Handle roots first */
2764 if (PathIsUNCW(lpszFile1))
2765 {
2766 if (!PathIsUNCW(lpszFile2))
2767 return 0;
2768 lpszIter1 += 2;
2769 lpszIter2 += 2;
2770 }
2771 else if (PathIsUNCW(lpszFile2))
2772 return 0; /* Know already lpszFile1 is not UNC */
2773
2774 do
2775 {
2776 /* Update len */
2777 if ((!*lpszIter1 || *lpszIter1 == '\\') &&
2778 (!*lpszIter2 || *lpszIter2 == '\\'))
2779 iLen = lpszIter1 - lpszFile1; /* Common to this point */
2780
2781 if (!*lpszIter1 || (tolowerW(*lpszIter1) != tolowerW(*lpszIter2)))
2782 break; /* Strings differ at this point */
2783
2784 lpszIter1++;
2785 lpszIter2++;
2786 } while (1);
2787
2788 if (iLen == 2)
2789 iLen++; /* Feature/Bug compatible with Win32 */
2790
2791 if (iLen && achPath)
2792 {
2793 memcpy(achPath,lpszFile1,iLen * sizeof(WCHAR));
2794 achPath[iLen] = '\0';
2795 }
2796 return iLen;
2797 }
2798
2799 /*************************************************************************
2800 * PathCompactPathA [SHLWAPI.@]
2801 *
2802 * Make a path fit into a given width when printed to a DC.
2803 *
2804 * PARAMS
2805 * hDc [I] Destination DC
2806 * lpszPath [I/O] Path to be printed to hDc
2807 * dx [I] Desired width
2808 *
2809 * RETURNS
2810 * TRUE If the path was modified/went well.
2811 * FALSE Otherwise.
2812 */
2813 BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx)
2814 {
2815 BOOL bRet = FALSE;
2816
2817 TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx);
2818
2819 if (lpszPath)
2820 {
2821 WCHAR szPath[MAX_PATH];
2822 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
2823 bRet = PathCompactPathW(hDC, szPath, dx);
2824 WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
2825 }
2826 return bRet;
2827 }
2828
2829 /*************************************************************************
2830 * PathCompactPathW [SHLWAPI.@]
2831 *
2832 * See PathCompactPathA.
2833 */
2834 BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx)
2835 {
2836 static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
2837 BOOL bRet = TRUE;
2838 HDC hdc = 0;
2839 WCHAR buff[MAX_PATH];
2840 SIZE size;
2841 DWORD dwLen;
2842
2843 TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx);
2844
2845 if (!lpszPath)
2846 return FALSE;
2847
2848 if (!hDC)
2849 hdc = hDC = GetDC(0);
2850
2851 /* Get the length of the whole path */
2852 dwLen = strlenW(lpszPath);
2853 GetTextExtentPointW(hDC, lpszPath, dwLen, &size);
2854
2855 if ((UINT)size.cx > dx)
2856 {
2857 /* Path too big, must reduce it */
2858 LPWSTR sFile;
2859 DWORD dwEllipsesLen = 0, dwPathLen = 0;
2860
2861 sFile = PathFindFileNameW(lpszPath);
2862 if (sFile != lpszPath) sFile--;
2863
2864 /* Get the size of ellipses */
2865 GetTextExtentPointW(hDC, szEllipses, 3, &size);
2866 dwEllipsesLen = size.cx;
2867 /* Get the size of the file name */
2868 GetTextExtentPointW(hDC, sFile, strlenW(sFile), &size);
2869 dwPathLen = size.cx;
2870
2871 if (sFile != lpszPath)
2872 {
2873 LPWSTR sPath = sFile;
2874 BOOL bEllipses = FALSE;
2875
2876 /* The path includes a file name. Include as much of the path prior to
2877 * the file name as possible, allowing for the ellipses, e.g:
2878 * c:\some very long path\filename ==> c:\some v...\filename
2879 */
2880 lstrcpynW(buff, sFile, MAX_PATH);
2881
2882 do
2883 {
2884 DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen;
2885
2886 GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size);
2887 dwTotalLen += size.cx;
2888 if (dwTotalLen <= dx)
2889 break;
2890 sPath--;
2891 if (!bEllipses)
2892 {
2893 bEllipses = TRUE;
2894 sPath -= 2;
2895 }
2896 } while (sPath > lpszPath);
2897
2898 if (sPath > lpszPath)
2899 {
2900 if (bEllipses)
2901 {
2902 strcpyW(sPath, szEllipses);
2903 strcpyW(sPath+3, buff);
2904 }
2905 bRet = TRUE;
2906 goto end;
2907 }
2908 strcpyW(lpszPath, szEllipses);
2909 strcpyW(lpszPath+3, buff);
2910 bRet = FALSE;
2911 goto end;
2912 }
2913
2914 /* Trim the path by adding ellipses to the end, e.g:
2915 * A very long file name.txt ==> A very...
2916 */
2917 dwLen = strlenW(lpszPath);
2918
2919 if (dwLen > MAX_PATH - 3)
2920 dwLen = MAX_PATH - 3;
2921 lstrcpynW(buff, sFile, dwLen);
2922
2923 do {
2924 dwLen--;
2925 GetTextExtentPointW(hDC, buff, dwLen, &size);
2926 } while (dwLen && size.cx + dwEllipsesLen > dx);
2927
2928 if (!dwLen)
2929 {
2930 DWORD dwWritten = 0;
2931
2932 dwEllipsesLen /= 3; /* Size of a single '.' */
2933
2934 /* Write as much of the Ellipses string as possible */
2935 while (dwWritten + dwEllipsesLen < dx && dwLen < 3)
2936 {
2937 *lpszPath++ = '.';
2938 dwWritten += dwEllipsesLen;
2939 dwLen++;
2940 }
2941 *lpszPath = '\0';
2942 bRet = FALSE;
2943 }
2944 else
2945 {
2946 strcpyW(buff + dwLen, szEllipses);
2947 strcpyW(lpszPath, buff);
2948 }
2949 }
2950
2951 end:
2952 if (hdc)
2953 ReleaseDC(0, hdc);
2954
2955 return bRet;
2956 }
2957
2958 /*************************************************************************
2959 * PathGetCharTypeA [SHLWAPI.@]
2960 *
2961 * Categorise a character from a file path.
2962 *
2963 * PARAMS
2964 * ch [I] Character to get the type of
2965 *
2966 * RETURNS
2967 * A set of GCT_ bit flags (from "shlwapi.h") indicating the character type.
2968 */
2969 UINT WINAPI PathGetCharTypeA(UCHAR ch)
2970 {
2971 return PathGetCharTypeW(ch);
2972 }
2973
2974 /*************************************************************************
2975 * PathGetCharTypeW [SHLWAPI.@]
2976 *
2977 * See PathGetCharTypeA.
2978 */
2979 UINT WINAPI PathGetCharTypeW(WCHAR ch)
2980 {
2981 UINT flags = 0;
2982
2983 TRACE("(%d)\n", ch);
2984
2985 if (!ch || ch < ' ' || ch == '<' || ch == '>' ||
2986 ch == '"' || ch == '|' || ch == '/')
2987 flags = GCT_INVALID; /* Invalid */
2988 else if (ch == '*' || ch=='?')
2989 flags = GCT_WILD; /* Wildchars */
2990 else if ((ch == '\\') || (ch == ':'))
2991 return GCT_SEPARATOR; /* Path separators */
2992 else
2993 {
2994 if (ch < 126)
2995 {
2996 if (((ch & 0x1) && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' ||
2997 ch == '.' || ch == '@' || ch == '^' ||
2998 ch == '\'' || ch == 130 || ch == '`')
2999 flags |= GCT_SHORTCHAR; /* All these are valid for DOS */
3000 }
3001 else
3002 flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */
3003 flags |= GCT_LFNCHAR; /* Valid for long file names */
3004 }
3005 return flags;
3006 }
3007
3008 /*************************************************************************
3009 * SHLWAPI_UseSystemForSystemFolders
3010 *
3011 * Internal helper for PathMakeSystemFolderW.
3012 */
3013 static BOOL SHLWAPI_UseSystemForSystemFolders(void)
3014 {
3015 static BOOL bCheckedReg = FALSE;
3016 static BOOL bUseSystemForSystemFolders = FALSE;
3017
3018 if (!bCheckedReg)
3019 {
3020 bCheckedReg = TRUE;
3021
3022 /* Key tells Win what file attributes to use on system folders */
3023 if (SHGetValueA(HKEY_LOCAL_MACHINE,
3024 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
3025 "UseSystemForSystemFolders", 0, 0, 0))
3026 bUseSystemForSystemFolders = TRUE;
3027 }
3028 return bUseSystemForSystemFolders;
3029 }
3030
3031 /*************************************************************************
3032 * PathMakeSystemFolderA [SHLWAPI.@]
3033 *
3034 * Set system folder attribute for a path.
3035 *
3036 * PARAMS
3037 * lpszPath [I] The path to turn into a system folder
3038 *
3039 * RETURNS
3040 * TRUE If the path was changed to/already was a system folder
3041 * FALSE If the path is invalid or SetFileAttributesA() fails
3042 */
3043 BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath)
3044 {
3045 BOOL bRet = FALSE;
3046
3047 TRACE("(%s)\n", debugstr_a(lpszPath));
3048
3049 if (lpszPath && *lpszPath)
3050 {
3051 WCHAR szPath[MAX_PATH];
3052 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
3053 bRet = PathMakeSystemFolderW(szPath);
3054 }
3055 return bRet;
3056 }
3057
3058 /*************************************************************************
3059 * PathMakeSystemFolderW [SHLWAPI.@]
3060 *
3061 * See PathMakeSystemFolderA.
3062 */
3063 BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath)
3064 {
3065 DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr;
3066 WCHAR buff[MAX_PATH];
3067
3068 TRACE("(%s)\n", debugstr_w(lpszPath));
3069
3070 if (!lpszPath || !*lpszPath)
3071 return FALSE;
3072
3073 /* If the directory is already a system directory, don't do anything */
3074 GetSystemDirectoryW(buff, MAX_PATH);
3075 if (!strcmpW(buff, lpszPath))
3076 return TRUE;
3077
3078 GetWindowsDirectoryW(buff, MAX_PATH);
3079 if (!strcmpW(buff, lpszPath))
3080 return TRUE;
3081
3082 /* "UseSystemForSystemFolders" Tells Win what attributes to use */
3083 if (SHLWAPI_UseSystemForSystemFolders())
3084 dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM;
3085
3086 if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
3087 return FALSE;
3088
3089 /* Change file attributes to system attributes */
3090 dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
3091 return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr);
3092 }
3093
3094 /*************************************************************************
3095 * PathRenameExtensionA [SHLWAPI.@]
3096 *
3097 * Swap the file extension in a path with another extension.
3098 *
3099 * PARAMS
3100 * lpszPath [I/O] Path to swap the extension in
3101 * lpszExt [I] The new extension
3102 *
3103 * RETURNS
3104 * TRUE if lpszPath was modified,
3105 * FALSE if lpszPath or lpszExt is NULL, or the new path is too long
3106 */
3107 BOOL WINAPI PathRenameExtensionA(LPSTR lpszPath, LPCSTR lpszExt)
3108 {
3109 LPSTR lpszExtension;
3110
3111 TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExt));
3112
3113 lpszExtension = PathFindExtensionA(lpszPath);
3114
3115 if (!lpszExtension || (lpszExtension - lpszPath + strlen(lpszExt) >= MAX_PATH))
3116 return FALSE;
3117
3118 strcpy(lpszExtension, lpszExt);
3119 return TRUE;
3120 }
3121
3122 /*************************************************************************
3123 * PathRenameExtensionW [SHLWAPI.@]
3124 *
3125 * See PathRenameExtensionA.
3126 */
3127 BOOL WINAPI PathRenameExtensionW(LPWSTR lpszPath, LPCWSTR lpszExt)
3128 {
3129 LPWSTR lpszExtension;
3130
3131 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExt));
3132
3133 lpszExtension = PathFindExtensionW(lpszPath);
3134
3135 if (!lpszExtension || (lpszExtension - lpszPath + strlenW(lpszExt) >= MAX_PATH))
3136 return FALSE;
3137
3138 strcpyW(lpszExtension, lpszExt);
3139 return TRUE;
3140 }
3141
3142 /*************************************************************************
3143 * PathSearchAndQualifyA [SHLWAPI.@]
3144 *
3145 * Determine if a given path is correct and fully qualified.
3146 *
3147 * PARAMS
3148 * lpszPath [I] Path to check
3149 * lpszBuf [O] Output for correct path
3150 * cchBuf [I] Size of lpszBuf
3151 *
3152 * RETURNS
3153 * Unknown.
3154 */
3155 BOOL WINAPI PathSearchAndQualifyA(LPCSTR lpszPath, LPSTR lpszBuf, UINT cchBuf)
3156 {
3157 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszPath), lpszBuf, cchBuf);
3158
3159 if(SearchPathA(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL))
3160 return TRUE;
3161 return !!GetFullPathNameA(lpszPath, cchBuf, lpszBuf, NULL);
3162 }
3163
3164 /*************************************************************************
3165 * PathSearchAndQualifyW [SHLWAPI.@]
3166 *
3167 * See PathSearchAndQualifyA.
3168 */
3169 BOOL WINAPI PathSearchAndQualifyW(LPCWSTR lpszPath, LPWSTR lpszBuf, UINT cchBuf)
3170 {
3171 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszPath), lpszBuf, cchBuf);
3172
3173 if(SearchPathW(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL))
3174 return TRUE;
3175 return !!GetFullPathNameW(lpszPath, cchBuf, lpszBuf, NULL);
3176 }
3177
3178 /*************************************************************************
3179 * PathSkipRootA [SHLWAPI.@]
3180 *
3181 * Return the portion of a path following the drive letter or mount point.
3182 *
3183 * PARAMS
3184 * lpszPath [I] The path to skip on
3185 *
3186 * RETURNS
3187 * Success: A pointer to the next character after the root.
3188 * Failure: NULL, if lpszPath is invalid, has no root or is a multibyte string.
3189 */
3190 LPSTR WINAPI PathSkipRootA(LPCSTR lpszPath)
3191 {
3192 TRACE("(%s)\n", debugstr_a(lpszPath));
3193
3194 if (!lpszPath || !*lpszPath)
3195 return NULL;
3196
3197 if (*lpszPath == '\\' && lpszPath[1] == '\\')
3198 {
3199 /* Network share: skip share server and mount point */
3200 lpszPath += 2;
3201 if ((lpszPath = StrChrA(lpszPath, '\\')) &&
3202 (lpszPath = StrChrA(lpszPath + 1, '\\')))
3203 lpszPath++;
3204 return (LPSTR)lpszPath;
3205 }
3206
3207 if (IsDBCSLeadByte(*lpszPath))
3208 return NULL;
3209
3210 /* Check x:\ */
3211 if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\')
3212 return (LPSTR)lpszPath + 3;
3213 return NULL;
3214 }
3215
3216 /*************************************************************************
3217 * PathSkipRootW [SHLWAPI.@]
3218 *
3219 * See PathSkipRootA.
3220 */
3221 LPWSTR WINAPI PathSkipRootW(LPCWSTR lpszPath)
3222 {
3223 TRACE("(%s)\n", debugstr_w(lpszPath));
3224
3225 if (!lpszPath || !*lpszPath)
3226 return NULL;
3227
3228 if (*lpszPath == '\\' && lpszPath[1] == '\\')
3229 {
3230 /* Network share: skip share server and mount point */
3231 lpszPath += 2;
3232 if ((lpszPath = StrChrW(lpszPath, '\\')) &&
3233 (lpszPath = StrChrW(lpszPath + 1, '\\')))
3234 lpszPath++;
3235 return (LPWSTR)lpszPath;
3236 }
3237
3238 /* Check x:\ */
3239 if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\')
3240 return (LPWSTR)lpszPath + 3;
3241 return NULL;
3242 }
3243
3244 /*************************************************************************
3245 * PathCreateFromUrlA [SHLWAPI.@]
3246 *
3247 * See PathCreateFromUrlW
3248 */
3249 HRESULT WINAPI PathCreateFromUrlA(LPCSTR pszUrl, LPSTR pszPath,
3250 LPDWORD pcchPath, DWORD dwReserved)
3251 {
3252 WCHAR bufW[MAX_PATH];
3253 WCHAR *pathW = bufW;
3254 UNICODE_STRING urlW;
3255 HRESULT ret;
3256 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
3257
3258 if (!pszUrl || !pszPath || !pcchPath || !*pcchPath)
3259 return E_INVALIDARG;
3260
3261 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
3262 return E_INVALIDARG;
3263 if((ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved)) == E_POINTER) {
3264 pathW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
3265 ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved);
3266 }
3267 if(ret == S_OK) {
3268 RtlUnicodeToMultiByteSize(&lenA, pathW, lenW * sizeof(WCHAR));
3269 if(*pcchPath > lenA) {
3270 RtlUnicodeToMultiByteN(pszPath, *pcchPath - 1, &lenA, pathW, lenW * sizeof(WCHAR));
3271 pszPath[lenA] = 0;
3272 *pcchPath = lenA;
3273 } else {
3274 *pcchPath = lenA + 1;
3275 ret = E_POINTER;
3276 }
3277 }
3278 if(pathW != bufW) HeapFree(GetProcessHeap(), 0, pathW);
3279 RtlFreeUnicodeString(&urlW);
3280 return ret;
3281 }
3282
3283 /*************************************************************************
3284 * PathCreateFromUrlW [SHLWAPI.@]
3285 *
3286 * Create a path from a URL
3287 *
3288 * PARAMS
3289 * lpszUrl [I] URL to convert into a path
3290 * lpszPath [O] Output buffer for the resulting Path
3291 * pcchPath [I] Length of lpszPath
3292 * dwFlags [I] Flags controlling the conversion
3293 *
3294 * RETURNS
3295 * Success: S_OK. lpszPath contains the URL in path format,
3296 * Failure: An HRESULT error code such as E_INVALIDARG.
3297 */
3298 HRESULT WINAPI PathCreateFromUrlW(LPCWSTR pszUrl, LPWSTR pszPath,
3299 LPDWORD pcchPath, DWORD dwReserved)
3300 {
3301 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
3302 static const WCHAR localhost[] = { 'l','o','c','a','l','h','o','s','t',0 };
3303 DWORD nslashes, unescape, len;
3304 const WCHAR *src;
3305 WCHAR *tpath, *dst;
3306 HRESULT ret;
3307
3308 TRACE("(%s,%p,%p,0x%08x)\n", debugstr_w(pszUrl), pszPath, pcchPath, dwReserved);
3309
3310 if (!pszUrl || !pszPath || !pcchPath || !*pcchPath)
3311 return E_INVALIDARG;
3312
3313 if (lstrlenW(pszUrl) < 5)
3314 return E_INVALIDARG;
3315
3316 if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
3317 file_colon, 5) != CSTR_EQUAL)
3318 return E_INVALIDARG;
3319 pszUrl += 5;
3320 ret = S_OK;
3321
3322 src = pszUrl;
3323 nslashes = 0;
3324 while (*src == '/' || *src == '\\') {
3325 nslashes++;
3326 src++;
3327 }
3328
3329 /* We need a temporary buffer so we can compute what size to ask for.
3330 * We know that the final string won't be longer than the current pszUrl
3331 * plus at most two backslashes. All the other transformations make it
3332 * shorter.
3333 */
3334 len = 2 + lstrlenW(pszUrl) + 1;
3335 if (*pcchPath < len)