1594 lines
41 KiB
C
1594 lines
41 KiB
C
/*
|
|
* Path Functions
|
|
*
|
|
* Copyright 1999, 2000 Juergen Schmied
|
|
* Copyright 2001, 2002 Jon Griffiths
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
#include "winreg.h"
|
|
#include "winternl.h"
|
|
#define NO_SHLWAPI_STREAM
|
|
#include "shlwapi.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(shell);
|
|
|
|
/* Get a function pointer from a DLL handle */
|
|
#define GET_FUNC(func, module, name, fail) \
|
|
do { \
|
|
if (!func) { \
|
|
if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \
|
|
func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \
|
|
if (!func) return fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
/* DLL handles for late bound calls */
|
|
static HMODULE SHLWAPI_hshell32;
|
|
|
|
/* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */
|
|
typedef BOOL (WINAPI *fnpIsNetDrive)(int);
|
|
static fnpIsNetDrive pIsNetDrive;
|
|
|
|
HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD);
|
|
|
|
static inline WCHAR* heap_strdupAtoW(LPCSTR str)
|
|
{
|
|
WCHAR *ret = NULL;
|
|
|
|
if (str)
|
|
{
|
|
DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
|
ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
|
|
if (ret)
|
|
MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathBuildRootA [SHLWAPI.@]
|
|
*
|
|
* Create a root drive string (e.g. "A:\") from a drive number.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [O] Destination for the drive string
|
|
*
|
|
* RETURNS
|
|
* lpszPath
|
|
*
|
|
* NOTES
|
|
* If lpszPath is NULL or drive is invalid, nothing is written to lpszPath.
|
|
*/
|
|
LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive)
|
|
{
|
|
TRACE("(%p,%d)\n", lpszPath, drive);
|
|
|
|
if (lpszPath && drive >= 0 && drive < 26)
|
|
{
|
|
lpszPath[0] = 'A' + drive;
|
|
lpszPath[1] = ':';
|
|
lpszPath[2] = '\\';
|
|
lpszPath[3] = '\0';
|
|
}
|
|
return lpszPath;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathBuildRootW [SHLWAPI.@]
|
|
*
|
|
* See PathBuildRootA.
|
|
*/
|
|
LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive)
|
|
{
|
|
TRACE("(%p,%d)\n", lpszPath, drive);
|
|
|
|
if (lpszPath && drive >= 0 && drive < 26)
|
|
{
|
|
lpszPath[0] = 'A' + drive;
|
|
lpszPath[1] = ':';
|
|
lpszPath[2] = '\\';
|
|
lpszPath[3] = '\0';
|
|
}
|
|
return lpszPath;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathRemoveArgsA [SHLWAPI.@]
|
|
*
|
|
* Strip space separated arguments from a path.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I/O] Path to remove arguments from
|
|
*
|
|
* RETURNS
|
|
* Nothing.
|
|
*/
|
|
void WINAPI PathRemoveArgsA(LPSTR lpszPath)
|
|
{
|
|
TRACE("(%s)\n",debugstr_a(lpszPath));
|
|
|
|
if(lpszPath)
|
|
{
|
|
LPSTR lpszArgs = PathGetArgsA(lpszPath);
|
|
if (*lpszArgs)
|
|
lpszArgs[-1] = '\0';
|
|
else
|
|
{
|
|
LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs);
|
|
if(*lpszLastChar == ' ')
|
|
*lpszLastChar = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathRemoveArgsW [SHLWAPI.@]
|
|
*
|
|
* See PathRemoveArgsA.
|
|
*/
|
|
void WINAPI PathRemoveArgsW(LPWSTR lpszPath)
|
|
{
|
|
TRACE("(%s)\n",debugstr_w(lpszPath));
|
|
|
|
if(lpszPath)
|
|
{
|
|
LPWSTR lpszArgs = PathGetArgsW(lpszPath);
|
|
if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' '))
|
|
lpszArgs[-1] = '\0';
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.4]
|
|
*
|
|
* Unicode version of PathFileExistsDefExtA.
|
|
*/
|
|
BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich)
|
|
{
|
|
static const WCHAR pszExts[][5] = { { '.', 'p', 'i', 'f', 0},
|
|
{ '.', 'c', 'o', 'm', 0},
|
|
{ '.', 'e', 'x', 'e', 0},
|
|
{ '.', 'b', 'a', 't', 0},
|
|
{ '.', 'l', 'n', 'k', 0},
|
|
{ '.', 'c', 'm', 'd', 0},
|
|
{ 0, 0, 0, 0, 0} };
|
|
|
|
TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich);
|
|
|
|
if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath))
|
|
return FALSE;
|
|
|
|
if (dwWhich)
|
|
{
|
|
LPCWSTR szExt = PathFindExtensionW(lpszPath);
|
|
if (!*szExt || dwWhich & 0x40)
|
|
{
|
|
size_t iChoose = 0;
|
|
int iLen = lstrlenW(lpszPath);
|
|
if (iLen > (MAX_PATH - 5))
|
|
return FALSE;
|
|
while ( (dwWhich & 0x1) && pszExts[iChoose][0] )
|
|
{
|
|
lstrcpyW(lpszPath + iLen, pszExts[iChoose]);
|
|
if (PathFileExistsW(lpszPath))
|
|
return TRUE;
|
|
iChoose++;
|
|
dwWhich >>= 1;
|
|
}
|
|
*(lpszPath + iLen) = (WCHAR)'\0';
|
|
return FALSE;
|
|
}
|
|
}
|
|
return PathFileExistsW(lpszPath);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.3]
|
|
*
|
|
* Determine if a file exists locally and is of an executable type.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I/O] File to search for
|
|
* dwWhich [I] Type of executable to search for
|
|
*
|
|
* RETURNS
|
|
* TRUE If the file was found. lpszPath contains the file name.
|
|
* FALSE Otherwise.
|
|
*
|
|
* NOTES
|
|
* lpszPath is modified in place and must be at least MAX_PATH in length.
|
|
* If the function returns FALSE, the path is modified to its original state.
|
|
* If the given path contains an extension or dwWhich is 0, executable
|
|
* extensions are not checked.
|
|
*
|
|
* Ordinals 3-6 are a classic case of MS exposing limited functionality to
|
|
* users (here through PathFindOnPathA()) and keeping advanced functionality for
|
|
* their own developers exclusive use. Monopoly, anyone?
|
|
*/
|
|
BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich);
|
|
|
|
if (lpszPath)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
|
|
bRet = PathFileExistsDefExtW(szPath, dwWhich);
|
|
if (bRet)
|
|
WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_PathFindInOtherDirs
|
|
*
|
|
* Internal helper for SHLWAPI_PathFindOnPathExA/W.
|
|
*/
|
|
static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich)
|
|
{
|
|
static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'};
|
|
static const WCHAR szPath[] = { 'P','A','T','H','\0'};
|
|
DWORD dwLenPATH;
|
|
LPCWSTR lpszCurr;
|
|
WCHAR *lpszPATH;
|
|
WCHAR buff[MAX_PATH];
|
|
|
|
TRACE("(%s,%08x)\n", debugstr_w(lpszFile), dwWhich);
|
|
|
|
/* Try system directories */
|
|
GetSystemDirectoryW(buff, MAX_PATH);
|
|
if (!PathAppendW(buff, lpszFile))
|
|
return FALSE;
|
|
if (PathFileExistsDefExtW(buff, dwWhich))
|
|
{
|
|
lstrcpyW(lpszFile, buff);
|
|
return TRUE;
|
|
}
|
|
GetWindowsDirectoryW(buff, MAX_PATH);
|
|
if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile))
|
|
return FALSE;
|
|
if (PathFileExistsDefExtW(buff, dwWhich))
|
|
{
|
|
lstrcpyW(lpszFile, buff);
|
|
return TRUE;
|
|
}
|
|
GetWindowsDirectoryW(buff, MAX_PATH);
|
|
if (!PathAppendW(buff, lpszFile))
|
|
return FALSE;
|
|
if (PathFileExistsDefExtW(buff, dwWhich))
|
|
{
|
|
lstrcpyW(lpszFile, buff);
|
|
return TRUE;
|
|
}
|
|
/* Try dirs listed in %PATH% */
|
|
dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH);
|
|
|
|
if (!dwLenPATH || !(lpszPATH = HeapAlloc(GetProcessHeap(), 0, (dwLenPATH + 1) * sizeof (WCHAR))))
|
|
return FALSE;
|
|
|
|
GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1);
|
|
lpszCurr = lpszPATH;
|
|
while (lpszCurr)
|
|
{
|
|
LPCWSTR lpszEnd = lpszCurr;
|
|
LPWSTR pBuff = buff;
|
|
|
|
while (*lpszEnd == ' ')
|
|
lpszEnd++;
|
|
while (*lpszEnd && *lpszEnd != ';')
|
|
*pBuff++ = *lpszEnd++;
|
|
*pBuff = '\0';
|
|
|
|
if (*lpszEnd)
|
|
lpszCurr = lpszEnd + 1;
|
|
else
|
|
lpszCurr = NULL; /* Last Path, terminate after this */
|
|
|
|
if (!PathAppendW(buff, lpszFile))
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, lpszPATH);
|
|
return FALSE;
|
|
}
|
|
if (PathFileExistsDefExtW(buff, dwWhich))
|
|
{
|
|
lstrcpyW(lpszFile, buff);
|
|
HeapFree(GetProcessHeap(), 0, lpszPATH);
|
|
return TRUE;
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, lpszPATH);
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.5]
|
|
*
|
|
* Search a range of paths for a specific type of executable.
|
|
*
|
|
* PARAMS
|
|
* lpszFile [I/O] File to search for
|
|
* lppszOtherDirs [I] Other directories to look in
|
|
* dwWhich [I] Type of executable to search for
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE. The path to the executable is stored in lpszFile.
|
|
* Failure: FALSE. The path to the executable is unchanged.
|
|
*/
|
|
BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich)
|
|
{
|
|
WCHAR szFile[MAX_PATH];
|
|
WCHAR buff[MAX_PATH];
|
|
|
|
TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich);
|
|
|
|
if (!lpszFile || !PathIsFileSpecA(lpszFile))
|
|
return FALSE;
|
|
|
|
MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH);
|
|
|
|
/* Search provided directories first */
|
|
if (lppszOtherDirs && *lppszOtherDirs)
|
|
{
|
|
WCHAR szOther[MAX_PATH];
|
|
LPCSTR *lpszOtherPath = lppszOtherDirs;
|
|
|
|
while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
|
|
{
|
|
MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH);
|
|
PathCombineW(buff, szOther, szFile);
|
|
if (PathFileExistsDefExtW(buff, dwWhich))
|
|
{
|
|
WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0);
|
|
return TRUE;
|
|
}
|
|
lpszOtherPath++;
|
|
}
|
|
}
|
|
/* Not found, try system and path dirs */
|
|
if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich))
|
|
{
|
|
WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.6]
|
|
*
|
|
* Unicode version of PathFindOnPathExA.
|
|
*/
|
|
BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich)
|
|
{
|
|
WCHAR buff[MAX_PATH];
|
|
|
|
TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich);
|
|
|
|
if (!lpszFile || !PathIsFileSpecW(lpszFile))
|
|
return FALSE;
|
|
|
|
/* Search provided directories first */
|
|
if (lppszOtherDirs && *lppszOtherDirs)
|
|
{
|
|
LPCWSTR *lpszOtherPath = lppszOtherDirs;
|
|
while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0])
|
|
{
|
|
PathCombineW(buff, *lpszOtherPath, lpszFile);
|
|
if (PathFileExistsDefExtW(buff, dwWhich))
|
|
{
|
|
lstrcpyW(lpszFile, buff);
|
|
return TRUE;
|
|
}
|
|
lpszOtherPath++;
|
|
}
|
|
}
|
|
/* Not found, try system and path dirs */
|
|
return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathFindOnPathA [SHLWAPI.@]
|
|
*
|
|
* Search a range of paths for an executable.
|
|
*
|
|
* PARAMS
|
|
* lpszFile [I/O] File to search for
|
|
* lppszOtherDirs [I] Other directories to look in
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE. The path to the executable is stored in lpszFile.
|
|
* Failure: FALSE. The path to the executable is unchanged.
|
|
*/
|
|
BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs)
|
|
{
|
|
TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs);
|
|
return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathFindOnPathW [SHLWAPI.@]
|
|
*
|
|
* See PathFindOnPathA.
|
|
*/
|
|
BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs)
|
|
{
|
|
TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs);
|
|
return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathCompactPathExA [SHLWAPI.@]
|
|
*
|
|
* Compact a path into a given number of characters.
|
|
*
|
|
* PARAMS
|
|
* lpszDest [O] Destination for compacted path
|
|
* lpszPath [I] Source path
|
|
* cchMax [I] Maximum size of compacted path
|
|
* dwFlags [I] Reserved
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE. The compacted path is written to lpszDest.
|
|
* Failure: FALSE. lpszPath is undefined.
|
|
*
|
|
* NOTES
|
|
* If cchMax is given as 0, lpszDest will still be NUL terminated.
|
|
*
|
|
* The Win32 version of this function contains a bug: When cchMax == 7,
|
|
* 8 bytes will be written to lpszDest. This bug is fixed in the Wine
|
|
* implementation.
|
|
*
|
|
* Some relative paths will be different when cchMax == 5 or 6. This occurs
|
|
* because Win32 will insert a "\" in lpszDest, even if one is
|
|
* not present in the original path.
|
|
*/
|
|
BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath,
|
|
UINT cchMax, DWORD dwFlags)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags);
|
|
|
|
if (lpszPath && lpszDest)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
WCHAR szDest[MAX_PATH];
|
|
|
|
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
|
|
szDest[0] = '\0';
|
|
bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags);
|
|
WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathCompactPathExW [SHLWAPI.@]
|
|
*
|
|
* See PathCompactPathExA.
|
|
*/
|
|
BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath,
|
|
UINT cchMax, DWORD dwFlags)
|
|
{
|
|
static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
|
|
LPCWSTR lpszFile;
|
|
DWORD dwLen, dwFileLen = 0;
|
|
|
|
TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags);
|
|
|
|
if (!lpszPath)
|
|
return FALSE;
|
|
|
|
if (!lpszDest)
|
|
{
|
|
WARN("Invalid lpszDest would crash under Win32!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
*lpszDest = '\0';
|
|
|
|
if (cchMax < 2)
|
|
return TRUE;
|
|
|
|
dwLen = lstrlenW(lpszPath) + 1;
|
|
|
|
if (dwLen < cchMax)
|
|
{
|
|
/* Don't need to compact */
|
|
memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
|
|
return TRUE;
|
|
}
|
|
|
|
/* Path must be compacted to fit into lpszDest */
|
|
lpszFile = PathFindFileNameW(lpszPath);
|
|
dwFileLen = lpszPath + dwLen - lpszFile;
|
|
|
|
if (dwFileLen == dwLen)
|
|
{
|
|
/* No root in psth */
|
|
if (cchMax <= 4)
|
|
{
|
|
while (--cchMax > 0) /* No room left for anything but ellipses */
|
|
*lpszDest++ = '.';
|
|
*lpszDest = '\0';
|
|
return TRUE;
|
|
}
|
|
/* Compact the file name with ellipses at the end */
|
|
cchMax -= 4;
|
|
memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
|
|
lstrcpyW(lpszDest + cchMax, szEllipses);
|
|
return TRUE;
|
|
}
|
|
/* We have a root in the path */
|
|
lpszFile--; /* Start compacted filename with the path separator */
|
|
dwFileLen++;
|
|
|
|
if (dwFileLen + 3 > cchMax)
|
|
{
|
|
/* Compact the file name */
|
|
if (cchMax <= 4)
|
|
{
|
|
while (--cchMax > 0) /* No room left for anything but ellipses */
|
|
*lpszDest++ = '.';
|
|
*lpszDest = '\0';
|
|
return TRUE;
|
|
}
|
|
lstrcpyW(lpszDest, szEllipses);
|
|
lpszDest += 3;
|
|
cchMax -= 4;
|
|
*lpszDest++ = *lpszFile++;
|
|
if (cchMax <= 4)
|
|
{
|
|
while (--cchMax > 0) /* No room left for anything but ellipses */
|
|
*lpszDest++ = '.';
|
|
*lpszDest = '\0';
|
|
return TRUE;
|
|
}
|
|
cchMax -= 4;
|
|
memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR));
|
|
lstrcpyW(lpszDest + cchMax, szEllipses);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Only the root needs to be Compacted */
|
|
dwLen = cchMax - dwFileLen - 3;
|
|
memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR));
|
|
lstrcpyW(lpszDest + dwLen, szEllipses);
|
|
lstrcpyW(lpszDest + dwLen + 3, lpszFile);
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsDirectoryA [SHLWAPI.@]
|
|
*
|
|
* Determine if a path is a valid directory
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] Path to check.
|
|
*
|
|
* RETURNS
|
|
* FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes)
|
|
* FALSE if lpszPath is invalid or not a directory.
|
|
*
|
|
* NOTES
|
|
* Although this function is prototyped as returning a BOOL, it returns
|
|
* FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as:
|
|
*
|
|
*| if (PathIsDirectoryA("c:\\windows\\") == TRUE)
|
|
*| ...
|
|
*
|
|
* will always fail.
|
|
*/
|
|
BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath)
|
|
{
|
|
DWORD dwAttr;
|
|
|
|
TRACE("(%s)\n", debugstr_a(lpszPath));
|
|
|
|
if (!lpszPath || PathIsUNCServerA(lpszPath))
|
|
return FALSE;
|
|
|
|
if (PathIsUNCServerShareA(lpszPath))
|
|
{
|
|
FIXME("UNC Server Share not yet supported - FAILING\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES)
|
|
return FALSE;
|
|
return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsDirectoryW [SHLWAPI.@]
|
|
*
|
|
* See PathIsDirectoryA.
|
|
*/
|
|
BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
|
|
{
|
|
DWORD dwAttr;
|
|
|
|
TRACE("(%s)\n", debugstr_w(lpszPath));
|
|
|
|
if (!lpszPath || PathIsUNCServerW(lpszPath))
|
|
return FALSE;
|
|
|
|
if (PathIsUNCServerShareW(lpszPath))
|
|
{
|
|
FIXME("UNC Server Share not yet supported - FAILING\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
|
|
return FALSE;
|
|
return dwAttr & FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathFileExistsAndAttributesA [SHLWAPI.445]
|
|
*
|
|
* Determine if a file exists.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] Path to check
|
|
* dwAttr [O] attributes of file
|
|
*
|
|
* RETURNS
|
|
* TRUE If the file exists and is readable
|
|
* FALSE Otherwise
|
|
*/
|
|
BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr)
|
|
{
|
|
UINT iPrevErrMode;
|
|
DWORD dwVal = 0;
|
|
|
|
TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr);
|
|
|
|
if (dwAttr)
|
|
*dwAttr = INVALID_FILE_ATTRIBUTES;
|
|
|
|
if (!lpszPath)
|
|
return FALSE;
|
|
|
|
iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
dwVal = GetFileAttributesA(lpszPath);
|
|
SetErrorMode(iPrevErrMode);
|
|
if (dwAttr)
|
|
*dwAttr = dwVal;
|
|
return (dwVal != INVALID_FILE_ATTRIBUTES);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathFileExistsAndAttributesW [SHLWAPI.446]
|
|
*
|
|
* See PathFileExistsA.
|
|
*/
|
|
BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr)
|
|
{
|
|
UINT iPrevErrMode;
|
|
DWORD dwVal;
|
|
|
|
TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr);
|
|
|
|
if (!lpszPath)
|
|
return FALSE;
|
|
|
|
iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
dwVal = GetFileAttributesW(lpszPath);
|
|
SetErrorMode(iPrevErrMode);
|
|
if (dwAttr)
|
|
*dwAttr = dwVal;
|
|
return (dwVal != INVALID_FILE_ATTRIBUTES);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsContentTypeA [SHLWAPI.@]
|
|
*
|
|
* Determine if a file is of a given registered content type.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] File to check
|
|
* lpszContentType [I] Content type to check for
|
|
*
|
|
* RETURNS
|
|
* TRUE If lpszPath is a given registered content type,
|
|
* FALSE Otherwise.
|
|
*
|
|
* NOTES
|
|
* This function looks up the registered content type for lpszPath. If
|
|
* a content type is registered, it is compared (case insensitively) to
|
|
* lpszContentType. Only if this matches does the function succeed.
|
|
*/
|
|
BOOL WINAPI PathIsContentTypeA(LPCSTR path, LPCSTR content_type)
|
|
{
|
|
char buf[MAX_PATH];
|
|
DWORD size = sizeof(buf);
|
|
LPCSTR ext;
|
|
|
|
TRACE("(%s,%s)\n", debugstr_a(path), debugstr_a(content_type));
|
|
|
|
if(!path) return FALSE;
|
|
if(!(ext = PathFindExtensionA(path)) || !*ext) return FALSE;
|
|
if(SHGetValueA(HKEY_CLASSES_ROOT, ext, "Content Type", NULL, buf, &size)) return FALSE;
|
|
return !lstrcmpiA(content_type, buf);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsContentTypeW [SHLWAPI.@]
|
|
*
|
|
* See PathIsContentTypeA.
|
|
*/
|
|
BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType)
|
|
{
|
|
static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' };
|
|
LPCWSTR szExt;
|
|
DWORD dwDummy;
|
|
WCHAR szBuff[MAX_PATH];
|
|
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType));
|
|
|
|
if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt &&
|
|
!SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType,
|
|
REG_NONE, szBuff, &dwDummy) &&
|
|
!wcsicmp(lpszContentType, szBuff))
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsSystemFolderA [SHLWAPI.@]
|
|
*
|
|
* Determine if a path or file attributes are a system folder.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] Path to check.
|
|
* dwAttrib [I] Attributes to check, if lpszPath is NULL.
|
|
*
|
|
* RETURNS
|
|
* TRUE If lpszPath or dwAttrib are a system folder.
|
|
* FALSE If GetFileAttributesA() fails or neither parameter is a system folder.
|
|
*/
|
|
BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib)
|
|
{
|
|
TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath), dwAttrib);
|
|
|
|
if (lpszPath && *lpszPath)
|
|
dwAttrib = GetFileAttributesA(lpszPath);
|
|
|
|
if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
|
|
!(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsSystemFolderW [SHLWAPI.@]
|
|
*
|
|
* See PathIsSystemFolderA.
|
|
*/
|
|
BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib)
|
|
{
|
|
TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath), dwAttrib);
|
|
|
|
if (lpszPath && *lpszPath)
|
|
dwAttrib = GetFileAttributesW(lpszPath);
|
|
|
|
if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
|
|
!(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathMakePrettyA [SHLWAPI.@]
|
|
*
|
|
* Convert an uppercase DOS filename into lowercase.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I/O] Path to convert.
|
|
*
|
|
* RETURNS
|
|
* TRUE If the path was an uppercase DOS path and was converted,
|
|
* FALSE Otherwise.
|
|
*/
|
|
BOOL WINAPI PathMakePrettyA(LPSTR lpszPath)
|
|
{
|
|
LPSTR pszIter = lpszPath;
|
|
|
|
TRACE("(%s)\n", debugstr_a(lpszPath));
|
|
|
|
if (!pszIter)
|
|
return FALSE;
|
|
|
|
if (*pszIter)
|
|
{
|
|
do
|
|
{
|
|
if (islower(*pszIter) || IsDBCSLeadByte(*pszIter))
|
|
return FALSE; /* Not DOS path */
|
|
pszIter++;
|
|
} while (*pszIter);
|
|
pszIter = lpszPath + 1;
|
|
while (*pszIter)
|
|
{
|
|
*pszIter = tolower(*pszIter);
|
|
pszIter++;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathMakePrettyW [SHLWAPI.@]
|
|
*
|
|
* See PathMakePrettyA.
|
|
*/
|
|
BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath)
|
|
{
|
|
LPWSTR pszIter = lpszPath;
|
|
|
|
TRACE("(%s)\n", debugstr_w(lpszPath));
|
|
|
|
if (!pszIter)
|
|
return FALSE;
|
|
|
|
if (*pszIter)
|
|
{
|
|
do
|
|
{
|
|
if (iswlower(*pszIter))
|
|
return FALSE; /* Not DOS path */
|
|
pszIter++;
|
|
} while (*pszIter);
|
|
pszIter = lpszPath + 1;
|
|
while (*pszIter)
|
|
{
|
|
*pszIter = towlower(*pszIter);
|
|
pszIter++;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathCompactPathA [SHLWAPI.@]
|
|
*
|
|
* Make a path fit into a given width when printed to a DC.
|
|
*
|
|
* PARAMS
|
|
* hDc [I] Destination DC
|
|
* lpszPath [I/O] Path to be printed to hDc
|
|
* dx [I] Desired width
|
|
*
|
|
* RETURNS
|
|
* TRUE If the path was modified/went well.
|
|
* FALSE Otherwise.
|
|
*/
|
|
BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx);
|
|
|
|
if (lpszPath)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
|
|
bRet = PathCompactPathW(hDC, szPath, dx);
|
|
WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathCompactPathW [SHLWAPI.@]
|
|
*
|
|
* See PathCompactPathA.
|
|
*/
|
|
BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx)
|
|
{
|
|
static const WCHAR szEllipses[] = { '.', '.', '.', '\0' };
|
|
BOOL bRet = TRUE;
|
|
HDC hdc = 0;
|
|
WCHAR buff[MAX_PATH];
|
|
SIZE size;
|
|
DWORD dwLen;
|
|
|
|
TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx);
|
|
|
|
if (!lpszPath)
|
|
return FALSE;
|
|
|
|
if (!hDC)
|
|
hdc = hDC = GetDC(0);
|
|
|
|
/* Get the length of the whole path */
|
|
dwLen = lstrlenW(lpszPath);
|
|
GetTextExtentPointW(hDC, lpszPath, dwLen, &size);
|
|
|
|
if ((UINT)size.cx > dx)
|
|
{
|
|
/* Path too big, must reduce it */
|
|
LPWSTR sFile;
|
|
DWORD dwEllipsesLen = 0, dwPathLen = 0;
|
|
|
|
sFile = PathFindFileNameW(lpszPath);
|
|
if (sFile != lpszPath) sFile--;
|
|
|
|
/* Get the size of ellipses */
|
|
GetTextExtentPointW(hDC, szEllipses, 3, &size);
|
|
dwEllipsesLen = size.cx;
|
|
/* Get the size of the file name */
|
|
GetTextExtentPointW(hDC, sFile, lstrlenW(sFile), &size);
|
|
dwPathLen = size.cx;
|
|
|
|
if (sFile != lpszPath)
|
|
{
|
|
LPWSTR sPath = sFile;
|
|
BOOL bEllipses = FALSE;
|
|
|
|
/* The path includes a file name. Include as much of the path prior to
|
|
* the file name as possible, allowing for the ellipses, e.g:
|
|
* c:\some very long path\filename ==> c:\some v...\filename
|
|
*/
|
|
lstrcpynW(buff, sFile, MAX_PATH);
|
|
|
|
do
|
|
{
|
|
DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen;
|
|
|
|
GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size);
|
|
dwTotalLen += size.cx;
|
|
if (dwTotalLen <= dx)
|
|
break;
|
|
sPath--;
|
|
if (!bEllipses)
|
|
{
|
|
bEllipses = TRUE;
|
|
sPath -= 2;
|
|
}
|
|
} while (sPath > lpszPath);
|
|
|
|
if (sPath > lpszPath)
|
|
{
|
|
if (bEllipses)
|
|
{
|
|
lstrcpyW(sPath, szEllipses);
|
|
lstrcpyW(sPath+3, buff);
|
|
}
|
|
bRet = TRUE;
|
|
goto end;
|
|
}
|
|
lstrcpyW(lpszPath, szEllipses);
|
|
lstrcpyW(lpszPath+3, buff);
|
|
bRet = FALSE;
|
|
goto end;
|
|
}
|
|
|
|
/* Trim the path by adding ellipses to the end, e.g:
|
|
* A very long file name.txt ==> A very...
|
|
*/
|
|
dwLen = lstrlenW(lpszPath);
|
|
|
|
if (dwLen > MAX_PATH - 3)
|
|
dwLen = MAX_PATH - 3;
|
|
lstrcpynW(buff, sFile, dwLen);
|
|
|
|
do {
|
|
dwLen--;
|
|
GetTextExtentPointW(hDC, buff, dwLen, &size);
|
|
} while (dwLen && size.cx + dwEllipsesLen > dx);
|
|
|
|
if (!dwLen)
|
|
{
|
|
DWORD dwWritten = 0;
|
|
|
|
dwEllipsesLen /= 3; /* Size of a single '.' */
|
|
|
|
/* Write as much of the Ellipses string as possible */
|
|
while (dwWritten + dwEllipsesLen < dx && dwLen < 3)
|
|
{
|
|
*lpszPath++ = '.';
|
|
dwWritten += dwEllipsesLen;
|
|
dwLen++;
|
|
}
|
|
*lpszPath = '\0';
|
|
bRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
lstrcpyW(buff + dwLen, szEllipses);
|
|
lstrcpyW(lpszPath, buff);
|
|
}
|
|
}
|
|
|
|
end:
|
|
if (hdc)
|
|
ReleaseDC(0, hdc);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_UseSystemForSystemFolders
|
|
*
|
|
* Internal helper for PathMakeSystemFolderW.
|
|
*/
|
|
static BOOL SHLWAPI_UseSystemForSystemFolders(void)
|
|
{
|
|
static BOOL bCheckedReg = FALSE;
|
|
static BOOL bUseSystemForSystemFolders = FALSE;
|
|
|
|
if (!bCheckedReg)
|
|
{
|
|
bCheckedReg = TRUE;
|
|
|
|
/* Key tells Win what file attributes to use on system folders */
|
|
if (SHGetValueA(HKEY_LOCAL_MACHINE,
|
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
|
|
"UseSystemForSystemFolders", 0, 0, 0))
|
|
bUseSystemForSystemFolders = TRUE;
|
|
}
|
|
return bUseSystemForSystemFolders;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathMakeSystemFolderA [SHLWAPI.@]
|
|
*
|
|
* Set system folder attribute for a path.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] The path to turn into a system folder
|
|
*
|
|
* RETURNS
|
|
* TRUE If the path was changed to/already was a system folder
|
|
* FALSE If the path is invalid or SetFileAttributesA() fails
|
|
*/
|
|
BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("(%s)\n", debugstr_a(lpszPath));
|
|
|
|
if (lpszPath && *lpszPath)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
|
|
bRet = PathMakeSystemFolderW(szPath);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathMakeSystemFolderW [SHLWAPI.@]
|
|
*
|
|
* See PathMakeSystemFolderA.
|
|
*/
|
|
BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath)
|
|
{
|
|
DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr;
|
|
WCHAR buff[MAX_PATH];
|
|
|
|
TRACE("(%s)\n", debugstr_w(lpszPath));
|
|
|
|
if (!lpszPath || !*lpszPath)
|
|
return FALSE;
|
|
|
|
/* If the directory is already a system directory, don't do anything */
|
|
GetSystemDirectoryW(buff, MAX_PATH);
|
|
if (!wcscmp(buff, lpszPath))
|
|
return TRUE;
|
|
|
|
GetWindowsDirectoryW(buff, MAX_PATH);
|
|
if (!wcscmp(buff, lpszPath))
|
|
return TRUE;
|
|
|
|
/* "UseSystemForSystemFolders" Tells Win what attributes to use */
|
|
if (SHLWAPI_UseSystemForSystemFolders())
|
|
dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM;
|
|
|
|
if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES)
|
|
return FALSE;
|
|
|
|
/* Change file attributes to system attributes */
|
|
dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
|
|
return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathUnmakeSystemFolderA [SHLWAPI.@]
|
|
*
|
|
* Remove the system folder attributes from a path.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] The path to remove attributes from
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE.
|
|
* Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling
|
|
* SetFileAttributesA() fails.
|
|
*/
|
|
BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath)
|
|
{
|
|
DWORD dwAttr;
|
|
|
|
TRACE("(%s)\n", debugstr_a(lpszPath));
|
|
|
|
if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
|
|
!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
|
|
return FALSE;
|
|
|
|
dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
|
|
return SetFileAttributesA(lpszPath, dwAttr);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathUnmakeSystemFolderW [SHLWAPI.@]
|
|
*
|
|
* See PathUnmakeSystemFolderA.
|
|
*/
|
|
BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath)
|
|
{
|
|
DWORD dwAttr;
|
|
|
|
TRACE("(%s)\n", debugstr_w(lpszPath));
|
|
|
|
if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES ||
|
|
!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
|
|
return FALSE;
|
|
|
|
dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
|
|
return SetFileAttributesW(lpszPath, dwAttr);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* PathSetDlgItemPathA [SHLWAPI.@]
|
|
*
|
|
* Set the text of a dialog item to a path, shrinking the path to fit
|
|
* if it is too big for the item.
|
|
*
|
|
* PARAMS
|
|
* hDlg [I] Dialog handle
|
|
* id [I] ID of item in the dialog
|
|
* lpszPath [I] Path to set as the items text
|
|
*
|
|
* RETURNS
|
|
* Nothing.
|
|
*
|
|
* NOTES
|
|
* If lpszPath is NULL, a blank string ("") is set (i.e. The previous
|
|
* window text is erased).
|
|
*/
|
|
VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
|
|
TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath));
|
|
|
|
if (lpszPath)
|
|
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
|
|
else
|
|
szPath[0] = '\0';
|
|
PathSetDlgItemPathW(hDlg, id, szPath);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathSetDlgItemPathW [SHLWAPI.@]
|
|
*
|
|
* See PathSetDlgItemPathA.
|
|
*/
|
|
VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath)
|
|
{
|
|
WCHAR path[MAX_PATH + 1];
|
|
HWND hwItem;
|
|
RECT rect;
|
|
HDC hdc;
|
|
HGDIOBJ hPrevObj;
|
|
|
|
TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath));
|
|
|
|
if (!(hwItem = GetDlgItem(hDlg, id)))
|
|
return;
|
|
|
|
if (lpszPath)
|
|
lstrcpynW(path, lpszPath, ARRAY_SIZE(path));
|
|
else
|
|
path[0] = '\0';
|
|
|
|
GetClientRect(hwItem, &rect);
|
|
hdc = GetDC(hDlg);
|
|
hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0));
|
|
|
|
if (hPrevObj)
|
|
{
|
|
PathCompactPathW(hdc, path, rect.right);
|
|
SelectObject(hdc, hPrevObj);
|
|
}
|
|
|
|
ReleaseDC(hDlg, hdc);
|
|
SetWindowTextW(hwItem, path);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsNetworkPathA [SHLWAPI.@]
|
|
*
|
|
* Determine if the given path is a network path.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] Path to check
|
|
*
|
|
* RETURNS
|
|
* TRUE If lpszPath is a UNC share or mapped network drive, or
|
|
* FALSE If lpszPath is a local drive or cannot be determined
|
|
*/
|
|
BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath)
|
|
{
|
|
int dwDriveNum;
|
|
|
|
TRACE("(%s)\n",debugstr_a(lpszPath));
|
|
|
|
if (!lpszPath)
|
|
return FALSE;
|
|
if (*lpszPath == '\\' && lpszPath[1] == '\\')
|
|
return TRUE;
|
|
dwDriveNum = PathGetDriveNumberA(lpszPath);
|
|
if (dwDriveNum == -1)
|
|
return FALSE;
|
|
GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
|
|
return pIsNetDrive(dwDriveNum);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsNetworkPathW [SHLWAPI.@]
|
|
*
|
|
* See PathIsNetworkPathA.
|
|
*/
|
|
BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath)
|
|
{
|
|
int dwDriveNum;
|
|
|
|
TRACE("(%s)\n", debugstr_w(lpszPath));
|
|
|
|
if (!lpszPath)
|
|
return FALSE;
|
|
if (*lpszPath == '\\' && lpszPath[1] == '\\')
|
|
return TRUE;
|
|
dwDriveNum = PathGetDriveNumberW(lpszPath);
|
|
if (dwDriveNum == -1)
|
|
return FALSE;
|
|
GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */
|
|
return pIsNetDrive(dwDriveNum);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsDirectoryEmptyA [SHLWAPI.@]
|
|
*
|
|
* Determine if a given directory is empty.
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I] Directory to check
|
|
*
|
|
* RETURNS
|
|
* TRUE If the directory exists and contains no files,
|
|
* FALSE Otherwise
|
|
*/
|
|
BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("(%s)\n",debugstr_a(lpszPath));
|
|
|
|
if (lpszPath)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH);
|
|
bRet = PathIsDirectoryEmptyW(szPath);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathIsDirectoryEmptyW [SHLWAPI.@]
|
|
*
|
|
* See PathIsDirectoryEmptyA.
|
|
*/
|
|
BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath)
|
|
{
|
|
static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' };
|
|
WCHAR szSearch[MAX_PATH];
|
|
DWORD dwLen;
|
|
HANDLE hfind;
|
|
BOOL retVal = TRUE;
|
|
WIN32_FIND_DATAW find_data;
|
|
|
|
TRACE("(%s)\n",debugstr_w(lpszPath));
|
|
|
|
if (!lpszPath || !PathIsDirectoryW(lpszPath))
|
|
return FALSE;
|
|
|
|
lstrcpynW(szSearch, lpszPath, MAX_PATH);
|
|
PathAddBackslashW(szSearch);
|
|
dwLen = lstrlenW(szSearch);
|
|
if (dwLen > MAX_PATH - 4)
|
|
return FALSE;
|
|
|
|
lstrcpyW(szSearch + dwLen, szAllFiles);
|
|
hfind = FindFirstFileW(szSearch, &find_data);
|
|
if (hfind == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
do
|
|
{
|
|
if (find_data.cFileName[0] == '.')
|
|
{
|
|
if (find_data.cFileName[1] == '\0') continue;
|
|
if (find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0') continue;
|
|
}
|
|
|
|
retVal = FALSE;
|
|
break;
|
|
}
|
|
while (FindNextFileW(hfind, &find_data));
|
|
|
|
FindClose(hfind);
|
|
return retVal;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* PathFindSuffixArrayA [SHLWAPI.@]
|
|
*
|
|
* Find a suffix string in an array of suffix strings
|
|
*
|
|
* PARAMS
|
|
* lpszSuffix [I] Suffix string to search for
|
|
* lppszArray [I] Array of suffix strings to search
|
|
* dwCount [I] Number of elements in lppszArray
|
|
*
|
|
* RETURNS
|
|
* Success: The index of the position of lpszSuffix in lppszArray
|
|
* Failure: 0, if any parameters are invalid or lpszSuffix is not found
|
|
*
|
|
* NOTES
|
|
* The search is case sensitive.
|
|
* The match is made against the end of the suffix string, so for example:
|
|
* lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not.
|
|
*/
|
|
LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount)
|
|
{
|
|
size_t dwLen;
|
|
int dwRet = 0;
|
|
|
|
TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount);
|
|
|
|
if (lpszSuffix && lppszArray && dwCount > 0)
|
|
{
|
|
dwLen = strlen(lpszSuffix);
|
|
|
|
while (dwRet < dwCount)
|
|
{
|
|
size_t dwCompareLen = strlen(*lppszArray);
|
|
if (dwCompareLen < dwLen)
|
|
{
|
|
if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
|
|
return *lppszArray; /* Found */
|
|
}
|
|
dwRet++;
|
|
lppszArray++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathFindSuffixArrayW [SHLWAPI.@]
|
|
*
|
|
* See PathFindSuffixArrayA.
|
|
*/
|
|
LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount)
|
|
{
|
|
size_t dwLen;
|
|
int dwRet = 0;
|
|
|
|
TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount);
|
|
|
|
if (lpszSuffix && lppszArray && dwCount > 0)
|
|
{
|
|
dwLen = lstrlenW(lpszSuffix);
|
|
|
|
while (dwRet < dwCount)
|
|
{
|
|
size_t dwCompareLen = lstrlenW(*lppszArray);
|
|
if (dwCompareLen < dwLen)
|
|
{
|
|
if (!wcscmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray))
|
|
return *lppszArray; /* Found */
|
|
}
|
|
dwRet++;
|
|
lppszArray++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathUndecorateA [SHLWAPI.@]
|
|
*
|
|
* Undecorate a file path
|
|
*
|
|
* PARAMS
|
|
* lpszPath [I/O] Path to remove any decoration from
|
|
*
|
|
* RETURNS
|
|
* Nothing
|
|
*
|
|
* NOTES
|
|
* A decorations form is "path[n].ext" where "n" is an optional decimal number.
|
|
*/
|
|
VOID WINAPI PathUndecorateA(LPSTR lpszPath)
|
|
{
|
|
TRACE("(%s)\n",debugstr_a(lpszPath));
|
|
|
|
if (lpszPath)
|
|
{
|
|
LPSTR lpszExt = PathFindExtensionA(lpszPath);
|
|
if (lpszExt > lpszPath && lpszExt[-1] == ']')
|
|
{
|
|
LPSTR lpszSkip = lpszExt - 2;
|
|
if (*lpszSkip == '[')
|
|
lpszSkip++; /* [] (no number) */
|
|
else
|
|
while (lpszSkip > lpszPath && isdigit(lpszSkip[-1]))
|
|
lpszSkip--;
|
|
if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\')
|
|
{
|
|
/* remove the [n] */
|
|
lpszSkip--;
|
|
while (*lpszExt)
|
|
*lpszSkip++ = *lpszExt++;
|
|
*lpszSkip = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
* PathUndecorateW [SHLWAPI.@]
|
|
*
|
|
* See PathUndecorateA.
|
|
*/
|
|
VOID WINAPI PathUndecorateW(LPWSTR lpszPath)
|
|
{
|
|
TRACE("(%s)\n",debugstr_w(lpszPath));
|
|
|
|
if (lpszPath)
|
|
{
|
|
LPWSTR lpszExt = PathFindExtensionW(lpszPath);
|
|
if (lpszExt > lpszPath && lpszExt[-1] == ']')
|
|
{
|
|
LPWSTR lpszSkip = lpszExt - 2;
|
|
if (*lpszSkip == '[')
|
|
lpszSkip++; /* [] (no number) */
|
|
else
|
|
while (lpszSkip > lpszPath && iswdigit(lpszSkip[-1]))
|
|
lpszSkip--;
|
|
if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\')
|
|
{
|
|
/* remove the [n] */
|
|
lpszSkip--;
|
|
while (*lpszExt)
|
|
*lpszSkip++ = *lpszExt++;
|
|
*lpszSkip = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.440]
|
|
*
|
|
* Find localised or default web content in "%WINDOWS%\web\".
|
|
*
|
|
* PARAMS
|
|
* lpszFile [I] File name containing content to look for
|
|
* lpszPath [O] Buffer to contain the full path to the file
|
|
* dwPathLen [I] Length of lpszPath
|
|
*
|
|
* RETURNS
|
|
* Success: S_OK. lpszPath contains the full path to the content.
|
|
* Failure: E_FAIL. The content does not exist or lpszPath is too short.
|
|
*/
|
|
HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen)
|
|
{
|
|
WCHAR szFile[MAX_PATH], szPath[MAX_PATH];
|
|
HRESULT hRet;
|
|
|
|
TRACE("(%s,%p,%d)\n", lpszFile, lpszPath, dwPathLen);
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH);
|
|
szPath[0] = '\0';
|
|
hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen);
|
|
WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0);
|
|
return hRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.441]
|
|
*
|
|
* Unicode version of SHGetWebFolderFilePathA.
|
|
*/
|
|
HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen)
|
|
{
|
|
static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'};
|
|
static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'};
|
|
DWORD dwLen, dwFileLen;
|
|
LANGID lidSystem, lidUser;
|
|
|
|
TRACE("(%s,%p,%d)\n", debugstr_w(lpszFile), lpszPath, dwPathLen);
|
|
|
|
/* Get base directory for web content */
|
|
dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen);
|
|
if (dwLen > 0 && lpszPath[dwLen-1] == '\\')
|
|
dwLen--;
|
|
|
|
dwFileLen = lstrlenW(lpszFile);
|
|
|
|
if (dwLen + dwFileLen + ARRAY_SIZE(szWeb) >= dwPathLen)
|
|
return E_FAIL; /* lpszPath too short */
|
|
|
|
lstrcpyW(lpszPath+dwLen, szWeb);
|
|
dwLen += ARRAY_SIZE(szWeb);
|
|
dwPathLen = dwPathLen - dwLen; /* Remaining space */
|
|
|
|
lidSystem = GetSystemDefaultUILanguage();
|
|
lidUser = GetUserDefaultUILanguage();
|
|
|
|
if (lidSystem != lidUser)
|
|
{
|
|
if (dwFileLen + ARRAY_SIZE(szWebMui) < dwPathLen)
|
|
{
|
|
/* Use localised content in the users UI language if present */
|
|
wsprintfW(lpszPath + dwLen, szWebMui, lidUser);
|
|
lstrcpyW(lpszPath + dwLen + ARRAY_SIZE(szWebMui), lpszFile);
|
|
if (PathFileExistsW(lpszPath))
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
/* Fall back to OS default installed content */
|
|
lstrcpyW(lpszPath + dwLen, lpszFile);
|
|
if (PathFileExistsW(lpszPath))
|
|
return S_OK;
|
|
return E_FAIL;
|
|
}
|