windows-nt/Source/XPSP1/NT/windows/appcompat/shims/lua/utils.cpp
2020-09-26 16:20:57 +08:00

1347 lines
30 KiB
C++

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
LUA_RedirectFS_Cleanup.cpp
Abstract:
Delete the redirected copies in every user's directory.
Created:
02/12/2001 maonis
Modified:
--*/
#include "precomp.h"
#include "secutils.h"
#include "utils.h"
#include <userenv.h>
WCHAR g_wszUserProfile[MAX_PATH] = L"";
DWORD g_cUserProfile = 0;
WCHAR g_wszSystemRoot[MAX_PATH] = L"";
DWORD g_cSystemRoot = 0;
DWORD
GetUserProfileDirW()
{
if (g_cUserProfile == 0)
{
HANDLE hToken;
if (OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY,
&hToken))
{
WCHAR wszProfileDir[MAX_PATH] = L"";
DWORD dwSize = MAX_PATH;
if (GetUserProfileDirectoryW(hToken, wszProfileDir, &dwSize))
{
dwSize = GetLongPathNameW(wszProfileDir, g_wszUserProfile, MAX_PATH);
if (dwSize <= MAX_PATH)
{
//
// Only if we successfully got the path and it's not more
// than MAX_PATH will we set the global values.
//
g_cUserProfile = dwSize;
}
else
{
g_wszUserProfile[0] = L'\0';
}
}
CloseHandle(hToken);
}
}
return g_cUserProfile;
}
BOOL
IsUserDirectory(LPCWSTR pwszPath)
{
GetUserProfileDirW();
if (g_cUserProfile)
{
return !_wcsnicmp(pwszPath, g_wszUserProfile, g_cUserProfile);
}
return FALSE;
}
DWORD
GetSystemRootDirW()
{
if (g_cSystemRoot == 0)
{
if (g_cSystemRoot = GetSystemWindowsDirectoryW(g_wszSystemRoot, MAX_PATH))
{
//
// Just to be cautious - if we really have a system directory that's
// longer than MAX_PATH, most likely something suspicious is going on
// here, so we bail out.
//
if (g_cSystemRoot >= MAX_PATH)
{
g_wszSystemRoot[0] = L'\0';
g_cSystemRoot = 0;
}
else if (g_cSystemRoot > 3)
{
g_wszSystemRoot[g_cSystemRoot] = L'\\';
g_wszSystemRoot[g_cSystemRoot + 1] = L'\0';
++g_cSystemRoot;
}
else
{
g_wszSystemRoot[g_cSystemRoot] = L'\0';
}
}
}
return g_cSystemRoot;
}
/*++
Function Description:
For the GetPrivateProfile* and WritePrivateProfile* APIs,
if the app didn't specify the path, we append the windows dir
in the front as that's where it'll be looking for and creating
the file it doesn't already exist.
Arguments:
IN lpFileName - The file name specified by the profile API.
IN/OUT pwszFullPath - Pointer to the buffer to receive the full path.
This buffer is at least MAX_PATH WCHARs long.
Return Value:
TRUE - Successfully got the path.
FALSE - We don't handle this filename, either because an error
occured or the file name is longer than MAX_PATH.
History:
05/16/2001 maonis Created
02/13/2002 maonis Modified to signal errors.
--*/
BOOL
MakeFileNameForProfileAPIsW(
IN LPCWSTR lpFileName,
IN OUT LPWSTR pwszFullPath // at least MAX_PATH in length
)
{
BOOL fIsSuccess = FALSE;
if (lpFileName)
{
DWORD cFileNameLen = wcslen(lpFileName);
if (wcschr(lpFileName, L'\\'))
{
if (cFileNameLen < MAX_PATH)
{
//
// The filename already contains the path, just copy it over.
//
wcsncpy(pwszFullPath, lpFileName, cFileNameLen);
fIsSuccess = TRUE;
}
}
else if (GetSystemRootDirW() && g_cSystemRoot)
{
DWORD cLen = g_cSystemRoot + cFileNameLen;
//
// Only copy when we know the buffer is big enough.
//
if (cLen < MAX_PATH)
{
wcsncpy(pwszFullPath, g_wszSystemRoot, g_cSystemRoot);
wcsncpy(pwszFullPath + g_cSystemRoot, lpFileName, cFileNameLen);
pwszFullPath[cLen - 1] = L'\0';
fIsSuccess = TRUE;
}
}
}
return fIsSuccess;
}
//
// If the .exe name is *setup*, *install* or _INS*._MP, we consider
// them a setup program and won't shim them.
//
BOOL IsSetup(
)
{
WCHAR wszModuleName[MAX_PATH + 1];
ZeroMemory(wszModuleName, (MAX_PATH + 1) * sizeof(WCHAR));
GetModuleFileNameW(NULL, wszModuleName, MAX_PATH + 1);
wszModuleName[MAX_PATH] = 0;
_wcslwr(wszModuleName);
if (wcsstr(wszModuleName, L"setup") || wcsstr(wszModuleName, L"install"))
{
return TRUE;
}
LPWSTR pwsz;
if (pwsz = wcsstr(wszModuleName, L"_ins"))
{
if (wcsstr(pwsz + 4, L"_mp"))
{
return TRUE;
}
}
return FALSE;
}
BOOL LuaShouldApplyShim(
)
{
return (!IsSetup() && ShouldApplyShim());
}
#define REDIRECT_DIR L"\\Local Settings\\Application Data\\Redirected\\"
// We look at HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList for the users.
#define PROFILELIST_STR L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"
#define CLASSES_HIVE_SUFFIX L"_Classes"
#define CLASSES_HIVE_SUFFIX_LEN (sizeof(CLASSES_HIVE_SUFFIX) / sizeof(WCHAR) - 1)
#define USER_HIVE_NAME L"\\NtUser.dat"
#define USER_HIVE_NAME_LEN (sizeof(USER_HIVE_NAME) / sizeof(WCHAR) - 1)
#define USER_CLASSES_HIVE_NAME L"\\Local Settings\\Application Data\\Microsoft\\Windows\\UsrClass.dat"
#define USER_CLASSES_HIVE_NAME_LEN (sizeof(USER_CLASSES_HIVE_NAME) / sizeof(WCHAR) - 1)
// Total number of users which is the number of subkeys of
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
static DWORD g_cUsers = 0;
// We need to keep a list of keys we had to load under HKEY_USERS and unload them
// when the process exits.
static WCHAR** g_wszLoadedKeys = NULL;
static DWORD g_cLoadedKeys = 0;
// The number of users is the number of subkeys under
// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
LONG
InitGetUsers(
OUT DWORD* pcUsers,
OUT HKEY* phKey
)
{
LONG lRes;
if ((lRes = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
PROFILELIST_STR,
0,
KEY_READ,
phKey)) == ERROR_SUCCESS)
{
lRes = RegQueryInfoKeyW(*phKey, NULL, NULL, NULL, pcUsers,
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
RegCloseKey(*phKey);
}
return lRes;
}
// In case of failure we need to clean up our array.
VOID
FreeUserDirectoryArray(
REDIRECTED_USER_PATH* pRedirectUserPaths
)
{
for (DWORD ui = 0; ui < g_cUsers; ++ui)
{
delete [] pRedirectUserPaths[ui].pwszPath;
}
delete [] pRedirectUserPaths;
}
BOOL
IsDirectory(
WCHAR* pwszName
)
{
DWORD dwAttrib = GetFileAttributesW(pwszName);
return (dwAttrib != -1 && dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
LONG GetProfilePath(
HKEY hkProfileList,
LPCWSTR pwszUserSID,
LPWSTR pwszUserDirectory
)
{
LONG lRes;
HKEY hkUserSID;
DWORD dwFlags;
// Open the user SID key.
if ((lRes = RegOpenKeyExW(
hkProfileList,
pwszUserSID,
0,
KEY_QUERY_VALUE,
&hkUserSID)) == ERROR_SUCCESS)
{
DWORD dwSize = sizeof(DWORD);
if ((lRes = RegQueryValueExW(
hkUserSID,
L"Flags",
NULL,
NULL,
(LPBYTE)&dwFlags,
&dwSize)) == ERROR_SUCCESS)
{
// Check if the value of Flag is 0, if so it's the user we care about.
if (dwFlags == 0)
{
DWORD cTemp = MAX_PATH;
WCHAR wszTemp[MAX_PATH] = L"";
if ((lRes = RegQueryValueExW(
hkUserSID,
L"ProfileImagePath",
NULL,
NULL,
(LPBYTE)wszTemp,
&cTemp)) == ERROR_SUCCESS)
{
DWORD cExpandLen = ExpandEnvironmentStringsW(wszTemp, pwszUserDirectory, MAX_PATH);
if (cExpandLen > MAX_PATH)
{
lRes = ERROR_MORE_DATA;
}
}
}
else
{
lRes = ERROR_INVALID_HANDLE;
}
}
RegCloseKey(hkUserSID);
}
return lRes;
}
BOOL
GetUsersFS(
REDIRECTED_USER_PATH** ppRedirectUserPaths,
DWORD* pcUsers
)
{
WCHAR wszRedirectDir[MAX_PATH] = L"";
DWORD cUsers;
HKEY hkProfileList;
if (InitGetUsers(&cUsers, &hkProfileList) != ERROR_SUCCESS)
{
DPF("LUAUtils", eDbgLevelError, "[GetUsersFS] Error initializing");
return FALSE;
}
*ppRedirectUserPaths = new REDIRECTED_USER_PATH [cUsers];
if (!*ppRedirectUserPaths)
{
DPF("LUAUtils", eDbgLevelError, "[GetUsersFS] Error allocating memory");
return FALSE;
}
REDIRECTED_USER_PATH* pRedirectUserPaths = *ppRedirectUserPaths;
ZeroMemory((PVOID)pRedirectUserPaths, cUsers * sizeof(REDIRECTED_USER_PATH));
WCHAR wszSubKey[MAX_PATH] = L"";
DWORD cSubKey = 0;
HKEY hkUserSID;
LONG lRes;
// The number of users we care about.
DWORD cLUAUsers = 0;
DWORD dwIndex = 0;
while (TRUE)
{
cSubKey = MAX_PATH;
lRes = RegEnumKeyExW(hkProfileList, dwIndex, wszSubKey, &cSubKey,
NULL, NULL, NULL, NULL);
if (lRes == ERROR_SUCCESS)
{
WCHAR wszUserDirectory[MAX_PATH] = L"";
if ((lRes = GetProfilePath(hkProfileList, wszSubKey, wszUserDirectory))
== ERROR_SUCCESS)
{
//
// If the directory doesn't exist, it means either the user
// never logged on, or there are no redirected files for that
// user. We simply skip it.
//
if (IsDirectory(wszUserDirectory))
{
DWORD cPath = wcslen(wszUserDirectory) + 1;
LPWSTR pwszPath = new WCHAR [cPath];
if (pwszPath)
{
wcscpy(pwszPath, wszUserDirectory);
pRedirectUserPaths[cLUAUsers].pwszPath = pwszPath;
pRedirectUserPaths[cLUAUsers].cLen = cPath;
++cLUAUsers;
}
else
{
DPF("LUAUtils", eDbgLevelError,
"[GetUsersFS] Error allocating memory");
lRes = ERROR_NOT_ENOUGH_MEMORY;
goto EXIT;
}
}
}
}
else if (lRes == ERROR_NO_MORE_ITEMS)
{
*pcUsers = cLUAUsers;
lRes = ERROR_SUCCESS;
goto EXIT;
}
else
{
break;
}
++dwIndex;
}
EXIT:
RegCloseKey(hkProfileList);
if (lRes == ERROR_SUCCESS)
{
return TRUE;
}
FreeUserDirectoryArray(pRedirectUserPaths);
return FALSE;
}
VOID
FreeUsersFS(
REDIRECTED_USER_PATH* pRedirectUserPaths
)
{
FreeUserDirectoryArray(pRedirectUserPaths);
}
LONG
LoadHive(
LPCWSTR pwszHiveName,
LPCWSTR pwszHiveFile,
HKEY* phKey
)
{
LONG lRes;
// If the hive is already loaded, we'll get a sharing violation so
// check that as well.
if ((lRes = RegLoadKeyW(HKEY_USERS, pwszHiveName, pwszHiveFile))
== ERROR_SUCCESS || lRes == ERROR_SHARING_VIOLATION)
{
if (lRes == ERROR_SUCCESS)
{
DWORD cLen = wcslen(pwszHiveName) + 1;
g_wszLoadedKeys[g_cLoadedKeys] = new WCHAR [cLen];
if (!(g_wszLoadedKeys[g_cLoadedKeys]))
{
DPF("LUAUtils", eDbgLevelError,
"[LoadHive] Error allocating %d WCHARs",
cLen);
return ERROR_NOT_ENOUGH_MEMORY;
}
// Store the hive name so later on we can unload this hive.
wcscpy(g_wszLoadedKeys[g_cLoadedKeys++], pwszHiveName);
}
lRes = RegOpenKeyExW(
HKEY_USERS,
pwszHiveName,
0,
KEY_ALL_ACCESS,
phKey);
}
return lRes;
}
BOOL
GetUsersReg(
USER_HIVE_KEY** pphkUsers,
DWORD* pcUsers
)
{
// We have to enable the "Restore files and directories" privilege to
// load each user's hive.
if (!AdjustPrivilege(SE_RESTORE_NAME, TRUE))
{
DPF("LUAUtils", eDbgLevelError,
"[GetUsersReg] Error enabling the SE_RESTORE_NAME privilege");
return FALSE;
}
DWORD cUsers;
HKEY hkProfileList;
if (InitGetUsers(&cUsers, &hkProfileList) != ERROR_SUCCESS)
{
DPF("LUAUtils", eDbgLevelError, "[GetUsersReg] Error initializing");
return FALSE;
}
*pphkUsers = new USER_HIVE_KEY [cUsers];
if (!*pphkUsers)
{
DPF("LUAUtils", eDbgLevelError,
"[GetUsersReg] Error allocating memory for %d USER_HIVE_KEYs",
cUsers);
return FALSE;
}
g_wszLoadedKeys = new WCHAR* [cUsers * 2];
if (!g_wszLoadedKeys)
{
DPF("LUAUtils", eDbgLevelError,
"[GetUsersReg] Error allocating memory for %d WCHARs",
cUsers * 2);
delete [] *pphkUsers;
return FALSE;
}
USER_HIVE_KEY* phkUsers = *pphkUsers;
ZeroMemory((PVOID)phkUsers, cUsers * sizeof(USER_HIVE_KEY));
ZeroMemory((PVOID)g_wszLoadedKeys, cUsers * 2 * sizeof (WCHAR*));
WCHAR wszSubKey[MAX_PATH] = L"";
WCHAR wszUserHive[MAX_PATH] = L"";
WCHAR wszUserClassesHive[MAX_PATH] = L"";
DWORD cSubKey = 0;
HKEY hkSubKey;
LONG lRes;
// The number of users we care about.
DWORD cLUAUsers = 0;
DWORD dwIndex = 0;
DWORD cUserHive = 0;
while (TRUE)
{
cSubKey = MAX_PATH;
lRes = RegEnumKeyExW(hkProfileList, dwIndex, wszSubKey, &cSubKey,
NULL, NULL, NULL, NULL);
if (lRes == ERROR_SUCCESS)
{
if ((lRes = GetProfilePath(hkProfileList, wszSubKey, wszUserHive))
== ERROR_SUCCESS)
{
//
// Make sure we don't buffer overflow.
//
cUserHive = wcslen(wszUserHive);
if ((cUserHive + USER_CLASSES_HIVE_NAME_LEN + 1) > MAX_PATH ||
(cUserHive + USER_HIVE_NAME_LEN + 1) > MAX_PATH)
{
DPF("LUAUtils", eDbgLevelError,
"[GetUsersReg] The hive key names are too long - we don't handle them");
goto EXIT;
}
//
// Construct the locations for the user hive and user classes data hive.
//
wcsncpy(wszUserClassesHive, wszUserHive, cUserHive);
wcsncpy(
wszUserClassesHive + cUserHive,
USER_CLASSES_HIVE_NAME,
USER_CLASSES_HIVE_NAME_LEN);
wszUserClassesHive[cUserHive + USER_CLASSES_HIVE_NAME_LEN] = L'\0';
wcsncpy(wszUserHive + cUserHive, USER_HIVE_NAME, USER_HIVE_NAME_LEN);
wszUserHive[cUserHive + USER_HIVE_NAME_LEN] = L'\0';
//
// Load the HKCU for this user.
//
if ((lRes = LoadHive(
wszSubKey,
wszUserHive,
&phkUsers[cLUAUsers].hkUser)) == ERROR_SUCCESS)
{
//
// We can't necessarily load the HKCR for this user - it might
// contain no data so we only attemp to load it.
//
if ((cSubKey + CLASSES_HIVE_SUFFIX_LEN + 1) > MAX_PATH)
{
DPF("LUAUtils", eDbgLevelError,
"[GetUsersReg] The CR key name is too long - we don't handle it");
goto EXIT;
}
wcsncpy(wszSubKey + cSubKey, CLASSES_HIVE_SUFFIX, CLASSES_HIVE_SUFFIX_LEN);
wszSubKey[cSubKey + CLASSES_HIVE_SUFFIX_LEN] = L'\0';
LoadHive(
wszSubKey,
wszUserClassesHive,
&phkUsers[cLUAUsers].hkUserClasses);
++cLUAUsers;
}
}
}
else if (lRes == ERROR_NO_MORE_ITEMS)
{
*pcUsers = cLUAUsers;
lRes = ERROR_SUCCESS;
goto EXIT;
}
else
{
break;
}
++dwIndex;
}
EXIT:
RegCloseKey(hkProfileList);
if (lRes == ERROR_SUCCESS)
{
return TRUE;
}
FreeUsersReg(phkUsers, cUsers);
return FALSE;
}
VOID
FreeUsersReg(
USER_HIVE_KEY* phkUsers,
DWORD cUsers
)
{
DWORD dw;
// Close all the open keys.
for (dw = 0; dw < cUsers; ++dw)
{
RegCloseKey(phkUsers[dw].hkUser);
RegCloseKey(phkUsers[dw].hkUserClasses);
}
delete [] phkUsers;
for (dw = 0; dw < g_cLoadedKeys; ++dw)
{
// Unloaded the keys we had to load under HKEY_USERS.
RegUnLoadKey(HKEY_USERS, g_wszLoadedKeys[dw]);
delete [] g_wszLoadedKeys[dw];
}
delete [] g_wszLoadedKeys;
// Disable the "Restore files and directories" privilege.
AdjustPrivilege(SE_RESTORE_NAME, FALSE);
}
//
// Registry utilies.
//
HKEY g_hkRedirectRoot = NULL;
HKEY g_hkCurrentUserClasses = NULL;
/*++
Function Description:
We only return TRUE if it's one of the predefined keys we are interested in.
We don't redirect the HKEY_USERS and HKEY_PERFORMANCE_DATA keys.
Arguments:
IN hKey - the key handle.
IN lpSubKey - subkey to check.
Return Value:
TRUE - It's one of our predefined keys.
FALSE - It's either a non-predefined key or a predefined key that we are not
interested in.
History:
03/27/2001 maonis Created
--*/
BOOL
IsPredefinedKey(
IN HKEY hKey
)
{
return (
hKey == HKEY_CLASSES_ROOT ||
hKey == HKEY_CURRENT_USER ||
hKey == HKEY_LOCAL_MACHINE);
}
LONG
GetRegRedirectKeys()
{
LONG lRet;
if (lRet = RegCreateKeyExW(
HKEY_CURRENT_USER,
LUA_REG_REDIRECT_KEY,
0,
0,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&g_hkRedirectRoot,
NULL) == ERROR_SUCCESS)
{
lRet = RegCreateKeyExW(
HKEY_CURRENT_USER,
LUA_SOFTWARE_CLASSES,
0,
0,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&g_hkCurrentUserClasses,
NULL);
}
return lRet;
}
#define IS_END_OF_COMPONENT(x) (*x == L'\\' || *x == L'\0')
/*++
Function Description:
Determines if 2 components match - one with wildcards and the other
without.
Note: this function is specialized for the LUA shims - the pattern
is all lowercase. If the components match, we advance the string
to the end of the component so when we do the whole path/file name
matching we don't need to go through the string twice.
Arguments:
IN ppPattern - component with wildcards.
IN ppString - component without wildcards.
Return Value:
TRUE - the components match.
FALSE - the components don't match.
History:
05/10/2001 maonis Created
--*/
BOOL
DoComponentsMatch(
LPCWSTR* ppwszPattern,
LPCWSTR* ppwszString)
{
LPCWSTR pwszPattern = *ppwszPattern;
LPCWSTR pwszString = *ppwszString;
LPCWSTR pwszSearch = NULL;
LPCWSTR pwszSearchPattern = NULL;
BOOL fIsSuccess = TRUE;
do
{
if (*pwszPattern == L'*')
{
while (*++pwszPattern == L'*');
if (IS_END_OF_COMPONENT(pwszPattern))
{
// Advanced the string to the end.
while (!IS_END_OF_COMPONENT(pwszString))
{
++pwszString;
}
goto EXIT;
}
pwszSearch = pwszString;
pwszSearchPattern = pwszPattern;
}
if (IS_END_OF_COMPONENT(pwszString))
{
break;
}
if ((*pwszPattern == L'?') ||
(*pwszPattern == *pwszString))
{
pwszPattern++;
}
else if (pwszSearch == NULL)
{
return FALSE;
}
else
{
pwszString = pwszSearch++;
pwszPattern = pwszSearchPattern;
}
++pwszString;
} while (!IS_END_OF_COMPONENT(pwszString));
if (*pwszPattern == L'*')
{
fIsSuccess = TRUE;
++pwszPattern;
}
else
{
fIsSuccess = IS_END_OF_COMPONENT(pwszPattern);
}
EXIT:
*ppwszPattern = pwszPattern;
*ppwszString = pwszString;
return fIsSuccess;
}
/*++
Function Description:
Determines if the item is in the redirect list.
Arguments:
IN pwszDirectory - All lowercase name.
IN cDirectory - The length of the directory.
IN pwszFile - The file name.
Return Value:
TRUE - the names match.
FALSE - the names don't match.
History:
11/30/2001 maonis Created
--*/
BOOL
DoesItemMatchRedirect(
LPCWSTR pwszItem,
const RITEM* pItem,
BOOL fIsDirectory
)
{
LPCWSTR pwszName = &(pItem->wszName[0]);
BOOL fMatchComponents;
if (pItem->fHasWC)
{
while (*pwszItem && *pwszName)
{
if (!DoComponentsMatch(&pwszName, &pwszItem))
{
return FALSE;
}
if (fIsDirectory)
{
if (!*pwszName)
{
//
// directory has exhausted. It's a match.
//
return TRUE;
}
if (!*pwszItem)
{
//
// directory hasn't exhausted but item has, no match.
//
return FALSE;
}
}
else
{
if (!*pwszItem)
{
//
// item has exhausted. It's a match.
//
return TRUE;
}
if (!*pwszName)
{
//
// item hasn't exhausted but file has, no match.
//
return FALSE;
}
}
++pwszName;
++pwszItem;
}
if (fIsDirectory)
{
return (!*pwszName);
}
else
{
return (!*pwszItem);
}
}
else
{
while (*pwszItem && *pwszName && *pwszItem == *pwszName)
{
++pwszItem;
++pwszName;
}
if (fIsDirectory)
{
return (!*pwszName && (!*pwszItem || *pwszItem == L'\\'));
}
else
{
return (!*pwszItem && (!*pwszName || *pwszName == L'\\'));
}
}
}
/*++
Function Description:
Parse the commandline argument for the LUA shims using ' ' as the delimiter.
If a token has spaces, use double quotes around it. Use this function the
same way you use wcstok except you don't have to specify the delimiter.
Arguments:
IN/OUT pwsz - the string to parse.
Return Value:
pointer to the next token.
History:
05/17/2001 maonis Created
--*/
LPWSTR GetNextToken(
LPWSTR pwsz
)
{
static LPWSTR pwszToken;
static LPWSTR pwszEndOfLastToken;
if (!pwsz)
{
pwsz = pwszEndOfLastToken;
}
// Skip the white space.
while (*pwsz && *pwsz == ' ')
{
++pwsz;
}
pwszToken = pwsz;
BOOL fInsideQuotes = 0;
while (*pwsz)
{
switch(*pwsz)
{
case L'"':
fInsideQuotes ^= 1;
if (fInsideQuotes)
{
++pwszToken;
}
case L' ':
if (!fInsideQuotes)
{
goto EXIT;
}
default:
++pwsz;
}
}
EXIT:
if (*pwsz)
{
*pwsz = L'\0';
pwszEndOfLastToken = ++pwsz;
}
else
{
pwszEndOfLastToken = pwsz;
}
return pwszToken;
}
/*++
Function Description:
Starting from the end going backward and find the first non whitespace
char. Set the whitespace char after it to '\0'.
Arguments:
IN pwsz - Beginning pointer.
Return Value:
None.
History:
06/27/2001 maonis Created
--*/
VOID TrimTrailingSpaces(
LPWSTR pwsz
)
{
if (pwsz)
{
DWORD cLen = wcslen(pwsz);
LPWSTR pwszEnd = pwsz + cLen - 1;
while (pwszEnd >= pwsz && (*pwszEnd == L' ' || *pwszEnd == L'\t'))
{
--pwszEnd;
}
*(++pwszEnd) = L'\0';
}
}
/*++
Function Description:
If the directory doesn't exist, we create it.
Arguments:
IN pwszDir - The name of the directory to create. The directory should NOT
start with \\?\ and it should haved a trailing slash.
Return Value:
TRUE - the directory was created.
FALSE - otherwise.
History:
05/17/2001 maonis Created
--*/
BOOL
CreateDirectoryOnDemand(
LPWSTR pwszDir
)
{
if (!pwszDir || !*pwszDir)
{
DPF("LUAUtils", eDbgLevelSpew,
"[CreateDirectoryOnDemand] Empty directory name - nothing to do");
return TRUE;
}
WCHAR* pwszStartPath = pwszDir;
WCHAR* pwszEndPath = pwszDir + wcslen(pwszDir);
WCHAR* pwszStartNext = pwszStartPath;
// Find the end of the next sub dir.
WCHAR* pwszEndNext;
DWORD dwAttrib;
while (pwszStartNext < pwszEndPath)
{
pwszEndNext = wcschr(pwszStartNext, L'\\');
if (pwszEndNext)
{
*pwszEndNext = L'\0';
if ((dwAttrib = GetFileAttributesW(pwszStartPath)) != -1)
{
// If the directory already exists, we probe its sub directory.
*pwszEndNext = L'\\';
pwszStartNext = pwszEndNext + 1;
continue;
}
if (!CreateDirectoryW(pwszStartPath, NULL))
{
DPF("LUAUtils", eDbgLevelError,
"[CreateDirectoryOnDemand] CreateDirectory %S failed: %d",
pwszStartPath,
GetLastError());
return FALSE;
}
*pwszEndNext = L'\\';
pwszStartNext = pwszEndNext + 1;
}
else
{
DPF("LUAUtils", eDbgLevelError,
"[CreateDirectoryOnDemand] Invalid directory name: %S", pwszStartPath);
return FALSE;
}
}
return TRUE;
}
/*++
Function Description:
Expand a string which might have enviorment variables embedded in it.
It gives you options to
1) Add a trailing slash if there's not one;
2) Create the directory if it doesn't exist;
3) Add the \\?\ prefix;
NOTE: The caller is responsible of free the memory using delete [].
Arguments:
IN pwszItem - string to expand.
OUT pcItemExpand - number of characters in the resulting string.
NOTE: this *includes* the terminating NULL.
IN fEnsureTrailingSlash - option 1.
IN fCreateDirectory - option 2.
IN fAddPrefix - option 3.
Return Value:
The expanded string or NULL if error occured.
History:
05/17/2001 maonis Created
--*/
LPWSTR
ExpandItem(
LPCWSTR pwszItem,
DWORD* pcItemExpand,
BOOL fEnsureTrailingSlash,
BOOL fCreateDirectory,
BOOL fAddPrefix
)
{
BOOL fIsSuccess = FALSE;
//
// Get the required length.
//
DWORD cLenExpand = ExpandEnvironmentStringsW(pwszItem, NULL, 0);
if (!cLenExpand)
{
DPF("LUAUtils", eDbgLevelError,
"[ExpandItem] Failed to get the required buffer size "
"when expanding %S: %d",
pwszItem, GetLastError());
return NULL;
}
if (fEnsureTrailingSlash)
{
++cLenExpand;
}
if (fAddPrefix)
{
cLenExpand += FILE_NAME_PREFIX_LEN;
}
LPWSTR pwszItemExpand = new WCHAR [cLenExpand];
if (!pwszItemExpand)
{
DPF("LUAUtils", eDbgLevelError,
"[ExpandItem] Error allocating %d WCHARs", cLenExpand);
return NULL;
}
LPWSTR pwszTemp = pwszItemExpand;
DWORD cTemp = cLenExpand;
if (fAddPrefix)
{
wcscpy(pwszItemExpand, FILE_NAME_PREFIX);
pwszTemp += FILE_NAME_PREFIX_LEN;
cTemp -= FILE_NAME_PREFIX_LEN;
}
if (!ExpandEnvironmentStringsW(pwszItem, pwszTemp, cTemp))
{
DPF("LUAUtils", eDbgLevelError,
"[ExpandItem] Failed to expand %S: %d",
pwszItem, GetLastError());
goto Cleanup;
}
// Ensure the trailing slash.
if (fEnsureTrailingSlash)
{
if (pwszItemExpand[cLenExpand - 3] != L'\\')
{
pwszItemExpand[cLenExpand - 2] = L'\\';
pwszItemExpand[cLenExpand - 1] = L'\0';
}
else
{
--cLenExpand;
}
if (fCreateDirectory &&
!CreateDirectoryOnDemand(pwszItemExpand + (fAddPrefix ? 4 : 0)))
{
DPF("LUAUtils", eDbgLevelError,
"[ExpandItem] Failed to create %S",
pwszItemExpand);
goto Cleanup;
}
}
*pcItemExpand = cLenExpand;
fIsSuccess = TRUE;
Cleanup:
if (!fIsSuccess)
{
delete [] pwszItemExpand;
pwszItemExpand = NULL;
}
return pwszItemExpand;
}
/*++
Function Description:
Given a delimiter character, returns the number of items in the string.
Return Value:
Number of items in the string.
History:
11/13/2001 maonis Created
--*/
DWORD
GetItemsCount(
LPCWSTR pwsz,
WCHAR chDelimiter
)
{
DWORD cItems = 0;
while (*pwsz) {
if (*pwsz == chDelimiter) {
++cItems;
}
++pwsz;
}
return (cItems + 1);
}