//---------------------------------------------------------------------------- // // 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." ); } }