Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

794 lines
21 KiB

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1997.
//
// File: path.cxx
//
// Contents: Common routines for processing file and pathnames.
//
// History: 11-22-1996 DavidMun Created
//
//---------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include <regstr.h> // for app path reg key constant
#include "..\inc\common.hxx"
#include "..\inc\misc.hxx"
#include "..\inc\debug.hxx"
//
// Forward references
//
LPCTSTR
FindFirstTrailingSpace(LPCTSTR ptsz);
BOOL
FileExistsInPath(
LPTSTR ptszFilename,
LPCTSTR ptszPath,
LPTSTR ptszFoundFile,
ULONG cchFoundBuf);
//+--------------------------------------------------------------------------
//
// Function: ProcessApplicationName
//
// Synopsis: Canonicalize and search for [ptszFilename].
//
// Arguments: [ptszFilename] - must be at least MAX_PATH chars long
// [tszWorkingDir] - "" or a directory not ending in slash
//
// Returns: TRUE - a filename was found
// FALSE - no filename found
//
// Modifies: *[ptszFilename]
//
// History: 11-21-1996 DavidMun Created
// 06-03-1997 DavidMun Expand environment vars
//
// Notes: This function should only be called to process filenames
// on the local machine.
//
//---------------------------------------------------------------------------
BOOL
ProcessApplicationName(LPTSTR ptszFilename, LPCTSTR tszWorkingDir)
{
BOOL fFound = FALSE;
TCHAR tszFilenameWithExe[MAX_PATH];
//
// Use tszFilenameWithExe as a temporary buffer for preparing the string
// in ptszFilename. Get rid of lead/trail spaces and double quotes, then
// expand environment strings.
//
lstrcpy(tszFilenameWithExe, ptszFilename);
StripLeadTrailSpace(tszFilenameWithExe);
DeleteQuotes(tszFilenameWithExe);
ExpandEnvironmentStrings(tszFilenameWithExe, ptszFilename, MAX_PATH);
tszFilenameWithExe[0] = TEXT('\0');
ULONG cchFilename = lstrlen(ptszFilename);
//
// If the filename doesn't end with .exe, and the resulting string
// wouldn't be greater than MAX_PATH, create a version of the filename
// with .exe appended.
//
// Note this will prevent us from finding foo.exe.exe when we're given
// foo.exe, but the performance gained by excluding this case outweighs
// the value of completeness, since it's unlikely anyone would create
// such a filename.
//
if (cchFilename < MAX_PATH - 4)
{
LPTSTR ptszLastDot = _tcsrchr(ptszFilename, TEXT('.'));
if (!ptszLastDot || lstrcmpi(ptszLastDot, DOTEXE))
{
lstrcpy(tszFilenameWithExe, ptszFilename);
lstrcpy(&tszFilenameWithExe[cchFilename], DOTEXE);
}
}
do
{
//
// If the user specified path information (if there is a colon or
// backslash anywhere in the string), look for the file as
// specified or with .exe appended, but look nowhere else.
//
if (_tcspbrk(ptszFilename, TEXT(":\\")))
{
if (*tszFilenameWithExe)
{
fFound = FileExists(tszFilenameWithExe);
if (fFound)
{
lstrcpy(ptszFilename, tszFilenameWithExe);
}
}
if (!fFound)
{
fFound = FileExists(ptszFilename);
}
break;
}
//
// First try the working directory
//
TCHAR tszFoundFile[MAX_PATH] = TEXT("");
if (*tszWorkingDir)
{
if (*tszFilenameWithExe)
{
fFound = FileExistsInPath(tszFilenameWithExe,
tszWorkingDir,
tszFoundFile,
MAX_PATH);
}
if (!fFound)
{
fFound = FileExistsInPath(ptszFilename,
tszWorkingDir,
tszFoundFile,
MAX_PATH);
}
if (fFound)
{
lstrcpy(ptszFilename, tszFoundFile);
break;
}
}
//
// Next try using the app paths key
//
TCHAR tszAppPathVar[MAX_PATH_VALUE] = TEXT("");
TCHAR tszAppPathDefault[MAX_PATH] = TEXT("");
if (*tszFilenameWithExe)
{
GetAppPathInfo(tszFilenameWithExe,
tszAppPathDefault,
MAX_PATH,
tszAppPathVar,
MAX_PATH_VALUE);
}
if (!*tszAppPathDefault && !*tszAppPathVar)
{
GetAppPathInfo(ptszFilename,
tszAppPathDefault,
MAX_PATH,
tszAppPathVar,
MAX_PATH_VALUE);
}
//
// If there was a default value, try that
//
if (*tszAppPathDefault)
{
fFound = FileExists(tszAppPathDefault);
if (fFound)
{
lstrcpy(ptszFilename, tszAppPathDefault);
break;
}
//
// If there's room, concat .exe to the default and look for
// that
//
ULONG cchDefault = lstrlen(tszAppPathDefault);
if (cchDefault < MAX_PATH - 4)
{
lstrcat(tszAppPathDefault, DOTEXE);
fFound = FileExists(tszAppPathDefault);
if (fFound)
{
lstrcpy(ptszFilename, tszAppPathDefault);
break;
}
}
}
//
// If the app path key specified a value for the PATH variable,
// try looking in all the directories it specifies
//
if (*tszAppPathVar)
{
if (*tszFilenameWithExe)
{
fFound = FileExistsInPath(tszFilenameWithExe,
tszAppPathVar,
tszFoundFile,
MAX_PATH);
}
if (!fFound)
{
fFound = FileExistsInPath(ptszFilename,
tszAppPathVar,
tszFoundFile,
MAX_PATH);
}
if (fFound)
{
lstrcpy(ptszFilename, tszFoundFile);
break;
}
}
//
// Try looking along the system PATH variable
//
ULONG cchPath;
TCHAR tszSystemPath[MAX_PATH_VALUE] = TEXT("");
cchPath = GetEnvironmentVariable(TEXT("Path"),
tszSystemPath,
MAX_PATH_VALUE);
if (!cchPath || cchPath > MAX_PATH_VALUE)
{
break;
}
if (*tszFilenameWithExe)
{
fFound = FileExistsInPath(tszFilenameWithExe,
tszSystemPath,
tszFoundFile,
MAX_PATH);
}
if (!fFound)
{
fFound = FileExistsInPath(ptszFilename,
tszSystemPath,
tszFoundFile,
MAX_PATH);
}
if (fFound)
{
lstrcpy(ptszFilename, tszFoundFile);
}
} while (0);
return fFound;
}
//+--------------------------------------------------------------------------
//
// Function: IsLocalFilename
//
// Synopsis: Return TRUE if [tszFilename] represents a file on the local
// machine, FALSE otherwise.
//
// History: 1-31-1997 DavidMun Created
//
//---------------------------------------------------------------------------
BOOL
IsLocalFilename(LPCTSTR tszFilename)
{
if (!tszFilename || !*tszFilename)
{
return FALSE;
}
if (tszFilename[0] == TEXT('\\') && tszFilename[1] == TEXT('\\'))
{
//
// Find the length of the portion of the name belonging to the machine name
//
LPCTSTR ptszNextSlash = _tcschr(tszFilename + 2, TEXT('\\'));
if (!ptszNextSlash)
{
return FALSE;
}
DWORD cchMachineName = (DWORD)(ptszNextSlash - tszFilename - 2);
//
// Get the local machine name (both NetBIOS and FQDN) to compare with that passed in.
//
TCHAR tszLocalName[SA_MAX_COMPUTERNAME_LENGTH + 1];
DWORD cchLocalName = SA_MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerName(tszLocalName, &cchLocalName))
{
ERR_OUT("IsLocalFilename: GetComputerName", HRESULT_FROM_WIN32(GetLastError()));
return FALSE;
}
TCHAR tszFQDN[SA_MAX_COMPUTERNAME_LENGTH + 1];
DWORD cchFQDN = SA_MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, tszFQDN, &cchFQDN))
{
ERR_OUT("IsLocalFilename: GetComputerNameEx", HRESULT_FROM_WIN32(GetLastError()));
return FALSE;
}
//
// Return whether we have a match on the machine name portion.
// I'm assuming that we won't have a case where the NetBIOS name
// and the FQDN are the same length but different.
//
if (cchMachineName == cchLocalName)
{
return lstrcmpi(tszFilename + 2, tszLocalName) == 0;
}
else if (cchMachineName == cchFQDN)
{
return lstrcmpi(tszFilename + 2, tszFQDN) == 0;
}
else
{
// if the lengths didn't match, there's no need
// to even bother with a string comparison
return FALSE;
}
}
if (s_isDriveLetter(tszFilename[0]) && tszFilename[1] == TEXT(':'))
{
TCHAR tszRoot[] = TEXT("x:\\");
tszRoot[0] = tszFilename[0];
UINT uiType = GetDriveType(tszRoot);
if (uiType == DRIVE_REMOTE || uiType == 0 || uiType == 1)
{
return FALSE;
}
}
return TRUE;
}
//+--------------------------------------------------------------------------
//
// Function: StripLeadTrailSpace
//
// Synopsis: Delete leading and trailing spaces from [ptsz].
//
// History: 11-22-1996 DavidMun Created
//
//---------------------------------------------------------------------------
VOID
StripLeadTrailSpace(LPTSTR ptsz)
{
ULONG cchLeadingSpace = _tcsspn(ptsz, TEXT(" \t"));
ULONG cch = lstrlen(ptsz);
//
// If there are any leading spaces or tabs, move the string
// (including its nul terminator) left to delete them.
//
if (cchLeadingSpace)
{
MoveMemory(ptsz,
ptsz + cchLeadingSpace,
(cch - cchLeadingSpace + 1) * sizeof(TCHAR));
cch -= cchLeadingSpace;
}
//
// Concatenate at the first trailing space
//
LPTSTR ptszTrailingSpace = (LPTSTR) FindFirstTrailingSpace(ptsz);
if (ptszTrailingSpace)
{
*ptszTrailingSpace = TEXT('\0');
}
}
//+--------------------------------------------------------------------------
//
// Function: FindFirstTrailingSpace
//
// Synopsis: Return a pointer to the first trailing space in [ptsz].
//
// History: 11-22-1996 DavidMun Created
//
//---------------------------------------------------------------------------
LPCTSTR
FindFirstTrailingSpace(LPCTSTR ptsz)
{
LPCTSTR ptszFirstTrailingSpace = NULL;
LPCTSTR ptszCur;
for (ptszCur = ptsz; *ptszCur; ptszCur= NextChar(ptszCur))
{
if (*ptszCur == ' ' || *ptszCur == '\t')
{
if (!ptszFirstTrailingSpace)
{
ptszFirstTrailingSpace = ptszCur;
}
}
else if (ptszFirstTrailingSpace)
{
ptszFirstTrailingSpace = NULL;
}
}
return ptszFirstTrailingSpace;
}
//+--------------------------------------------------------------------------
//
// Function: DeleteQuotes
//
// Synopsis: Delete all instances of the double quote character from
// [ptsz].
//
// Arguments: [ptsz] - nul terminated string
//
// Modifies: *[ptsz]
//
// History: 11-21-1996 DavidMun Created
//
//---------------------------------------------------------------------------
VOID
DeleteQuotes(LPTSTR ptsz)
{
TCHAR *ptszLead;
TCHAR *ptszTrail;
//
// Move a lead and trail pointer through the buffer, copying from the lead
// to the trail whenever the character isn't one we're deleting (a double
// quote).
//
// Note: the "Lead" and "Trail" in ptszLead and ptszTrail do not refer
// to DBCS lead/trail bytes, rather that the ptszLead pointer can move
// ahead of ptszTrail when it is advanced past a double quote.
//
for (ptszTrail = ptszLead = ptsz;
*ptszLead;
ptszLead = NextChar(ptszLead))
{
//
// If the current char is a double quote, we want it deleted, so don't
// copy it and go on to the next char.
//
if (*ptszLead == TEXT('"'))
{
continue;
}
//
// ptszLead is pointing to a 'normal' character, i.e. not a double
// quote.
//
// It might be the first byte of a two-byte DBCS char if we are
// running on Win9x. Be sure to copy both bytes of such a character
// (We're using the terms Lead and Trail in two different senses
// here.)
//
if (IsLead(*ptszLead))
{
*ptszTrail++ = ptszLead[0];
*ptszTrail++ = ptszLead[1];
}
else
{
*ptszTrail++ = ptszLead[0];
}
}
*ptszTrail = TEXT('\0');
}
//+--------------------------------------------------------------------------
//
// Function: AddQuotes
//
// Synopsis: If there's room in the buffer, insert a quote as the first
// character and concat a quote as the last character.
//
// Arguments: [ptsz] - string to modify
// [cchBuf] - size of string's buffer, in chars
//
// History: 11-22-1996 DavidMun Created
//
//---------------------------------------------------------------------------
VOID
AddQuotes(LPTSTR ptsz, ULONG cchBuf)
{
ULONG cch = lstrlen(ptsz);
if (cch < cchBuf - 2)
{
MoveMemory(ptsz + 1, ptsz, cch * sizeof(TCHAR));
*ptsz = ptsz[cch + 1] = TEXT('"');
ptsz[cch + 2] = TEXT('\0');
}
}
//+---------------------------------------------------------------------------
//
// Function: FileExists
//
// Synopsis: Return TRUE if the specified file exists and is not a
// directory.
//
// Arguments: [ptszFileName] - filename to search for & modify
//
// Modifies: Filename portion of [ptszFileName].
//
// Returns: TRUE - file found
// FALSE - file not found or error
//
// History: 11-21-96 DavidMun Created
//
//----------------------------------------------------------------------------
BOOL
FileExists(LPTSTR ptszFileName)
{
TCHAR tszFullPath[MAX_PATH];
LPTSTR ptszFilePart;
ULONG cchFullPath = GetFullPathName(ptszFileName,
MAX_PATH,
tszFullPath,
&ptszFilePart);
if (cchFullPath && cchFullPath <= MAX_PATH)
{
lstrcpy(ptszFileName, tszFullPath);
}
ULONG flAttributes;
flAttributes = GetFileAttributes(ptszFileName);
// If we couldn't determine file's attributes, don't consider it found
if (flAttributes == 0xFFFFFFFF)
{
return FALSE;
}
// if file is actually a directory, it's unsuitable as a task, so fail
if (flAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
return FALSE;
}
// Get the filename sans trailing spaces
WIN32_FIND_DATA FindFileData;
HANDLE hFile = FindFirstFile(ptszFileName, &FindFileData);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
FindClose(hFile);
LPTSTR ptszLastSlash = _tcsrchr((LPTSTR)ptszFileName, TEXT('\\'));
if (ptszLastSlash)
{
lstrcpy(ptszLastSlash + 1, FindFileData.cFileName);
}
return TRUE;
}
//+--------------------------------------------------------------------------
//
// Function: FileExistsInPath
//
// Synopsis: Return TRUE if [ptszFilename] exists in path [ptszPath].
//
// Arguments: [ptszFilename] - file to look for
// [ptszPath] - semicolon delimited list of dirs
// [ptszFoundFile] - if found, [ptszDir]\[ptszFilename]
// [cchFoundBuf] - size in chars of [ptszFoundFile] buffer
//
// Returns: TRUE if file found in dir, FALSE otherwise
//
// Modifies: *[ptszFoundFile]
//
// History: 11-22-1996 DavidMun Created
//
// Notes: Note that by calling FileExists we ensure the found file
// is a file, not a directory.
//
//---------------------------------------------------------------------------
BOOL
FileExistsInPath(
LPTSTR ptszFilename,
LPCTSTR ptszPath,
LPTSTR ptszFoundFile,
ULONG cchFoundBuf)
{
ULONG cchCopied;
LPTSTR ptszFilePart;
cchCopied = SearchPath(ptszPath,
ptszFilename,
NULL,
cchFoundBuf,
ptszFoundFile,
&ptszFilePart);
if (cchCopied && cchCopied <= cchFoundBuf)
{
return FileExists(ptszFoundFile);
}
return FALSE;
}
#define MAX_KEY_LEN (ARRAY_LEN(REGSTR_PATH_APPPATHS) + MAX_PATH)
//+--------------------------------------------------------------------------
//
// Function: GetAppPathInfo
//
// Synopsis: Fill [ptszAppPathDefault] with the default value and
// [ptszAppPathVar] with the Path value in the
// [ptszFilename] application's key under the APPPATHS regkey.
//
// Arguments: [ptszFilename] - application name
// [ptszAppPathDefault] - if not NULL, filled with default value
// [cchDefaultBuf] - size of [ptszAppPathDefault] buffer
// [ptszAppPathVar] - if not NULL, filled with Path value
// [cchPathVarBuf] - size of [cchPathVarBuf] buffer
//
// Modifies: *[ptszAppPathDefault], *[ptszAppPathVar]
//
// History: 11-22-1996 DavidMun Created
//
// Notes: Both values are optional on the registry key, so if a
// requested value isn't found, it is set to "".
//
//---------------------------------------------------------------------------
VOID
GetAppPathInfo(
LPCTSTR ptszFilename,
LPTSTR ptszAppPathDefault,
ULONG cchDefaultBuf,
LPTSTR ptszAppPathVar,
ULONG cchPathVarBuf)
{
HKEY hkey = NULL;
TCHAR tszAppPathKey[MAX_KEY_LEN];
//
// Initialize out vars
//
if (ptszAppPathDefault)
{
ptszAppPathDefault[0] = TEXT('\0');
}
if (ptszAppPathVar)
{
ptszAppPathVar[0] = TEXT('\0');
}
//
// Build registry key name for this app
//
lstrcpy(tszAppPathKey, REGSTR_PATH_APPPATHS);
lstrcat(tszAppPathKey, TEXT("\\"));
lstrcat(tszAppPathKey, ptszFilename);
do
{
LRESULT lr;
lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
tszAppPathKey,
0,
KEY_QUERY_VALUE,
&hkey);
if (lr != ERROR_SUCCESS)
{
break;
}
//
// If the key could be opened, attempt to read requested values.
// Both are optional, so ignore errors.
//
DWORD cb;
DWORD dwType;
if (ptszAppPathDefault)
{
cb = cchDefaultBuf * sizeof(TCHAR);
lr = RegQueryValueEx(hkey,
NULL, // value name
NULL, // reserved
&dwType,
(LPBYTE) ptszAppPathDefault,
&cb);
if (lr == ERROR_SUCCESS)
{
schAssert(dwType == REG_SZ);
}
}
if (ptszAppPathVar)
{
cb = cchPathVarBuf * sizeof(TCHAR);
lr = RegQueryValueEx(hkey,
TEXT("Path"), // value name
NULL, // reserved
&dwType,
(LPBYTE) ptszAppPathVar,
&cb);
if (lr == ERROR_SUCCESS)
{
schAssert(dwType == REG_SZ);
}
}
} while (0);
if (hkey)
{
RegCloseKey(hkey);
}
}