source: trunk/src/shlwapi/path.c@ 10621

Last change on this file since 10621 was 10621, checked in by cinc, 21 years ago

Fix for PathSetDlgItemPathW() by D. Teickner so import of reg files with W98-regedit works (taking sizeof(WCHAR) into account).

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