|
|
//----------------------------------------------------------------------------
//
// Copyright (c) 1997-1999 Microsoft Corporation
// All rights reserved.
//
// File Name:
// pathsup.c
//
// Description:
// Some path support routines.
//
//----------------------------------------------------------------------------
#include "pch.h"
static TCHAR g_szSetupMgrFileExtensions[MAX_PATH + 1] = _T("");
//---------------------------------------------------------------------------
//
// Function: CleanTrailingSlashes
//
// Purpose: Cleans up trailing slashes off pathnames. This is a support
// routine for ConcatenatePaths().
//
// Arguments:
// LPTSTR lpBuffer - MAX_PATH buffer
//
// Returns: VOID
//
//---------------------------------------------------------------------------
static VOID CleanTrailingSlashes(LPTSTR lpBuffer) { TCHAR *p = lpBuffer + lstrlen(lpBuffer) - 1;
while ( p >= lpBuffer && *p == _T('\\') ) *p-- = _T('\0'); }
//---------------------------------------------------------------------------
//
// Function: CleanLeadingSlashes
//
// Purpose: Removes leading slashes from the given string.
//
// Arguments:
// LPTSTR lpStr - str to clean
//
// Returns:
// A pointer to the character after the run of back-slashes.
//
//---------------------------------------------------------------------------
static LPTSTR CleanLeadingSlashes(LPTSTR lpStr) { TCHAR *p=lpStr;
while ( *p && *p == TEXT('\\') ) p++;
return p; }
//---------------------------------------------------------------------------
//
// Function: ConcatenatePaths
//
// Purpose: This function cats path components together. It makes sure
// that there are not multiple slashes separating each item, and
// that there isn't a trailing back-slash.
//
// The last string passed must be NULL.
//
// Arguments:
// LPTSTR lpBuffer - MAX_PATH buffer
// ...
//
// Returns:
// TRUE if all is ok
// FALSE if resultant string is >= MAX_PATH chars
//
//---------------------------------------------------------------------------
BOOL __cdecl ConcatenatePaths(LPTSTR lpBuffer, ...) { LPTSTR lpString; va_list arglist; HRESULT hrCat;
va_start(arglist, lpBuffer); lpString = va_arg(arglist, LPTSTR);
while ( lpString != NULL ) {
if ( lstrlen(lpBuffer) + lstrlen(lpString) >= MAX_PATH ) return FALSE;
lpString = CleanLeadingSlashes(lpString); CleanTrailingSlashes(lpString); CleanTrailingSlashes(lpBuffer);
if ( lpBuffer[0] ) { hrCat=StringCchCat(lpBuffer, MAX_PATH, _T("\\")); hrCat=StringCchCat(lpBuffer, MAX_PATH, lpString); } else { lstrcpyn(lpBuffer, lpString, MAX_PATH); }
lpString = va_arg(arglist, LPTSTR); }
va_end(arglist);
return TRUE; }
//---------------------------------------------------------------------------
//
// Function: ParseDriveLetterOrUnc
//
// Purpose: Will parse past the \\srv\share\ or D:\ and return a pointer
// to the character after that mess.
//
// Returns: Pointer to the pathname 1 char past the volume descriptor,
// NULL if errors. GetLastError() will be valid when NULL
// is returned.
//
// Notes:
// - Only pass in fully qualified pathnames. Use MyGetFullPath().
//
//---------------------------------------------------------------------------
LPTSTR ParseDriveLetterOrUnc(LPTSTR lpFileName) { TCHAR *p=NULL;
//
// If path is of form \\srv\share\, get a pointer past the whole mess.
//
// Note we start at lpFileName+3 because "srv" (in this example) must
// be at least 1 character.
//
if ( lpFileName[0] == _T('\\') && lpFileName[1] == _T('\\') ) {
//
// Move past the computer name
//
p = lpFileName + 2;
while( *p != _T('\\') ) { if( *p == _T('\0') ) { SetLastError( ERROR_BAD_PATHNAME ); return( NULL ); }
p++;
}
p++;
//
// Scan past the share name
//
while( *p != _T('\\') ) { if( *p == _T('\0') ) { SetLastError( ERROR_BAD_PATHNAME ); return( NULL ); }
p++;
}
p++;
}
//
// Get past the D:\ if path is of that form
//
if ( towupper(lpFileName[0]) >= _T('A') && towupper(lpFileName[0]) <= _T('Z') && lpFileName[1] == _T(':') && lpFileName[2] == _T('\\') ) {
p = lpFileName + 3; }
//
// If we never set *p, then the path is not in a valid form.
//
if ( p == NULL ) { SetLastError(ERROR_BAD_PATHNAME); return NULL; }
return p; }
//---------------------------------------------------------------------------
//
// Function: GetComputerNameFromUnc
//
// Purpose: To strip out the computer name from a full UNC path.
//
// Example: \\computername\sharename\dir1\dir2 would return
// \\computername
//
// Arguments:
//
// szComputerName is assumed to be MAX_PATH length
//
// Returns: VOID
//
// Notes:
// - Only pass in fully qualified pathnames. Use MyGetFullPath().
//
//---------------------------------------------------------------------------
VOID GetComputerNameFromUnc( IN TCHAR *szFullUncPath, OUT TCHAR *szComputerName, IN DWORD cbSize) {
TCHAR *pString;
AssertMsg( szFullUncPath[0] == _T('\\') && szFullUncPath[1] == _T('\\'), "szFullUncPath is not a well formed net path" );
lstrcpyn( szComputerName, szFullUncPath, cbSize );
pString = &(szComputerName[2]);
//
// Scan past the computer name
//
while( *pString != _T('\\') ) { if( *pString == _T('\0') ) { AssertMsg( FALSE, "Bad UNC path"); return; }
pString++;
}
*pString = _T('\0');
}
//---------------------------------------------------------------------------
//
// Function: GetComputerAndShareNameFromUnc
//
// Purpose: To strip out the computer and share name from a full UNC path.
//
// Example: \\computername\sharename\dir1\dir2 would return
// \\computername\sharename
//
// Arguments:
//
// szComputerAndShareName is assumed to be MAX_PATH length
//
// Returns: VOID
//
// Notes:
// - Only pass in fully qualified pathnames. Use MyGetFullPath().
//
//---------------------------------------------------------------------------
VOID GetComputerAndShareNameFromUnc( IN TCHAR *szFullUncPath, OUT TCHAR *szComputerAndShareName, IN DWORD cbSize) {
TCHAR *pString;
AssertMsg( szFullUncPath[0] == _T('\\') && szFullUncPath[1] == _T('\\'), "szFullUncPath is not a well formed net path");
lstrcpyn( szComputerAndShareName, szFullUncPath, cbSize );
pString = &(szComputerAndShareName[2]);
//
// Scan past the computer name
//
while( *pString != _T('\\') ) { if( *pString == _T('\0') ) { AssertMsg( FALSE, "Bad UNC path"); return; }
pString++;
}
pString++;
//
// Scan past the share name
//
while( *pString != _T('\\') ) { if( *pString == _T('\0') ) { //
// already just the computer and share name so just return
//
return; }
pString++;
}
*pString = _T('\0');
}
//---------------------------------------------------------------------------
//
// Function: MyGetFullPath
//
// Purpose: Small wrapper on GetFullPathName(). It assumes the buffer
// is MAX_PATH.
//
// Returns:
// Pointer to filename part in the buffer, NULL if errors. The
// Win32 error code will be valid if fails.
//
// Notes:
// - This function should be called whenever obtaining a pathname
// from the user. Some of the other routines in this file
// require a fully qualified and cleaned up pathname (i.e. no
// trailing space and such).
//
//---------------------------------------------------------------------------
LPTSTR MyGetFullPath(LPTSTR lpFileName) { TCHAR Buffer[MAX_PATH], *lpFilePart;
lstrcpyn(Buffer, lpFileName, AS(Buffer));
if ( ! GetFullPathName(Buffer, MAX_PATH, lpFileName, &lpFilePart) ) { lpFilePart = NULL; return NULL; }
return lpFilePart; }
//---------------------------------------------------------------------------
//
// Function: GetPathFromPathAndFilename
//
// Purpose: To obtain the just the path from a string that contains a path
// and a filename.
//
// Arguments: LPTSTR lpPathAndFileName - the full path and filename
// TCHAR *szPath - buffer the path is to be returned in, it is
// assumed to be of MAX_PATH length
//
// Returns:
// Inside szBuffer is just the path from the input of the path and file
// name
// BOOL - TRUE on success, FALSE on failure
//
//
// Examples:
// lpPathAndFileName szBuffer
//
// c:\foo\bar.exe returns c:\foo
// c:\bar.exe c:\ //
//---------------------------------------------------------------------------
BOOL GetPathFromPathAndFilename( IN LPTSTR lpPathAndFileName, OUT TCHAR *szPath, IN DWORD cbSize ) {
INT iFileNameLength; INT iPathLength; INT iPathAndFileNameLength; TCHAR Buffer[MAX_PATH]; TCHAR *lpFilePart;
lstrcpyn(Buffer, lpPathAndFileName, AS(Buffer));
if ( ! GetFullPathName(Buffer, MAX_PATH, lpPathAndFileName, &lpFilePart) ) { return( FALSE ); }
iFileNameLength = lstrlen( lpFilePart );
iPathAndFileNameLength = lstrlen( lpPathAndFileName );
lstrcpyn( szPath, lpPathAndFileName, cbSize );
szPath[iPathAndFileNameLength - iFileNameLength] = _T('\0');
//
// At this point szPath looks like either c:\foo\ or c:\ // So trim the last back slash unless at the root
//
iPathLength = lstrlen( szPath );
if( iPathLength > 3 ) { szPath[iPathLength-1] = _T('\0'); }
return( TRUE );
}
//---------------------------------------------------------------------------
//
// Function: MyGetDiskFreeSpace
//
// Purpose: Gets the free space in bytes on the given drive and returns
// a LONGLONG (int64).
//
// The Win32 apis won't return an int64. Also, the Win32 apis
// require d:\. But this function will accept any fully
// qualified path.
//
// Arguments:
// LPTSTR - any fully qualified path
//
// Returns:
// LONGLONG - free space
//
//---------------------------------------------------------------------------
LONGLONG MyGetDiskFreeSpace(LPTSTR Drive) { BOOL bRet; DWORD nSectorsPerCluster, nBytesPerSector, nFreeClusters, nTotalClusters; TCHAR DriveBuffer[MAX_PATH];
LONGLONG FreeBytes; HRESULT hrCat;
if( _istalpha( Drive[0] ) ) { lstrcpyn(DriveBuffer, Drive, 4); DriveBuffer[3] = _T('\0'); } else if( Drive[0] == _T('\\') ) { GetComputerNameFromUnc( Drive, DriveBuffer, AS(DriveBuffer) );
hrCat=StringCchCat( DriveBuffer, AS(DriveBuffer), _T("\\") );
hrCat=StringCchCat( DriveBuffer, AS(DriveBuffer), WizGlobals.DistShareName );
hrCat=StringCchCat( DriveBuffer, AS(DriveBuffer), _T("\\") ); } else { AssertMsg(FALSE, "MyGetDiskFreeSpace failed, programming error, bad Drive parameter"); }
bRet = GetDiskFreeSpace( DriveBuffer, &nSectorsPerCluster, &nBytesPerSector, &nFreeClusters, &nTotalClusters );
if( bRet == FALSE ) { ReportErrorId( NULL, MSGTYPE_ERR | MSGTYPE_WIN32, IDS_ERR_UNABLE_TO_DETERMINE_FREE_SPACE, DriveBuffer );
return( 0 ); }
FreeBytes = (LONGLONG) nFreeClusters * (LONGLONG) nBytesPerSector * (LONGLONG) nSectorsPerCluster;
return( FreeBytes ); }
//---------------------------------------------------------------------------
//
// Function: MySetupQuerySpaceRequiredOnDrive
//
// Purpose: Uses setupapi disk-space-list and returns the LONGLONG
// of how many bytes are needed.
//
// Arguments:
// LPTSTR - any fully qualified path
//
// Returns:
// LONGLONG - free space
//
//---------------------------------------------------------------------------
LONGLONG MySetupQuerySpaceRequiredOnDrive(HDSKSPC hDiskSpace, LPTSTR Drive) { BOOL bRet; LONGLONG llRequiredSpace; TCHAR DriveBuffer[MAX_PATH];
if( _istalpha( Drive[0] ) ) { lstrcpyn(DriveBuffer, Drive, 3); DriveBuffer[2] = _T('\0'); } else if( Drive[0] == _T('\\') ) {
GetComputerAndShareNameFromUnc( Drive, DriveBuffer, AS(DriveBuffer) );
} else { AssertMsg(FALSE, "SetupQuerySpaceRequiredOnDrive failed, programming error, bad Drive parameter"); }
bRet = SetupQuerySpaceRequiredOnDrive( hDiskSpace, DriveBuffer, &llRequiredSpace, NULL, 0);
AssertMsg(bRet, "SetupQuerySpaceRequiredOnDrive failed, programming error");
return llRequiredSpace; }
//---------------------------------------------------------------------------
//
// Function: IsPathOnLocalDiskDrive
//
// Purpose: Determines if the path is on a local disk drive or not.
//
// Arguments:
// LPTSTR lpPath - fully qualified path
//
// Returns: BOOL
//
//---------------------------------------------------------------------------
BOOL IsPathOnLocalDiskDrive(LPCTSTR lpPath) { UINT nDriveType; TCHAR szDrivePath[MAX_PATH + 1];
//
// Use GetDriveType to determine if the path is a local or a network path
//
lstrcpyn( szDrivePath, lpPath, AS(szDrivePath) );
if( szDrivePath[0] != _T('\\') ) {
//
// Truncate the path to the root dir
//
szDrivePath[3] = _T('\0');
}
nDriveType = GetDriveType( szDrivePath );
if( nDriveType == DRIVE_REMOTE ) { return( FALSE ); } else { return( TRUE ); }
}
//---------------------------------------------------------------------------
//
// Function: EnsureDirExists
//
// Purpose: Function that will iteratively create the given directory
// by creating each piece of the pathname if necessary.
//
// Arguments:
// LPTSTR lpDirName - dir name
//
// Returns: BOOL
//
// Notes:
// - This function requires a fully qualified pathname. Translate
// pathnames using MyGetFullPath() first.
//
// - The Win32 error code will be valid upon failure.
//
//---------------------------------------------------------------------------
BOOL EnsureDirExists(LPTSTR lpDirName) { BOOL bRestoreSlash; DWORD dwAttribs; TCHAR *p;
//
// Parse off the D:\ or \\srv\shr\. The lasterror will already
// be set by ParseDriveLetterOrUnc() if any errors occured.
//
if ( (p = ParseDriveLetterOrUnc(lpDirName)) == NULL ) return FALSE;
//
// Now parse off each piece of the pathname and make sure dir exists
//
while ( *p ) {
// find next \ or end // of pathname
while ( *p && *p != _T('\\') ) p++;
bRestoreSlash = FALSE;
if ( *p == _T('\\') ) { *p = _T('\0'); bRestoreSlash = TRUE; }
// see if a file with that name already exists
dwAttribs = GetFileAttributes(lpDirName); if ( dwAttribs != (DWORD) -1 && !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ) {
if ( bRestoreSlash ) *p = _T('\\');
SetLastError(ERROR_ALREADY_EXISTS); return FALSE; }
// create the dir and allow a failure if the dir already exists
if ( !CreateDirectory(lpDirName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS ) {
if ( bRestoreSlash ) *p = _T('\\');
return FALSE; }
if ( bRestoreSlash ) *p = _T('\\');
// advance to next piece of the pathname
p++; }
return TRUE; }
//---------------------------------------------------------------------------
//
// Function: DoesFolderExist
//
// Purpose: Checks if the given folder exists or not.
//
// Arguments:
// LPTSTR lpDirName - dir name
//
// Returns: BOOL
//
//---------------------------------------------------------------------------
BOOL DoesFolderExist(LPTSTR lpDirName) { DWORD dwAttribs = GetFileAttributes(lpDirName);
if ( dwAttribs == (DWORD) -1 ) return FALSE;
if ( !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
return TRUE; }
//---------------------------------------------------------------------------
//
// Function: DoesFileExist
//
// Purpose: Checks if the given file exists or not.
//
// Arguments:
// LPTSTR lpFileName - file name
//
// Returns: BOOL
//
//---------------------------------------------------------------------------
BOOL DoesFileExist(LPTSTR lpFileName) { DWORD dwAttribs = GetFileAttributes(lpFileName);
if ( dwAttribs == (DWORD) -1 ) return FALSE;
if ( dwAttribs & FILE_ATTRIBUTE_DIRECTORY ) return FALSE;
return TRUE; }
//---------------------------------------------------------------------------
//
// Function: DoesPathExist
//
// Purpose: Checks if the given path exists or not. It does not pay
// attention to whether it is a file or directory.
//
// Arguments:
// LPTSTR lpPathName - path name
//
// Returns: BOOL
//
//---------------------------------------------------------------------------
BOOL DoesPathExist(LPTSTR lpPathName) { DWORD dwAttribs = GetFileAttributes(lpPathName);
if ( dwAttribs == (DWORD) -1 ) return FALSE;
return TRUE; }
//---------------------------------------------------------------------------
//
// Function: ILFreePriv
//
// Purpose: Frees an ID list that some shell apis allocate with it's own
// special allocator.
//
// Arguments:
// LPITEMIDLIST pidl - pointer to shell specially alloced mem
//
// Returns: VOID
//
//---------------------------------------------------------------------------
VOID ILFreePriv(LPITEMIDLIST pidl) { LPMALLOC pMalloc;
if (pidl) { if ( NOERROR == SHGetMalloc(&pMalloc) ) { pMalloc->lpVtbl->Free(pMalloc, pidl); pMalloc->lpVtbl->Release(pMalloc); } } }
//
// Constants used for GetOpenFileName() and GetSaveFileName() calls that
// allow user to browse for an answer file.
//
//#define TEXT_FILE_FILTER _T("Text Files (*.txt)\0*.txt\0Remote Boot Files (*.sif)\0*.sif\0Sysprep Inf Files (*.inf)\0*.inf\0All Files (*.*)\0*.*\0")
#define TEXT_EXTENSION _T("txt")
//----------------------------------------------------------------------------
//
// Function: GetAnswerFileName
//
// Purpose: Function for the 'Browse' button on the SaveScript page and
// the NewOrEdit page.
//
// Arguments:
// HWND hwnd - calling window
// LPTSTR buffer - output, pass in a MAX_PATH buffer
//
// Returns:
// BOOL - success
//
//----------------------------------------------------------------------------
BOOL GetAnswerFileName(HWND hwnd, LPTSTR lpFileName, BOOL bSavingFile) { OPENFILENAME ofn; DWORD dwFlags; TCHAR PathBuffer[MAX_PATH]; INT iRet; HRESULT hrPrintf;
//
// If we haven't already loaded the resource strings, then load them now.
//
if( g_szSetupMgrFileExtensions[0] == _T('\0') ) {
TCHAR *StrTextFiles; TCHAR *StrRemoteBootFiles; TCHAR *StrSysprepFiles; TCHAR *StrAllFiles;
//
// Load the resource strings
//
StrTextFiles = AllocateString(NULL, IDS_TEXT_FILES); StrRemoteBootFiles = AllocateString(NULL, IDS_REMOTE_BOOT_FILES); StrSysprepFiles = AllocateString(NULL, IDS_SYSPREP_FILES); StrAllFiles = AllocateString(NULL, IDS_ALL_FILES);
//
// Build the text file filter string
//
//
// The question marks (?) are just placehoders for where the NULL char
// will be inserted.
//
hrPrintf=StringCchPrintf( g_szSetupMgrFileExtensions,AS(g_szSetupMgrFileExtensions), _T("%s (*.txt)?*.txt?%s (*.sif)?*.sif?%s (*.inf)?*.inf?%s (*.*)?*.*?"), StrTextFiles, StrRemoteBootFiles, StrSysprepFiles, StrAllFiles );
FREE(StrTextFiles); FREE(StrRemoteBootFiles); FREE(StrSysprepFiles); FREE(StrAllFiles);
ConvertQuestionsToNull( g_szSetupMgrFileExtensions );
}
if ( bSavingFile ) dwFlags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST; else dwFlags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
GetCurrentDirectory(MAX_PATH, PathBuffer);
ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hwnd; ofn.hInstance = NULL; ofn.lpstrFilter = g_szSetupMgrFileExtensions; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0L; ofn.nFilterIndex = 1; ofn.lpstrFile = lpFileName; ofn.nMaxFile = MAX_PATH; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = PathBuffer; ofn.lpstrTitle = NULL; ofn.Flags = dwFlags; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = TEXT_EXTENSION;
if ( bSavingFile ) iRet = GetSaveFileName(&ofn); else iRet = GetOpenFileName(&ofn);
if ( ! iRet ) return FALSE;
MyGetFullPath(lpFileName);
return TRUE; }
//----------------------------------------------------------------------------
//
// Function: ShowBrowseFolder
//
// Purpose: Displays a browse folder for the user to select a file from.
// Takes the headache out of making an OPENFILENAME struct and filling
// it up.
//
// Arguments:
// HWND hwnd - handle to the dialog box
// TCHAR *szFileFilter - string to display descriptions and extensions
// on the files
// TCHAR *szFileExtension - string that is the default extension for the file
// DWORD dwFlags - bit flags used to initialize the browse dialog
// TCHAR *szStartingPath - path the browse should start at
// TCHAR *szFileNameAndPath - path and filename the user selected
//
// Returns: Non-Zero - if user specified a file
// Zero - if user did not specify a file
//
//----------------------------------------------------------------------------
INT ShowBrowseFolder( IN HWND hwnd, IN TCHAR *szFileFilter, IN TCHAR *szFileExtension, IN DWORD dwFlags, IN TCHAR *szStartingPath, IN OUT TCHAR *szFileNameAndPath ) {
OPENFILENAME ofn;
ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hwnd; ofn.hInstance = NULL; ofn.lpstrFilter = szFileFilter; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0L; ofn.nFilterIndex = 1; ofn.lpstrFile = szFileNameAndPath; ofn.nMaxFile = MAX_PATH; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = szStartingPath; ofn.lpstrTitle = NULL; ofn.Flags = dwFlags; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = szFileExtension;
return( GetOpenFileName( &ofn ) );
}
//----------------------------------------------------------------------------
//
// Function: GetPlatform
//
// Purpose:
//
// Arguments: OUT TCHAR *pBuffer - buffer to copy the platform string to,
// assumed to be able to hold MAX_PATH chars
//
// Returns: VOID
//
//----------------------------------------------------------------------------
VOID GetPlatform( OUT TCHAR *pBuffer ) {
SYSTEM_INFO SystemInfo;
GetSystemInfo( &SystemInfo );
switch( SystemInfo.wProcessorArchitecture ) { case PROCESSOR_ARCHITECTURE_INTEL:
lstrcpyn( pBuffer, _T("i386"), MAX_PATH );
break;
case PROCESSOR_ARCHITECTURE_AMD64:
lstrcpyn( pBuffer, _T("amd64"), MAX_PATH );
break;
default:
lstrcpyn( pBuffer, _T("i386"), MAX_PATH );
AssertMsg( FALSE, "Unknown Processor. Can't set sysprep language files path." );
}
}
|