4 * Copyright 1999, 2000 Juergen Schmied
5 * Copyright 2001, 2002 Jon Griffiths
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.
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.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #define NO_SHLWAPI_STREAM
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
38 /* Get a function pointer from a DLL handle */
39 #define GET_FUNC(func, module, name, fail) \
42 if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
43 func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
44 if (!func) return fail; \
48 /* DLL handles for late bound calls */
49 static HMODULE SHLWAPI_hshell32
;
51 /* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
52 typedef BOOL (WINAPI
*fnpIsNetDrive
)(int);
53 static fnpIsNetDrive pIsNetDrive
;
55 HRESULT WINAPI
SHGetWebFolderFilePathW(LPCWSTR
,LPWSTR
,DWORD
);
57 /*************************************************************************
58 * PathBuildRootA [SHLWAPI.@]
60 * Create a root drive string (e.g. "A:\") from a drive number.
63 * lpszPath [O] Destination for the drive string
69 * If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
71 LPSTR WINAPI
PathBuildRootA(LPSTR lpszPath
, int drive
)
73 TRACE("(%p,%d)\n", lpszPath
, drive
);
75 if (lpszPath
&& drive
>= 0 && drive
< 26)
77 lpszPath
[0] = 'A' + drive
;
85 /*************************************************************************
86 * PathBuildRootW [SHLWAPI.@]
90 LPWSTR WINAPI
PathBuildRootW(LPWSTR lpszPath
, int drive
)
92 TRACE("(%p,%d)\n", lpszPath
, drive
);
94 if (lpszPath
&& drive
>= 0 && drive
< 26)
96 lpszPath
[0] = 'A' + drive
;
104 /*************************************************************************
105 * PathRemoveArgsA [SHLWAPI.@]
107 * Strip space separated arguments from a path.
110 * lpszPath [I/O] Path to remove arguments from
115 void WINAPI
PathRemoveArgsA(LPSTR lpszPath
)
117 TRACE("(%s)\n",debugstr_a(lpszPath
));
121 LPSTR lpszArgs
= PathGetArgsA(lpszPath
);
126 LPSTR lpszLastChar
= CharPrevA(lpszPath
, lpszArgs
);
127 if(*lpszLastChar
== ' ')
128 *lpszLastChar
= '\0';
133 /*************************************************************************
134 * PathRemoveArgsW [SHLWAPI.@]
136 * See PathRemoveArgsA.
138 void WINAPI
PathRemoveArgsW(LPWSTR lpszPath
)
140 TRACE("(%s)\n",debugstr_w(lpszPath
));
144 LPWSTR lpszArgs
= PathGetArgsW(lpszPath
);
145 if (*lpszArgs
|| (lpszArgs
> lpszPath
&& lpszArgs
[-1] == ' '))
150 /*************************************************************************
153 * Unicode version of PathFileExistsDefExtA.
155 BOOL WINAPI
PathFileExistsDefExtW(LPWSTR lpszPath
,DWORD dwWhich
)
157 static const WCHAR pszExts
[][5] = { { '.', 'p', 'i', 'f', 0},
158 { '.', 'c', 'o', 'm', 0},
159 { '.', 'e', 'x', 'e', 0},
160 { '.', 'b', 'a', 't', 0},
161 { '.', 'l', 'n', 'k', 0},
162 { '.', 'c', 'm', 'd', 0},
165 TRACE("(%s,%d)\n", debugstr_w(lpszPath
), dwWhich
);
167 if (!lpszPath
|| PathIsUNCServerW(lpszPath
) || PathIsUNCServerShareW(lpszPath
))
172 LPCWSTR szExt
= PathFindExtensionW(lpszPath
);
173 if (!*szExt
|| dwWhich
& 0x40)
176 int iLen
= lstrlenW(lpszPath
);
177 if (iLen
> (MAX_PATH
- 5))
179 while ( (dwWhich
& 0x1) && pszExts
[iChoose
][0] )
181 lstrcpyW(lpszPath
+ iLen
, pszExts
[iChoose
]);
182 if (PathFileExistsW(lpszPath
))
187 *(lpszPath
+ iLen
) = (WCHAR
)'\0';
191 return PathFileExistsW(lpszPath
);
194 /*************************************************************************
197 * Determine if a file exists locally and is of an executable type.
200 * lpszPath [I/O] File to search for
201 * dwWhich [I] Type of executable to search for
204 * TRUE If the file was found. lpszPath contains the file name.
208 * lpszPath is modified in place and must be at least MAX_PATH in length.
209 * If the function returns FALSE, the path is modified to its original state.
210 * If the given path contains an extension or dwWhich is 0, executable
211 * extensions are not checked.
213 * Ordinals 3-6 are a classic case of MS exposing limited functionality to
214 * users (here through PathFindOnPathA()) and keeping advanced functionality for
215 * their own developers exclusive use. Monopoly, anyone?
217 BOOL WINAPI
PathFileExistsDefExtA(LPSTR lpszPath
,DWORD dwWhich
)
221 TRACE("(%s,%d)\n", debugstr_a(lpszPath
), dwWhich
);
225 WCHAR szPath
[MAX_PATH
];
226 MultiByteToWideChar(CP_ACP
,0,lpszPath
,-1,szPath
,MAX_PATH
);
227 bRet
= PathFileExistsDefExtW(szPath
, dwWhich
);
229 WideCharToMultiByte(CP_ACP
,0,szPath
,-1,lpszPath
,MAX_PATH
,0,0);
234 /*************************************************************************
235 * SHLWAPI_PathFindInOtherDirs
237 * Internal helper for SHLWAPI_PathFindOnPathExA/W.
239 static BOOL
SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile
, DWORD dwWhich
)
241 static const WCHAR szSystem
[] = { 'S','y','s','t','e','m','\0'};
242 static const WCHAR szPath
[] = { 'P','A','T','H','\0'};
246 WCHAR buff
[MAX_PATH
];
248 TRACE("(%s,%08x)\n", debugstr_w(lpszFile
), dwWhich
);
250 /* Try system directories */
251 GetSystemDirectoryW(buff
, MAX_PATH
);
252 if (!PathAppendW(buff
, lpszFile
))
254 if (PathFileExistsDefExtW(buff
, dwWhich
))
256 lstrcpyW(lpszFile
, buff
);
259 GetWindowsDirectoryW(buff
, MAX_PATH
);
260 if (!PathAppendW(buff
, szSystem
) || !PathAppendW(buff
, lpszFile
))
262 if (PathFileExistsDefExtW(buff
, dwWhich
))
264 lstrcpyW(lpszFile
, buff
);
267 GetWindowsDirectoryW(buff
, MAX_PATH
);
268 if (!PathAppendW(buff
, lpszFile
))
270 if (PathFileExistsDefExtW(buff
, dwWhich
))
272 lstrcpyW(lpszFile
, buff
);
275 /* Try dirs listed in %PATH% */
276 dwLenPATH
= GetEnvironmentVariableW(szPath
, buff
, MAX_PATH
);
278 if (!dwLenPATH
|| !(lpszPATH
= HeapAlloc(GetProcessHeap(), 0, (dwLenPATH
+ 1) * sizeof (WCHAR
))))
281 GetEnvironmentVariableW(szPath
, lpszPATH
, dwLenPATH
+ 1);
285 LPCWSTR lpszEnd
= lpszCurr
;
288 while (*lpszEnd
== ' ')
290 while (*lpszEnd
&& *lpszEnd
!= ';')
291 *pBuff
++ = *lpszEnd
++;
295 lpszCurr
= lpszEnd
+ 1;
297 lpszCurr
= NULL
; /* Last Path, terminate after this */
299 if (!PathAppendW(buff
, lpszFile
))
301 HeapFree(GetProcessHeap(), 0, lpszPATH
);
304 if (PathFileExistsDefExtW(buff
, dwWhich
))
306 lstrcpyW(lpszFile
, buff
);
307 HeapFree(GetProcessHeap(), 0, lpszPATH
);
311 HeapFree(GetProcessHeap(), 0, lpszPATH
);
315 /*************************************************************************
318 * Search a range of paths for a specific type of executable.
321 * lpszFile [I/O] File to search for
322 * lppszOtherDirs [I] Other directories to look in
323 * dwWhich [I] Type of executable to search for
326 * Success: TRUE. The path to the executable is stored in lpszFile.
327 * Failure: FALSE. The path to the executable is unchanged.
329 BOOL WINAPI
PathFindOnPathExA(LPSTR lpszFile
,LPCSTR
*lppszOtherDirs
,DWORD dwWhich
)
331 WCHAR szFile
[MAX_PATH
];
332 WCHAR buff
[MAX_PATH
];
334 TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile
), lppszOtherDirs
, dwWhich
);
336 if (!lpszFile
|| !PathIsFileSpecA(lpszFile
))
339 MultiByteToWideChar(CP_ACP
,0,lpszFile
,-1,szFile
,MAX_PATH
);
341 /* Search provided directories first */
342 if (lppszOtherDirs
&& *lppszOtherDirs
)
344 WCHAR szOther
[MAX_PATH
];
345 LPCSTR
*lpszOtherPath
= lppszOtherDirs
;
347 while (lpszOtherPath
&& *lpszOtherPath
&& (*lpszOtherPath
)[0])
349 MultiByteToWideChar(CP_ACP
,0,*lpszOtherPath
,-1,szOther
,MAX_PATH
);
350 PathCombineW(buff
, szOther
, szFile
);
351 if (PathFileExistsDefExtW(buff
, dwWhich
))
353 WideCharToMultiByte(CP_ACP
,0,buff
,-1,lpszFile
,MAX_PATH
,0,0);
359 /* Not found, try system and path dirs */
360 if (SHLWAPI_PathFindInOtherDirs(szFile
, dwWhich
))
362 WideCharToMultiByte(CP_ACP
,0,szFile
,-1,lpszFile
,MAX_PATH
,0,0);
368 /*************************************************************************
371 * Unicode version of PathFindOnPathExA.
373 BOOL WINAPI
PathFindOnPathExW(LPWSTR lpszFile
,LPCWSTR
*lppszOtherDirs
,DWORD dwWhich
)
375 WCHAR buff
[MAX_PATH
];
377 TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile
), lppszOtherDirs
, dwWhich
);
379 if (!lpszFile
|| !PathIsFileSpecW(lpszFile
))
382 /* Search provided directories first */
383 if (lppszOtherDirs
&& *lppszOtherDirs
)
385 LPCWSTR
*lpszOtherPath
= lppszOtherDirs
;
386 while (lpszOtherPath
&& *lpszOtherPath
&& (*lpszOtherPath
)[0])
388 PathCombineW(buff
, *lpszOtherPath
, lpszFile
);
389 if (PathFileExistsDefExtW(buff
, dwWhich
))
391 lstrcpyW(lpszFile
, buff
);
397 /* Not found, try system and path dirs */
398 return SHLWAPI_PathFindInOtherDirs(lpszFile
, dwWhich
);
401 /*************************************************************************
402 * PathFindOnPathA [SHLWAPI.@]
404 * Search a range of paths for an executable.
407 * lpszFile [I/O] File to search for
408 * lppszOtherDirs [I] Other directories to look in
411 * Success: TRUE. The path to the executable is stored in lpszFile.
412 * Failure: FALSE. The path to the executable is unchanged.
414 BOOL WINAPI
PathFindOnPathA(LPSTR lpszFile
, LPCSTR
*lppszOtherDirs
)
416 TRACE("(%s,%p)\n", debugstr_a(lpszFile
), lppszOtherDirs
);
417 return PathFindOnPathExA(lpszFile
, lppszOtherDirs
, 0);
420 /*************************************************************************
421 * PathFindOnPathW [SHLWAPI.@]
423 * See PathFindOnPathA.
425 BOOL WINAPI
PathFindOnPathW(LPWSTR lpszFile
, LPCWSTR
*lppszOtherDirs
)
427 TRACE("(%s,%p)\n", debugstr_w(lpszFile
), lppszOtherDirs
);
428 return PathFindOnPathExW(lpszFile
,lppszOtherDirs
, 0);
431 /*************************************************************************
432 * PathCompactPathExA [SHLWAPI.@]
434 * Compact a path into a given number of characters.
437 * lpszDest [O] Destination for compacted path
438 * lpszPath [I] Source path
439 * cchMax [I] Maximum size of compacted path
440 * dwFlags [I] Reserved
443 * Success: TRUE. The compacted path is written to lpszDest.
444 * Failure: FALSE. lpszPath is undefined.
447 * If cchMax is given as 0, lpszDest will still be NUL terminated.
449 * The Win32 version of this function contains a bug: When cchMax == 7,
450 * 8 bytes will be written to lpszDest. This bug is fixed in the Wine
453 * Some relative paths will be different when cchMax == 5 or 6. This occurs
454 * because Win32 will insert a "\" in lpszDest, even if one is
455 * not present in the original path.
457 BOOL WINAPI
PathCompactPathExA(LPSTR lpszDest
, LPCSTR lpszPath
,
458 UINT cchMax
, DWORD dwFlags
)
462 TRACE("(%p,%s,%d,0x%08x)\n", lpszDest
, debugstr_a(lpszPath
), cchMax
, dwFlags
);
464 if (lpszPath
&& lpszDest
)
466 WCHAR szPath
[MAX_PATH
];
467 WCHAR szDest
[MAX_PATH
];
469 MultiByteToWideChar(CP_ACP
,0,lpszPath
,-1,szPath
,MAX_PATH
);
471 bRet
= PathCompactPathExW(szDest
, szPath
, cchMax
, dwFlags
);
472 WideCharToMultiByte(CP_ACP
,0,szDest
,-1,lpszDest
,MAX_PATH
,0,0);
477 /*************************************************************************
478 * PathCompactPathExW [SHLWAPI.@]
480 * See PathCompactPathExA.
482 BOOL WINAPI
PathCompactPathExW(LPWSTR lpszDest
, LPCWSTR lpszPath
,
483 UINT cchMax
, DWORD dwFlags
)
485 static const WCHAR szEllipses
[] = { '.', '.', '.', '\0' };
487 DWORD dwLen
, dwFileLen
= 0;
489 TRACE("(%p,%s,%d,0x%08x)\n", lpszDest
, debugstr_w(lpszPath
), cchMax
, dwFlags
);
496 WARN("Invalid lpszDest would crash under Win32!\n");
505 dwLen
= lstrlenW(lpszPath
) + 1;
509 /* Don't need to compact */
510 memcpy(lpszDest
, lpszPath
, dwLen
* sizeof(WCHAR
));
514 /* Path must be compacted to fit into lpszDest */
515 lpszFile
= PathFindFileNameW(lpszPath
);
516 dwFileLen
= lpszPath
+ dwLen
- lpszFile
;
518 if (dwFileLen
== dwLen
)
520 /* No root in psth */
523 while (--cchMax
> 0) /* No room left for anything but ellipses */
528 /* Compact the file name with ellipses at the end */
530 memcpy(lpszDest
, lpszFile
, cchMax
* sizeof(WCHAR
));
531 lstrcpyW(lpszDest
+ cchMax
, szEllipses
);
534 /* We have a root in the path */
535 lpszFile
--; /* Start compacted filename with the path separator */
538 if (dwFileLen
+ 3 > cchMax
)
540 /* Compact the file name */
543 while (--cchMax
> 0) /* No room left for anything but ellipses */
548 lstrcpyW(lpszDest
, szEllipses
);
551 *lpszDest
++ = *lpszFile
++;
554 while (--cchMax
> 0) /* No room left for anything but ellipses */
560 memcpy(lpszDest
, lpszFile
, cchMax
* sizeof(WCHAR
));
561 lstrcpyW(lpszDest
+ cchMax
, szEllipses
);
565 /* Only the root needs to be Compacted */
566 dwLen
= cchMax
- dwFileLen
- 3;
567 memcpy(lpszDest
, lpszPath
, dwLen
* sizeof(WCHAR
));
568 lstrcpyW(lpszDest
+ dwLen
, szEllipses
);
569 lstrcpyW(lpszDest
+ dwLen
+ 3, lpszFile
);
573 /*************************************************************************
574 * PathIsDirectoryA [SHLWAPI.@]
576 * Determine if a path is a valid directory
579 * lpszPath [I] Path to check.
582 * FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
583 * FALSE if lpszPath is invalid or not a directory.
586 * Although this function is prototyped as returning a BOOL, it returns
587 * FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
589 *| if (PathIsDirectoryA("c:\\windows\\") == TRUE)
594 BOOL WINAPI
PathIsDirectoryA(LPCSTR lpszPath
)
598 TRACE("(%s)\n", debugstr_a(lpszPath
));
600 if (!lpszPath
|| PathIsUNCServerA(lpszPath
))
603 if (PathIsUNCServerShareA(lpszPath
))
605 FIXME("UNC Server Share not yet supported - FAILING\n");
609 if ((dwAttr
= GetFileAttributesA(lpszPath
)) == INVALID_FILE_ATTRIBUTES
)
611 return dwAttr
& FILE_ATTRIBUTE_DIRECTORY
;
614 /*************************************************************************
615 * PathIsDirectoryW [SHLWAPI.@]
617 * See PathIsDirectoryA.
619 BOOL WINAPI
PathIsDirectoryW(LPCWSTR lpszPath
)
623 TRACE("(%s)\n", debugstr_w(lpszPath
));
625 if (!lpszPath
|| PathIsUNCServerW(lpszPath
))
628 if (PathIsUNCServerShareW(lpszPath
))
630 FIXME("UNC Server Share not yet supported - FAILING\n");
634 if ((dwAttr
= GetFileAttributesW(lpszPath
)) == INVALID_FILE_ATTRIBUTES
)
636 return dwAttr
& FILE_ATTRIBUTE_DIRECTORY
;
639 /*************************************************************************
640 * PathFileExistsAndAttributesA [SHLWAPI.445]
642 * Determine if a file exists.
645 * lpszPath [I] Path to check
646 * dwAttr [O] attributes of file
649 * TRUE If the file exists and is readable
652 BOOL WINAPI
PathFileExistsAndAttributesA(LPCSTR lpszPath
, DWORD
*dwAttr
)
657 TRACE("(%s %p)\n", debugstr_a(lpszPath
), dwAttr
);
660 *dwAttr
= INVALID_FILE_ATTRIBUTES
;
665 iPrevErrMode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
666 dwVal
= GetFileAttributesA(lpszPath
);
667 SetErrorMode(iPrevErrMode
);
670 return (dwVal
!= INVALID_FILE_ATTRIBUTES
);
673 /*************************************************************************
674 * PathFileExistsAndAttributesW [SHLWAPI.446]
676 * See PathFileExistsA.
678 BOOL WINAPI
PathFileExistsAndAttributesW(LPCWSTR lpszPath
, DWORD
*dwAttr
)
683 TRACE("(%s %p)\n", debugstr_w(lpszPath
), dwAttr
);
688 iPrevErrMode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
689 dwVal
= GetFileAttributesW(lpszPath
);
690 SetErrorMode(iPrevErrMode
);
693 return (dwVal
!= INVALID_FILE_ATTRIBUTES
);
696 /*************************************************************************
697 * PathIsContentTypeA [SHLWAPI.@]
699 * Determine if a file is of a given registered content type.
702 * lpszPath [I] File to check
703 * lpszContentType [I] Content type to check for
706 * TRUE If lpszPath is a given registered content type,
710 * This function looks up the registered content type for lpszPath. If
711 * a content type is registered, it is compared (case insensitively) to
712 * lpszContentType. Only if this matches does the function succeed.
714 BOOL WINAPI
PathIsContentTypeA(LPCSTR path
, LPCSTR content_type
)
717 DWORD size
= sizeof(buf
);
720 TRACE("(%s,%s)\n", debugstr_a(path
), debugstr_a(content_type
));
722 if(!path
) return FALSE
;
723 if(!(ext
= PathFindExtensionA(path
)) || !*ext
) return FALSE
;
724 if(SHGetValueA(HKEY_CLASSES_ROOT
, ext
, "Content Type", NULL
, buf
, &size
)) return FALSE
;
725 return !lstrcmpiA(content_type
, buf
);
728 /*************************************************************************
729 * PathIsContentTypeW [SHLWAPI.@]
731 * See PathIsContentTypeA.
733 BOOL WINAPI
PathIsContentTypeW(LPCWSTR lpszPath
, LPCWSTR lpszContentType
)
735 static const WCHAR szContentType
[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
738 WCHAR szBuff
[MAX_PATH
];
740 TRACE("(%s,%s)\n", debugstr_w(lpszPath
), debugstr_w(lpszContentType
));
742 if (lpszPath
&& (szExt
= PathFindExtensionW(lpszPath
)) && *szExt
&&
743 !SHGetValueW(HKEY_CLASSES_ROOT
, szExt
, szContentType
,
744 REG_NONE
, szBuff
, &dwDummy
) &&
745 !wcsicmp(lpszContentType
, szBuff
))
752 /*************************************************************************
753 * PathIsSystemFolderA [SHLWAPI.@]
755 * Determine if a path or file attributes are a system folder.
758 * lpszPath [I] Path to check.
759 * dwAttrib [I] Attributes to check, if lpszPath is NULL.
762 * TRUE If lpszPath or dwAttrib are a system folder.
763 * FALSE If GetFileAttributesA() fails or neither parameter is a system folder.
765 BOOL WINAPI
PathIsSystemFolderA(LPCSTR lpszPath
, DWORD dwAttrib
)
767 TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath
), dwAttrib
);
769 if (lpszPath
&& *lpszPath
)
770 dwAttrib
= GetFileAttributesA(lpszPath
);
772 if (dwAttrib
== INVALID_FILE_ATTRIBUTES
|| !(dwAttrib
& FILE_ATTRIBUTE_DIRECTORY
) ||
773 !(dwAttrib
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_READONLY
)))
778 /*************************************************************************
779 * PathIsSystemFolderW [SHLWAPI.@]
781 * See PathIsSystemFolderA.
783 BOOL WINAPI
PathIsSystemFolderW(LPCWSTR lpszPath
, DWORD dwAttrib
)
785 TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath
), dwAttrib
);
787 if (lpszPath
&& *lpszPath
)
788 dwAttrib
= GetFileAttributesW(lpszPath
);
790 if (dwAttrib
== INVALID_FILE_ATTRIBUTES
|| !(dwAttrib
& FILE_ATTRIBUTE_DIRECTORY
) ||
791 !(dwAttrib
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_READONLY
)))
796 /*************************************************************************
797 * PathMakePrettyA [SHLWAPI.@]
799 * Convert an uppercase DOS filename into lowercase.
802 * lpszPath [I/O] Path to convert.
805 * TRUE If the path was an uppercase DOS path and was converted,
808 BOOL WINAPI
PathMakePrettyA(LPSTR lpszPath
)
810 LPSTR pszIter
= lpszPath
;
812 TRACE("(%s)\n", debugstr_a(lpszPath
));
821 if (islower(*pszIter
) || IsDBCSLeadByte(*pszIter
))
822 return FALSE
; /* Not DOS path */
825 pszIter
= lpszPath
+ 1;
828 *pszIter
= tolower(*pszIter
);
835 /*************************************************************************
836 * PathMakePrettyW [SHLWAPI.@]
838 * See PathMakePrettyA.
840 BOOL WINAPI
PathMakePrettyW(LPWSTR lpszPath
)
842 LPWSTR pszIter
= lpszPath
;
844 TRACE("(%s)\n", debugstr_w(lpszPath
));
853 if (iswlower(*pszIter
))
854 return FALSE
; /* Not DOS path */
857 pszIter
= lpszPath
+ 1;
860 *pszIter
= towlower(*pszIter
);
867 /*************************************************************************
868 * PathCompactPathA [SHLWAPI.@]
870 * Make a path fit into a given width when printed to a DC.
873 * hDc [I] Destination DC
874 * lpszPath [I/O] Path to be printed to hDc
875 * dx [I] Desired width
878 * TRUE If the path was modified/went well.
881 BOOL WINAPI
PathCompactPathA(HDC hDC
, LPSTR lpszPath
, UINT dx
)
885 TRACE("(%p,%s,%d)\n", hDC
, debugstr_a(lpszPath
), dx
);
889 WCHAR szPath
[MAX_PATH
];
890 MultiByteToWideChar(CP_ACP
,0,lpszPath
,-1,szPath
,MAX_PATH
);
891 bRet
= PathCompactPathW(hDC
, szPath
, dx
);
892 WideCharToMultiByte(CP_ACP
,0,szPath
,-1,lpszPath
,MAX_PATH
,0,0);
897 /*************************************************************************
898 * PathCompactPathW [SHLWAPI.@]
900 * See PathCompactPathA.
902 BOOL WINAPI
PathCompactPathW(HDC hDC
, LPWSTR lpszPath
, UINT dx
)
904 static const WCHAR szEllipses
[] = { '.', '.', '.', '\0' };
907 WCHAR buff
[MAX_PATH
];
911 TRACE("(%p,%s,%d)\n", hDC
, debugstr_w(lpszPath
), dx
);
917 hdc
= hDC
= GetDC(0);
919 /* Get the length of the whole path */
920 dwLen
= lstrlenW(lpszPath
);
921 GetTextExtentPointW(hDC
, lpszPath
, dwLen
, &size
);
923 if ((UINT
)size
.cx
> dx
)
925 /* Path too big, must reduce it */
927 DWORD dwEllipsesLen
= 0, dwPathLen
= 0;
929 sFile
= PathFindFileNameW(lpszPath
);
930 if (sFile
!= lpszPath
) sFile
--;
932 /* Get the size of ellipses */
933 GetTextExtentPointW(hDC
, szEllipses
, 3, &size
);
934 dwEllipsesLen
= size
.cx
;
935 /* Get the size of the file name */
936 GetTextExtentPointW(hDC
, sFile
, lstrlenW(sFile
), &size
);
939 if (sFile
!= lpszPath
)
941 LPWSTR sPath
= sFile
;
942 BOOL bEllipses
= FALSE
;
944 /* The path includes a file name. Include as much of the path prior to
945 * the file name as possible, allowing for the ellipses, e.g:
946 * c:\some very long path\filename ==> c:\some v...\filename
948 lstrcpynW(buff
, sFile
, MAX_PATH
);
952 DWORD dwTotalLen
= bEllipses
? dwPathLen
+ dwEllipsesLen
: dwPathLen
;
954 GetTextExtentPointW(hDC
, lpszPath
, sPath
- lpszPath
, &size
);
955 dwTotalLen
+= size
.cx
;
956 if (dwTotalLen
<= dx
)
964 } while (sPath
> lpszPath
);
966 if (sPath
> lpszPath
)
970 lstrcpyW(sPath
, szEllipses
);
971 lstrcpyW(sPath
+3, buff
);
976 lstrcpyW(lpszPath
, szEllipses
);
977 lstrcpyW(lpszPath
+3, buff
);
982 /* Trim the path by adding ellipses to the end, e.g:
983 * A very long file name.txt ==> A very...
985 dwLen
= lstrlenW(lpszPath
);
987 if (dwLen
> MAX_PATH
- 3)
988 dwLen
= MAX_PATH
- 3;
989 lstrcpynW(buff
, sFile
, dwLen
);
993 GetTextExtentPointW(hDC
, buff
, dwLen
, &size
);
994 } while (dwLen
&& size
.cx
+ dwEllipsesLen
> dx
);
1000 dwEllipsesLen
/= 3; /* Size of a single '.' */
1002 /* Write as much of the Ellipses string as possible */
1003 while (dwWritten
+ dwEllipsesLen
< dx
&& dwLen
< 3)
1006 dwWritten
+= dwEllipsesLen
;
1014 lstrcpyW(buff
+ dwLen
, szEllipses
);
1015 lstrcpyW(lpszPath
, buff
);
1026 /*************************************************************************
1027 * SHLWAPI_UseSystemForSystemFolders
1029 * Internal helper for PathMakeSystemFolderW.
1031 static BOOL
SHLWAPI_UseSystemForSystemFolders(void)
1033 static BOOL bCheckedReg
= FALSE
;
1034 static BOOL bUseSystemForSystemFolders
= FALSE
;
1040 /* Key tells Win what file attributes to use on system folders */
1041 if (SHGetValueA(HKEY_LOCAL_MACHINE
,
1042 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
1043 "UseSystemForSystemFolders", 0, 0, 0))
1044 bUseSystemForSystemFolders
= TRUE
;
1046 return bUseSystemForSystemFolders
;
1049 /*************************************************************************
1050 * PathMakeSystemFolderA [SHLWAPI.@]
1052 * Set system folder attribute for a path.
1055 * lpszPath [I] The path to turn into a system folder
1058 * TRUE If the path was changed to/already was a system folder
1059 * FALSE If the path is invalid or SetFileAttributesA() fails
1061 BOOL WINAPI
PathMakeSystemFolderA(LPCSTR lpszPath
)
1065 TRACE("(%s)\n", debugstr_a(lpszPath
));
1067 if (lpszPath
&& *lpszPath
)
1069 WCHAR szPath
[MAX_PATH
];
1070 MultiByteToWideChar(CP_ACP
,0,lpszPath
,-1,szPath
,MAX_PATH
);
1071 bRet
= PathMakeSystemFolderW(szPath
);
1076 /*************************************************************************
1077 * PathMakeSystemFolderW [SHLWAPI.@]
1079 * See PathMakeSystemFolderA.
1081 BOOL WINAPI
PathMakeSystemFolderW(LPCWSTR lpszPath
)
1083 DWORD dwDefaultAttr
= FILE_ATTRIBUTE_READONLY
, dwAttr
;
1084 WCHAR buff
[MAX_PATH
];
1086 TRACE("(%s)\n", debugstr_w(lpszPath
));
1088 if (!lpszPath
|| !*lpszPath
)
1091 /* If the directory is already a system directory, don't do anything */
1092 GetSystemDirectoryW(buff
, MAX_PATH
);
1093 if (!wcscmp(buff
, lpszPath
))
1096 GetWindowsDirectoryW(buff
, MAX_PATH
);
1097 if (!wcscmp(buff
, lpszPath
))
1100 /* "UseSystemForSystemFolders" Tells Win what attributes to use */
1101 if (SHLWAPI_UseSystemForSystemFolders())
1102 dwDefaultAttr
= FILE_ATTRIBUTE_SYSTEM
;
1104 if ((dwAttr
= GetFileAttributesW(lpszPath
)) == INVALID_FILE_ATTRIBUTES
)
1107 /* Change file attributes to system attributes */
1108 dwAttr
&= ~(FILE_ATTRIBUTE_SYSTEM
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_READONLY
);
1109 return SetFileAttributesW(lpszPath
, dwAttr
| dwDefaultAttr
);
1112 /*************************************************************************
1113 * PathUnmakeSystemFolderA [SHLWAPI.@]
1115 * Remove the system folder attributes from a path.
1118 * lpszPath [I] The path to remove attributes from
1122 * Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling
1123 * SetFileAttributesA() fails.
1125 BOOL WINAPI
PathUnmakeSystemFolderA(LPCSTR lpszPath
)
1129 TRACE("(%s)\n", debugstr_a(lpszPath
));
1131 if (!lpszPath
|| !*lpszPath
|| (dwAttr
= GetFileAttributesA(lpszPath
)) == INVALID_FILE_ATTRIBUTES
||
1132 !(dwAttr
& FILE_ATTRIBUTE_DIRECTORY
))
1135 dwAttr
&= ~(FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
);
1136 return SetFileAttributesA(lpszPath
, dwAttr
);
1139 /*************************************************************************
1140 * PathUnmakeSystemFolderW [SHLWAPI.@]
1142 * See PathUnmakeSystemFolderA.
1144 BOOL WINAPI
PathUnmakeSystemFolderW(LPCWSTR lpszPath
)
1148 TRACE("(%s)\n", debugstr_w(lpszPath
));
1150 if (!lpszPath
|| !*lpszPath
|| (dwAttr
= GetFileAttributesW(lpszPath
)) == INVALID_FILE_ATTRIBUTES
||
1151 !(dwAttr
& FILE_ATTRIBUTE_DIRECTORY
))
1154 dwAttr
&= ~(FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
);
1155 return SetFileAttributesW(lpszPath
, dwAttr
);
1159 /*************************************************************************
1160 * PathSetDlgItemPathA [SHLWAPI.@]
1162 * Set the text of a dialog item to a path, shrinking the path to fit
1163 * if it is too big for the item.
1166 * hDlg [I] Dialog handle
1167 * id [I] ID of item in the dialog
1168 * lpszPath [I] Path to set as the items text
1174 * If lpszPath is NULL, a blank string ("") is set (i.e. The previous
1175 * window text is erased).
1177 VOID WINAPI
PathSetDlgItemPathA(HWND hDlg
, int id
, LPCSTR lpszPath
)
1179 WCHAR szPath
[MAX_PATH
];
1181 TRACE("(%p,%8x,%s)\n",hDlg
, id
, debugstr_a(lpszPath
));
1184 MultiByteToWideChar(CP_ACP
,0,lpszPath
,-1,szPath
,MAX_PATH
);
1187 PathSetDlgItemPathW(hDlg
, id
, szPath
);
1190 /*************************************************************************
1191 * PathSetDlgItemPathW [SHLWAPI.@]
1193 * See PathSetDlgItemPathA.
1195 VOID WINAPI
PathSetDlgItemPathW(HWND hDlg
, int id
, LPCWSTR lpszPath
)
1197 WCHAR path
[MAX_PATH
+ 1];
1203 TRACE("(%p,%8x,%s)\n",hDlg
, id
, debugstr_w(lpszPath
));
1205 if (!(hwItem
= GetDlgItem(hDlg
, id
)))
1209 lstrcpynW(path
, lpszPath
, ARRAY_SIZE(path
));
1213 GetClientRect(hwItem
, &rect
);
1215 hPrevObj
= SelectObject(hdc
, (HGDIOBJ
)SendMessageW(hwItem
,WM_GETFONT
,0,0));
1219 PathCompactPathW(hdc
, path
, rect
.right
);
1220 SelectObject(hdc
, hPrevObj
);
1223 ReleaseDC(hDlg
, hdc
);
1224 SetWindowTextW(hwItem
, path
);
1227 /*************************************************************************
1228 * PathIsNetworkPathA [SHLWAPI.@]
1230 * Determine if the given path is a network path.
1233 * lpszPath [I] Path to check
1236 * TRUE If lpszPath is a UNC share or mapped network drive, or
1237 * FALSE If lpszPath is a local drive or cannot be determined
1239 BOOL WINAPI
PathIsNetworkPathA(LPCSTR lpszPath
)
1243 TRACE("(%s)\n",debugstr_a(lpszPath
));
1247 if (*lpszPath
== '\\' && lpszPath
[1] == '\\')
1249 dwDriveNum
= PathGetDriveNumberA(lpszPath
);
1250 if (dwDriveNum
== -1)
1252 GET_FUNC(pIsNetDrive
, shell32
, (LPCSTR
)66, FALSE
); /* ord 66 = shell32.IsNetDrive */
1253 return pIsNetDrive(dwDriveNum
);
1256 /*************************************************************************
1257 * PathIsNetworkPathW [SHLWAPI.@]
1259 * See PathIsNetworkPathA.
1261 BOOL WINAPI
PathIsNetworkPathW(LPCWSTR lpszPath
)
1265 TRACE("(%s)\n", debugstr_w(lpszPath
));
1269 if (*lpszPath
== '\\' && lpszPath
[1] == '\\')
1271 dwDriveNum
= PathGetDriveNumberW(lpszPath
);
1272 if (dwDriveNum
== -1)
1274 GET_FUNC(pIsNetDrive
, shell32
, (LPCSTR
)66, FALSE
); /* ord 66 = shell32.IsNetDrive */
1275 return pIsNetDrive(dwDriveNum
);
1278 /*************************************************************************
1279 * PathIsDirectoryEmptyA [SHLWAPI.@]
1281 * Determine if a given directory is empty.
1284 * lpszPath [I] Directory to check
1287 * TRUE If the directory exists and contains no files,
1290 BOOL WINAPI
PathIsDirectoryEmptyA(LPCSTR lpszPath
)
1294 TRACE("(%s)\n",debugstr_a(lpszPath
));
1298 WCHAR szPath
[MAX_PATH
];
1299 MultiByteToWideChar(CP_ACP
,0,lpszPath
,-1,szPath
,MAX_PATH
);
1300 bRet
= PathIsDirectoryEmptyW(szPath
);
1305 /*************************************************************************
1306 * PathIsDirectoryEmptyW [SHLWAPI.@]
1308 * See PathIsDirectoryEmptyA.
1310 BOOL WINAPI
PathIsDirectoryEmptyW(LPCWSTR lpszPath
)
1312 static const WCHAR szAllFiles
[] = { '*', '.', '*', '\0' };
1313 WCHAR szSearch
[MAX_PATH
];
1317 WIN32_FIND_DATAW find_data
;
1319 TRACE("(%s)\n",debugstr_w(lpszPath
));
1321 if (!lpszPath
|| !PathIsDirectoryW(lpszPath
))
1324 lstrcpynW(szSearch
, lpszPath
, MAX_PATH
);
1325 PathAddBackslashW(szSearch
);
1326 dwLen
= lstrlenW(szSearch
);
1327 if (dwLen
> MAX_PATH
- 4)
1330 lstrcpyW(szSearch
+ dwLen
, szAllFiles
);
1331 hfind
= FindFirstFileW(szSearch
, &find_data
);
1332 if (hfind
== INVALID_HANDLE_VALUE
)
1337 if (find_data
.cFileName
[0] == '.')
1339 if (find_data
.cFileName
[1] == '\0') continue;
1340 if (find_data
.cFileName
[1] == '.' && find_data
.cFileName
[2] == '\0') continue;
1346 while (FindNextFileW(hfind
, &find_data
));
1353 /*************************************************************************
1354 * PathFindSuffixArrayA [SHLWAPI.@]
1356 * Find a suffix string in an array of suffix strings
1359 * lpszSuffix [I] Suffix string to search for
1360 * lppszArray [I] Array of suffix strings to search
1361 * dwCount [I] Number of elements in lppszArray
1364 * Success: The index of the position of lpszSuffix in lppszArray
1365 * Failure: 0, if any parameters are invalid or lpszSuffix is not found
1368 * The search is case sensitive.
1369 * The match is made against the end of the suffix string, so for example:
1370 * lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not.
1372 LPCSTR WINAPI
PathFindSuffixArrayA(LPCSTR lpszSuffix
, LPCSTR
*lppszArray
, int dwCount
)
1377 TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix
), lppszArray
, dwCount
);
1379 if (lpszSuffix
&& lppszArray
&& dwCount
> 0)
1381 dwLen
= strlen(lpszSuffix
);
1383 while (dwRet
< dwCount
)
1385 size_t dwCompareLen
= strlen(*lppszArray
);
1386 if (dwCompareLen
< dwLen
)
1388 if (!strcmp(lpszSuffix
+ dwLen
- dwCompareLen
, *lppszArray
))
1389 return *lppszArray
; /* Found */
1398 /*************************************************************************
1399 * PathFindSuffixArrayW [SHLWAPI.@]
1401 * See PathFindSuffixArrayA.
1403 LPCWSTR WINAPI
PathFindSuffixArrayW(LPCWSTR lpszSuffix
, LPCWSTR
*lppszArray
, int dwCount
)
1408 TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix
), lppszArray
, dwCount
);
1410 if (lpszSuffix
&& lppszArray
&& dwCount
> 0)
1412 dwLen
= lstrlenW(lpszSuffix
);
1414 while (dwRet
< dwCount
)
1416 size_t dwCompareLen
= lstrlenW(*lppszArray
);
1417 if (dwCompareLen
< dwLen
)
1419 if (!wcscmp(lpszSuffix
+ dwLen
- dwCompareLen
, *lppszArray
))
1420 return *lppszArray
; /* Found */
1429 /*************************************************************************
1430 * PathUndecorateA [SHLWAPI.@]
1432 * Undecorate a file path
1435 * lpszPath [I/O] Path to remove any decoration from
1441 * A decorations form is "path[n].ext" where "n" is an optional decimal number.
1443 void WINAPI
PathUndecorateA(char *path
)
1447 TRACE("(%s)\n", debugstr_a(path
));
1451 ext
= PathFindExtensionA(path
);
1452 if (ext
== path
|| ext
[-1] != ']') return;
1455 while (skip
> path
&& '0' <= *skip
&& *skip
<= '9')
1458 if (skip
> path
&& *skip
== '[' && skip
[-1] != '\\')
1459 memmove(skip
, ext
, strlen(ext
) + 1);
1462 /*************************************************************************
1463 * PathUndecorateW [SHLWAPI.@]
1465 * See PathUndecorateA.
1467 void WINAPI
PathUndecorateW(WCHAR
*path
)
1471 TRACE("(%s)\n", debugstr_w(path
));
1475 ext
= PathFindExtensionW(path
);
1476 if (ext
== path
|| ext
[-1] != ']') return;
1479 while (skip
> path
&& '0' <= *skip
&& *skip
<= '9')
1482 if (skip
> path
&& *skip
== '[' && skip
[-1] != '\\')
1483 memmove(skip
, ext
, (wcslen(ext
) + 1) * sizeof(WCHAR
));
1486 /*************************************************************************
1489 * Find localised or default web content in "%WINDOWS%\web\".
1492 * lpszFile [I] File name containing content to look for
1493 * lpszPath [O] Buffer to contain the full path to the file
1494 * dwPathLen [I] Length of lpszPath
1497 * Success: S_OK. lpszPath contains the full path to the content.
1498 * Failure: E_FAIL. The content does not exist or lpszPath is too short.
1500 HRESULT WINAPI
SHGetWebFolderFilePathA(LPCSTR lpszFile
, LPSTR lpszPath
, DWORD dwPathLen
)
1502 WCHAR szFile
[MAX_PATH
], szPath
[MAX_PATH
];
1505 TRACE("(%s,%p,%d)\n", lpszFile
, lpszPath
, dwPathLen
);
1507 MultiByteToWideChar(CP_ACP
, 0, lpszFile
, -1, szFile
, MAX_PATH
);
1509 hRet
= SHGetWebFolderFilePathW(szFile
, szPath
, dwPathLen
);
1510 WideCharToMultiByte(CP_ACP
, 0, szPath
, -1, lpszPath
, dwPathLen
, 0, 0);
1514 /*************************************************************************
1517 * Unicode version of SHGetWebFolderFilePathA.
1519 HRESULT WINAPI
SHGetWebFolderFilePathW(LPCWSTR lpszFile
, LPWSTR lpszPath
, DWORD dwPathLen
)
1521 static const WCHAR szWeb
[] = {'\\','W','e','b','\\','\0'};
1522 static const WCHAR szWebMui
[] = {'m','u','i','\\','%','0','4','x','\\','\0'};
1523 DWORD dwLen
, dwFileLen
;
1524 LANGID lidSystem
, lidUser
;
1526 TRACE("(%s,%p,%d)\n", debugstr_w(lpszFile
), lpszPath
, dwPathLen
);
1528 /* Get base directory for web content */
1529 dwLen
= GetSystemWindowsDirectoryW(lpszPath
, dwPathLen
);
1530 if (dwLen
> 0 && lpszPath
[dwLen
-1] == '\\')
1533 dwFileLen
= lstrlenW(lpszFile
);
1535 if (dwLen
+ dwFileLen
+ ARRAY_SIZE(szWeb
) >= dwPathLen
)
1536 return E_FAIL
; /* lpszPath too short */
1538 lstrcpyW(lpszPath
+dwLen
, szWeb
);
1539 dwLen
+= ARRAY_SIZE(szWeb
);
1540 dwPathLen
= dwPathLen
- dwLen
; /* Remaining space */
1542 lidSystem
= GetSystemDefaultUILanguage();
1543 lidUser
= GetUserDefaultUILanguage();
1545 if (lidSystem
!= lidUser
)
1547 if (dwFileLen
+ ARRAY_SIZE(szWebMui
) < dwPathLen
)
1549 /* Use localised content in the users UI language if present */
1550 wsprintfW(lpszPath
+ dwLen
, szWebMui
, lidUser
);
1551 lstrcpyW(lpszPath
+ dwLen
+ ARRAY_SIZE(szWebMui
), lpszFile
);
1552 if (PathFileExistsW(lpszPath
))
1557 /* Fall back to OS default installed content */
1558 lstrcpyW(lpszPath
+ dwLen
, lpszFile
);
1559 if (PathFileExistsW(lpszPath
))