// // Utility.cc // // Utility functions for BuildDistribution. // // (C) Copyright 2010 ARM Research Labs, LLC See // www.armresearch.com for the copyright terms. // // $Id$ // #include #include #include #include #include #include #include #include #include "Utility.h" static DWORD LastError = 0; // Last value from GetLastError(). Needed because // GetLastError() doesn't return the correct value // after an exception is thrown. void OutputUsage(std::string DefaultTempPath) { std::cout << "\n" "Usage:\n" "\n" " BuildDistribution -d DistPath -t TempPath\n" "\n" " where DistPath is the path of the distribution\n" " (e.g. SNFMultiSDK_Windows_3.1, and TempPath is the name of the\n" " directory to use for temporary storage. TempPath must exist.\n" " The default value of TempPath is '" << DefaultTempPath << "'.\n" ; } void OutputHelp() { std::cout << "\n" "The program does the following:\n" "\n" " 1) For each directory DIR that appears in DistPath and also in\n" " DistPath\\..:\n" "\n" " a) Delete the contents of DistPath/DIR, except for .svn.\n" "\n" " b) Copy the contents of DistPath\\..\\DIR to DistPath\\DIR, except\n" " for .svn.\n" "\n" " 2) Create a temporary directory TempPath/DistName, where DistName is\n" " the directory name of DistPath (e.g. if DistName is\n" " c:\\dir1\\dir2\\SNFMultiSDK_Windows_3.1, then DistName is\n" " SNFMultiSDK_Windows_3.1).\n" "\n" " 3) For each directory DIR that appears in DistPath and also in\n" " DistPath\\..:\n" "\n" " a) Copy all files from DistPath/DIR to TempPath/DistName, except\n" " for .svn.\n\n" ; } void OutputErrorMessage(std::string Message) { LPVOID lpMsgBuf; if (LastError != 0) { FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, LastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); std::cerr << Message << ": " << (LPCSTR) lpMsgBuf; } else { std::cerr << Message << "\n"; } } /// Case-insensitive search for a specified string in a container of strings. // // \param[in] Needle is the string to search for. // // \param[in] Haystack is the container of strings to search. // // \returns true if Needle is in Haystack (case-insensitive // comparison), false otherwise. // bool IsInContainer(std::string Needle, StringContainer Haystack); /// Get a list of directories in a specified path. // // This function returns a list of directories in the specified path, // excluding a specified list of names. // // \param[in] DistPath is the path to search. // // \param[in] ExcludeName contains the names to exclude. // // \returns a list of directory names in the specified path, excluding // names in ExcludeNames. // StringContainer GetDirectoryList(std::string DistPath, StringContainer ExcludeName); /// Get a list of files in a specified path. // // This function returns a list of files in the specified path, // excluding a specified list of names. // // \param[in] DistPath is the path to search. // // \param[in] ExcludeName contains the names to exclude. // // \returns a list of file names in the specified path, // excluding names in ExcludeNames. // StringContainer GetFileList(std::string DistPath, StringContainer ExcludeName); /// Update the files in one directory. // // \param[in] SourceDir is the name of the directory that contains the // files to copy from. // // \param[in] DestDir is the name of the directory to clear and copy // files to. // // \param[in] ExcludeName contains the names of the files to not // delete or copy. // // This function: // // 1) Deletes the files in DestDir, except for ExcludeName and directories. // // 2) If a directory in exists in both SourceDir and DestDir, then // update that directory by invoking UpdateCommonDirectories(). // // 3) If a directory exists in DestDir but not in SourceDir, delete // that directory and all it's contents. // // 4) Copy the non-directory files SourceDir to DestDir, except for // ExcludeName. // void UpdateOneDirectory(std::string SourceDir, std::string DestDir, StringContainer ExcludeName); /// Delete a directory and all it's contents. // // This function recursively deletes the contents of a directory, and // then deletes the directory. // // \param[in] Path is the path to delete. // void DeletePath(std::string Path); void GetDirectoryNames(std::string &InputDistPath, std::string &DistSourcePath, std::string &DistParentPath, std::string &DistName) { DWORD RetVal = 0; TCHAR PathPart[MAX_PATH]; LPTSTR FilePart = NULL; RetVal = GetFullPathName(InputDistPath.c_str(), // Get the path name for the source. MAX_PATH, PathPart, &FilePart); LastError = GetLastError(); if (0 == RetVal) { throw(std::invalid_argument("Error determining directory names")); } DistSourcePath = PathPart; if ( (NULL != FilePart) && (0 != *FilePart) ) { // Extract the distribution name // (last component of the source path. DistName = FilePart; } else { throw(std::invalid_argument("Unable to determine distribution name")); } std::string::size_type DistNameIndex; // Get the parent directory of the // source directory. DistNameIndex = DistSourcePath.rfind("\\"); if (std::string::npos == DistNameIndex) { throw(std::invalid_argument("Unable to determine the name of the " "distribution parent directory")); } DistParentPath = DistSourcePath.substr(0, DistNameIndex); } bool IsInContainer(std::string Needle, StringContainer Haystack) { std::transform(Needle.begin(), Needle.end(), Needle.begin(), ::toupper); StringContainer::iterator iFile; for (iFile = Haystack.begin(); iFile != Haystack.end(); iFile++) { // Check each file in the container. std::transform(iFile->begin(), iFile->end(), iFile->begin(), ::toupper); if (*iFile == Needle) { return true; } } return false; } StringContainer GetDirectoryList(std::string DistPath, StringContainer ExcludeName) { DistPath += "\\*.*"; // Create filter string. WIN32_FIND_DATA FileData; HANDLE ListHandle; std::string FileName; ListHandle = FindFirstFile(DistPath.c_str(), &FileData); StringContainer DirName; // Container to hold directory // names of the distribution. if ( (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && // Is this a directory... (! IsInContainer(FileData.cFileName, ExcludeName)) ) { // ...and not an excluded file? DirName.push_back(FileData.cFileName); // Yes. Save the name. } while (true) { if (FindNextFile(ListHandle, &FileData)) { if ( (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && // Is this a directory... (! IsInContainer(FileData.cFileName, ExcludeName)) ) { // ...and not an excluded file? DirName.push_back(FileData.cFileName); // Yes. Save the name. } } else { LastError = GetLastError(); if (ERROR_NO_MORE_FILES == LastError) { break; } else { std::ostringstream temp; temp << "Error determining directory list of '" << DistPath << "'"; throw(std::runtime_error(temp.str())); } } } if (FindClose(ListHandle) == 0) { std::ostringstream Temp; LastError = GetLastError(); Temp << "***Error closing handle for " << DistPath; throw(std::runtime_error(Temp.str())); } return DirName; } StringContainer GetFileList(std::string DirPath, StringContainer ExcludeName) { std::string DirPathFilter; DirPathFilter = DirPath + "\\*.*"; // Create filter string. WIN32_FIND_DATA FileData; HANDLE ListHandle; ListHandle = FindFirstFile(DirPathFilter.c_str(), &FileData); StringContainer FileNameList; // Container to hold file names. std::string FileName; if ( !(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && // Is this not a directory... (! IsInContainer(FileData.cFileName, ExcludeName)) ) { // ...and not an excluded file? FileNameList.push_back(FileData.cFileName); // Yes. Save the name. } while (true) { if (FindNextFile(ListHandle, &FileData)) { if ( !(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && // Is this not a directory... (! IsInContainer(FileData.cFileName, ExcludeName)) ) { // ...and not an excluded file? FileNameList.push_back(FileData.cFileName); // Yes. Save the name. } } else { LastError = GetLastError(); if (ERROR_NO_MORE_FILES == LastError) { break; } else { std::ostringstream temp; temp << "Error determining directory list of '" << DirPath << "'"; throw(std::runtime_error(temp.str())); } } } if (FindClose(ListHandle) == 0) { std::ostringstream Temp; LastError = GetLastError(); Temp << "***Error closing handle for " << DirPath; throw(std::runtime_error(Temp.str())); } return FileNameList; } void UpdateOneDirectory(std::string SourceDir, std::string DestDir, StringContainer ExcludeName) { StringContainer DestDirFileList; // Files in DestDir. DestDirFileList = GetFileList(DestDir, ExcludeName); StringContainer::iterator iFile; std::string FileToDelete; for (iFile = DestDirFileList.begin(); // Remove the files. iFile != DestDirFileList.end(); iFile++) { if (! IsInContainer(*iFile, ExcludeName)) { // Check if this file is in ExcludeName. FileToDelete = DestDir + "\\"; // It is not. Delete it. FileToDelete += *iFile; if (DeleteFile(FileToDelete.c_str()) == 0) { std::ostringstream Temp; LastError = GetLastError(); Temp << "***Error deleting " << FileToDelete; throw(std::runtime_error(Temp.str())); } } } StringContainer SourceDirFileList; // Files in SourceDir. std::string SourceFile, DestFile; SourceDirFileList = GetFileList(SourceDir, ExcludeName); for (iFile = SourceDirFileList.begin(); // Copy the files from SourceDir. iFile != SourceDirFileList.end(); iFile++) { if (! IsInContainer(*iFile, ExcludeName)) { // Check if this file is in ExcludeName. SourceFile = SourceDir + "\\"; // It is not. Copy it. SourceFile += *iFile; DestFile = DestDir + "\\"; DestFile += *iFile; if (CopyFile(SourceFile.c_str(), DestFile.c_str(), true) == 0) { std::ostringstream Temp; LastError = GetLastError(); Temp << "Error copying " << SourceFile << " to " << DestFile; throw(std::runtime_error(Temp.str())); } } } StringContainer SourceDirDirList, DestDirDirList; std::string SourceUpdateDir, DestUpdateDir; StringContainer SourceDirExtraDirList, DestDirExtraDirList; SourceDirDirList = GetDirectoryList(SourceDir, ExcludeName); DestDirDirList = GetDirectoryList(DestDir, ExcludeName); for (iFile = DestDirDirList.begin(); // Check that all directories in iFile != DestDirDirList.end(); // the destination directory are iFile++) { // also in the source directory. if (! IsInContainer(*iFile, SourceDirDirList)) { // Not in source directory? DestDirExtraDirList.push_back(*iFile); // It is not. This is an error. } } for (iFile = SourceDirDirList.begin(); // Check that all directories in iFile != SourceDirDirList.end(); // the source directory are also iFile++) { // in the destination directory. if (! IsInContainer(*iFile, DestDirDirList)) { // Not in source directory? SourceDirExtraDirList.push_back(*iFile); // It is not. This is an error. } } if (!SourceDirExtraDirList.empty() || !DestDirExtraDirList.empty()) { // Check for error. std::ostringstream Temp; if (!SourceDirExtraDirList.empty()) { // Extra directories in source. Temp << "***Error--There were directories in " << SourceDir << " that are not in " << DestDir << ":\n\t"; std::copy(SourceDirExtraDirList.begin(), SourceDirExtraDirList.end(), std::ostream_iterator(Temp, "\n\t")); } if (!DestDirExtraDirList.empty()) { // Extra directories in destination. Temp << "***Error--There were directories in " << DestDir << " that are not in " << SourceDir << ":\n\t"; std::copy(DestDirExtraDirList.begin(), DestDirExtraDirList.end(), std::ostream_iterator(Temp, "\n\t")); } throw(std::runtime_error(Temp.str())); } for (iFile = DestDirDirList.begin(); // Update each directory. iFile != DestDirDirList.end(); iFile++) { SourceUpdateDir = SourceDir + "\\"; SourceUpdateDir += *iFile; DestUpdateDir = DestDir + "\\"; DestUpdateDir += *iFile; UpdateOneDirectory(SourceUpdateDir, DestUpdateDir, ExcludeName); } } void UpdateCommonDirectories(std::string DistPath, std::string DistParentPath, StringContainer ExcludeName) { StringContainer DistDirList, DistParentDirList; DistDirList = GetDirectoryList(DistPath, ExcludeName); // Distribution directory names. DistParentDirList = GetDirectoryList(DistParentPath, ExcludeName); // Parent of distribution directory names. #if 0 // DEBUG. std::cout << "Directory list of distribution--\n"; std::copy(DistDirList.begin(), DistDirList.end(), std::ostream_iterator(std::cout, "\n")); std::cout << "\nDirectory list of distribution parent--\n"; std::copy(DistParentDirList.begin(), DistParentDirList.end(), std::ostream_iterator(std::cout, "\n")); // END OF DEBUG. #endif StringContainer::iterator iDistDir; // Update direcotries with the same name. for (iDistDir = DistDirList.begin(); iDistDir != DistDirList.end(); iDistDir++) { std::string SourceDir, DestDir; if (IsInContainer(*iDistDir, DistParentDirList)) { // Check if this is a common directory. SourceDir = DistParentPath + "\\"; // This is a common directory. SourceDir += *iDistDir; DestDir = DistPath + "\\"; DestDir += *iDistDir; UpdateOneDirectory(SourceDir, DestDir, ExcludeName); } } return; } void PrepareTempDir(std::string TempPath) { WIN32_FIND_DATA FileData; DWORD LastError; HANDLE DirHandle; std::ostringstream Temp; DirHandle = FindFirstFile(TempPath.c_str(), &FileData); // Check whether the directory exists. if (INVALID_HANDLE_VALUE == DirHandle) { // Directory doesn't exist. if (!CreateDirectory(TempPath.c_str(), NULL)) { LastError = GetLastError(); Temp << "***Error creating the temporary directory " << TempPath; throw(std::runtime_error(Temp.str())); } return; } // If the path is a file, delete it and create a new directory. if ( !(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (CloseHandle(DirHandle) != 0) { LastError = GetLastError(); Temp << "***Error closing handle for " << TempPath; throw(std::runtime_error(Temp.str())); } if (DeleteFile(TempPath.c_str()) != 0) { LastError = GetLastError(); Temp << "***Error deleting " << TempPath; throw(std::runtime_error(Temp.str())); } PrepareTempDir(TempPath); return; } if (CloseHandle(DirHandle) != 0) { LastError = GetLastError(); Temp << "***Error closing handle for " << TempPath; throw(std::runtime_error(Temp.str())); } DeletePath(TempPath); // TempPath is a directory. Delete it. PrepareTempDir(TempPath); // Create an empty directory. } void DeletePath(std::string Path) { StringContainer DirList, FileList, ExcludeName; StringContainer::iterator iName; std::string NameToDelete; ExcludeName.push_back("."); ExcludeName.push_back(".."); DirList = GetDirectoryList(Path, ExcludeName); FileList = GetFileList(Path, ExcludeName); for (iName = FileList.begin(); // Delete each file. iName != FileList.end(); iName++) { NameToDelete = Path + "\\"; NameToDelete += *iName; if (DeleteFile(NameToDelete.c_str()) == 0) { std::ostringstream Temp; LastError = GetLastError(); Temp << "***Error deleting " << NameToDelete << " in " << Path; throw(std::runtime_error(Temp.str())); } } for (iName = DirList.begin(); // Delete each directory. iName != DirList.end(); iName++) { NameToDelete = Path + "\\"; NameToDelete += *iName; DeletePath(NameToDelete); } if (RemoveDirectory(Path.c_str()) == 0) { std::ostringstream Temp; LastError = GetLastError(); Temp << "***Error removing directory " << Path; throw(std::runtime_error(Temp.str())); } } void CopyDirectory(std::string SourcePath, std::string DestPath, StringContainer ExcludeName) { StringContainer DirList, FileList; StringContainer::iterator iName; std::string SourceName, DestName; BOOL FailIfExists = true; DirList = GetDirectoryList(SourcePath, ExcludeName); FileList = GetFileList(SourcePath, ExcludeName); for (iName = FileList.begin(); // Copy each file. iName != FileList.end(); iName++) { SourceName = SourcePath + "\\"; SourceName += *iName; DestName = DestPath + "\\"; DestName += *iName; if (!CopyFile(SourceName.c_str(), DestName.c_str(), FailIfExists)) { std::ostringstream Temp; LastError = GetLastError(); Temp << "Error copying " << *iName << " to " << DestPath; throw(std::runtime_error(Temp.str())); } } for (iName = DirList.begin(); // Copy each directory. iName != DirList.end(); iName++) { SourceName = SourcePath + "\\"; SourceName += *iName; DestName = DestPath + "\\"; DestName += *iName; if (!CreateDirectory(DestName.c_str(), NULL)) { // Create the directory. std::ostringstream Temp; LastError = GetLastError(); Temp << "***Error creating the temporary directory " << DestName; throw(std::runtime_error(Temp.str())); } CopyDirectory(SourceName, DestName, ExcludeName); } }