diff --git a/reactos/tools/cabman/cabinet.cxx b/reactos/tools/cabman/cabinet.cxx index b76dd0acc35..c19d7bb4dfe 100755 --- a/reactos/tools/cabman/cabinet.cxx +++ b/reactos/tools/cabman/cabinet.cxx @@ -18,6 +18,9 @@ #include #include #include +#if !defined(WIN32) +# include +#endif #if defined(__FreeBSD__) || defined(__APPLE__) # include #endif // __FreeBSD__ @@ -315,10 +318,12 @@ CCabinet::CCabinet() CabinetReservedFileBuffer = NULL; CabinetReservedFileSize = 0; - FolderListHead = NULL; - FolderListTail = NULL; - FileListHead = NULL; - FileListTail = NULL; + FolderListHead = NULL; + FolderListTail = NULL; + FileListHead = NULL; + FileListTail = NULL; + CriteriaListHead = NULL; + CriteriaListTail = NULL; Codec = NULL; CodecId = -1; @@ -511,6 +516,82 @@ void CCabinet::SetDestinationPath(char* DestinationPath) NormalizePath(DestPath, MAX_PATH); } +ULONG CCabinet::AddSearchCriteria(char* SearchCriteria) +/* + * FUNCTION: Adds a criteria to the search criteria list + * ARGUMENTS: + * SearchCriteria = String with the search criteria to add + * RETURNS: + * Status of operation + */ +{ + PSEARCH_CRITERIA Criteria; + + // Add the criteria to the list of search criteria + Criteria = (PSEARCH_CRITERIA)AllocateMemory(sizeof(SEARCH_CRITERIA)); + if(!Criteria) + { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + Criteria->Prev = CriteriaListTail; + Criteria->Next = NULL; + + if(CriteriaListTail) + CriteriaListTail->Next = Criteria; + else + CriteriaListHead = Criteria; + + CriteriaListTail = Criteria; + + // Set the actual criteria string + Criteria->Search = (char*)AllocateMemory(strlen(SearchCriteria) + 1); + if (!Criteria->Search) + { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + strcpy(Criteria->Search, SearchCriteria); + + return CAB_STATUS_SUCCESS; +} + +void CCabinet::DestroySearchCriteria() +/* + * FUNCTION: Destroys the list with the search criteria + */ +{ + PSEARCH_CRITERIA Criteria; + PSEARCH_CRITERIA NextCriteria; + + Criteria = CriteriaListHead; + + while(Criteria) + { + NextCriteria = Criteria->Next; + + FreeMemory(Criteria->Search); + FreeMemory(Criteria); + + Criteria = NextCriteria; + } + + CriteriaListHead = NULL; + CriteriaListTail = NULL; +} + +bool CCabinet::HasSearchCriteria() +/* + * FUNCTION: Returns whether we have search criteria + * RETURNS: + * Whether we have search criteria or not. + */ +{ + return (CriteriaListHead != NULL); +} + bool CCabinet::SetCompressionCodec(char* CodecName) /* * FUNCTION: Selects the codec to use for compression @@ -821,19 +902,16 @@ void CCabinet::Close() } -ULONG CCabinet::FindFirst(char* FileName, - PCAB_SEARCH Search) +ULONG CCabinet::FindFirst(PCAB_SEARCH Search) /* * FUNCTION: Finds the first file in the cabinet that matches a search criteria * ARGUMENTS: - * FileName = Pointer to search criteria * Search = Pointer to search structure * RETURNS: * Status of operation */ { RestartSearch = false; - strncpy(Search->Search, FileName, MAX_PATH); Search->Next = FileListHead; return FindNext(Search); } @@ -848,6 +926,8 @@ ULONG CCabinet::FindNext(PCAB_SEARCH Search) * Status of operation */ { + bool bFound = false; + PSEARCH_CRITERIA Criteria; ULONG Status; if (RestartSearch) @@ -867,7 +947,32 @@ ULONG CCabinet::FindNext(PCAB_SEARCH Search) RestartSearch = false; } - /* FIXME: Check search criteria */ + /* Check each search criteria against each file */ + while(Search->Next) + { + // Some features (like displaying cabinets) don't require search criteria, so we can just break here. + // If a feature requires it, handle this in the ParseCmdline() function in "main.cxx". + if(!CriteriaListHead) + break; + + Criteria = CriteriaListHead; + + while(Criteria) + { + if(MatchFileNamePattern(Search->Next->FileName, Criteria->Search)) + { + bFound = true; + break; + } + + Criteria = Criteria->Next; + } + + if(bFound) + break; + + Search->Next = Search->Next->Next; + } if (!Search->Next) { @@ -1960,6 +2065,151 @@ ULONG CCabinet::AddFile(char* FileName) return CAB_STATUS_SUCCESS; } +bool CCabinet::CreateSimpleCabinet() +/* + * FUNCTION: Create a simple cabinet based on the files in the criteria list + */ +{ + bool bRet = false; + char* pszFile; + char szFilePath[MAX_PATH]; + char szFile[MAX_PATH]; + PSEARCH_CRITERIA Criteria; + ULONG Status; + +#if defined(WIN32) + HANDLE hFind; + WIN32_FIND_DATA FindFileData; +#else + DIR* dirp; + struct dirent* dp; + struct stat stbuf; +#endif + + // Initialize a new cabinet + Status = NewCabinet(); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT(MIN_TRACE, ("Cannot create cabinet (%u).\n", (UINT)Status)); + goto cleanup; + } + + // Add each file in the criteria list + Criteria = CriteriaListHead; + + while(Criteria) + { + // Store the file path with a trailing slash in szFilePath + pszFile = strrchr(Criteria->Search, '/'); + if(!pszFile) + pszFile = strrchr(Criteria->Search, '\\'); + + if(pszFile) + { + strncpy(szFilePath, Criteria->Search, pszFile - Criteria->Search + 1); + szFilePath[pszFile - Criteria->Search + 1] = 0; + } + else + szFilePath[0] = 0; + +#if defined(WIN32) + // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern + hFind = FindFirstFile(Criteria->Search, &FindFileData); + + // Don't stop if a search criteria is not found + if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND) + { + DPRINT(MIN_TRACE, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria->Search, (UINT)GetLastError())); + goto cleanup; + } + + do + { + if(!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + strcpy(szFile, szFilePath); + strcat(szFile, FindFileData.cFileName); + + Status = AddFile(szFile); + + if(Status != CAB_STATUS_SUCCESS) + { + DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status)); + goto cleanup; + } + } + } + while(FindNextFile(hFind, &FindFileData)); + + FindClose(hFind); +#else + // Unix: Use opendir/readdir to loop through all entries, stat to check if it's a file and MatchFileNamePattern to match the file against the pattern + if(szFilePath[0] == 0) + strcpy(szFilePath, "./"); + + dirp = opendir(szFilePath); + + if(dirp) + { + while( (dp = readdir(dirp)) ) + { + strcpy(szFile, szFilePath); + strcat(szFile, dp->d_name); + + if(stat(szFile, &stbuf) == 0) + { + if(stbuf.st_mode != S_IFDIR) + { + // As we added "./" to szFilePath above, szFile might contain "./test.txt" now and Criteria->Search "test.txt". + // Therefore they won't match using MatchFileNamePattern. By using pszFile here, we can avoid this problem. + if(szFile[0] == '.' && szFile[1] == '/') + pszFile = szFile + 2; + else + pszFile = szFile; + + if(MatchFileNamePattern(pszFile, Criteria->Search)) + { + Status = AddFile(szFile); + + if(Status != CAB_STATUS_SUCCESS) + { + DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status)); + goto cleanup; + } + } + } + } + else + { + DPRINT(MIN_TRACE, ("stat failed, error code is %i\n", errno)); + goto cleanup; + } + } + + closedir(dirp); + } + +#endif + + Criteria = Criteria->Next; + } + + Status = WriteDisk(false); + if (Status == CAB_STATUS_SUCCESS) + Status = CloseDisk(); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT(MIN_TRACE, ("Cannot write disk (%u).\n", (UINT)Status)); + goto cleanup; + } + + CloseCabinet(); + bRet = true; + +cleanup: + DestroySearchCriteria(); + return bRet; +} void CCabinet::SetMaxDiskSize(ULONG Size) /* @@ -2509,8 +2759,6 @@ void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode) void CCabinet::DestroyFileNodes() /* * FUNCTION: Destroys file nodes - * ARGUMENTS: - * FolderNode = Pointer to folder node */ { PCFFILE_NODE PrevNode; @@ -2641,8 +2889,8 @@ void CCabinet::DestroyDeletedFolderNodes() ULONG CCabinet::ComputeChecksum(void* Buffer, - ULONG Size, - ULONG Seed) + ULONG Size, + ULONG Seed) /* * FUNCTION: Computes checksum for data block * ARGUMENTS: @@ -2700,8 +2948,8 @@ ULONG CCabinet::ComputeChecksum(void* Buffer, ULONG CCabinet::ReadBlock(void* Buffer, - ULONG Size, - PULONG BytesRead) + ULONG Size, + PULONG BytesRead) /* * FUNCTION: Read a block of data from file * ARGUMENTS: @@ -2718,6 +2966,67 @@ ULONG CCabinet::ReadBlock(void* Buffer, return CAB_STATUS_SUCCESS; } +bool CCabinet::MatchFileNamePattern(char* FileName, char* Pattern) +/* + * FUNCTION: Matches a wildcard character pattern against a file + * ARGUMENTS: + * FileName = The file name to check + * Pattern = The pattern + * RETURNS: + * Whether the pattern matches the file + * + * COPYRIGHT: + * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version. + * Adapted from code written by Ingo Wilken. + * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup + */ +{ + char* retryPattern = NULL; + char* retryFileName = NULL; + char ch; + + while (*FileName || *Pattern) + { + ch = *Pattern++; + + switch (ch) + { + case '*': + retryPattern = Pattern; + retryFileName = FileName; + break; + + case '?': + if (*FileName++ == '\0') + return false; + + break; + + default: + if (*FileName == ch) + { + if (*FileName) + FileName++; + break; + } + + if (*FileName) + { + Pattern = retryPattern; + FileName = ++retryFileName; + break; + } + + return false; + } + + if (!Pattern) + return false; + } + + return true; +} + #ifndef CAB_READ_ONLY ULONG CCabinet::InitCabinetHeader() diff --git a/reactos/tools/cabman/cabinet.h b/reactos/tools/cabman/cabinet.h index 35c6e12119a..01f47a2455b 100755 --- a/reactos/tools/cabman/cabinet.h +++ b/reactos/tools/cabman/cabinet.h @@ -224,13 +224,18 @@ typedef struct _CFFILE_NODE PCFFOLDER_NODE FolderNode; // Folder this file belong to } CFFILE_NODE, *PCFFILE_NODE; +typedef struct _SEARCH_CRITERIA +{ + struct _SEARCH_CRITERIA *Next; // Pointer to next search criteria + struct _SEARCH_CRITERIA *Prev; // Pointer to previous search criteria + char* Search; // The actual search criteria +} SEARCH_CRITERIA, *PSEARCH_CRITERIA; typedef struct _CAB_SEARCH { - char Search[MAX_PATH]; // Search criteria - PCFFILE_NODE Next; // Pointer to next node - PCFFILE File; // Pointer to current CFFILE - char* FileName; // Current filename + PCFFILE_NODE Next; // Pointer to next node + PCFFILE File; // Pointer to current CFFILE + char* FileName; // Current filename } CAB_SEARCH, *PCAB_SEARCH; @@ -345,16 +350,25 @@ public: /* Closes the current open cabinet file */ void Close(); /* Locates the first file in the current cabinet file that matches a search criteria */ - ULONG FindFirst(char* FileName, PCAB_SEARCH Search); + ULONG FindFirst(PCAB_SEARCH Search); /* Locates the next file in the current cabinet file */ ULONG FindNext(PCAB_SEARCH Search); /* Extracts a file from the current cabinet file */ ULONG ExtractFile(char* FileName); /* Select codec engine to use */ void SelectCodec(LONG Id); - /* Returns if a codec engine is selected */ + /* Returns whether a codec engine is selected */ bool IsCodecSelected(); + /* Adds a search criteria for adding files to a simple cabinet, displaying files in a cabinet or extracting them */ + ULONG AddSearchCriteria(char* SearchCriteria); + /* Destroys the search criteria list */ + void DestroySearchCriteria(); + /* Returns whether we have search criteria */ + bool HasSearchCriteria(); + #ifndef CAB_READ_ONLY + /* Creates a simple cabinet based on the search criteria data */ + bool CreateSimpleCabinet(); /* Sets the codec to use for compression (based on a string value) */ bool SetCompressionCodec(char* CodecName); /* Creates a new cabinet file */ @@ -412,6 +426,7 @@ private: void DestroyDeletedFolderNodes(); ULONG ComputeChecksum(void* Buffer, ULONG Size, ULONG Seed); ULONG ReadBlock(void* Buffer, ULONG Size, PULONG BytesRead); + bool MatchFileNamePattern(char* FileName, char* Pattern); #ifndef CAB_READ_ONLY ULONG InitCabinetHeader(); ULONG WriteCabinetHeader(bool MoreDisks); @@ -455,6 +470,8 @@ private: PCFDATA_NODE CurrentDataNode; PCFFILE_NODE FileListHead; PCFFILE_NODE FileListTail; + PSEARCH_CRITERIA CriteriaListHead; + PSEARCH_CRITERIA CriteriaListTail; CCABCodec *Codec; LONG CodecId; bool CodecSelected; diff --git a/reactos/tools/cabman/cabman.h b/reactos/tools/cabman/cabman.h index 02d60f01ba2..ed7be30a8bb 100755 --- a/reactos/tools/cabman/cabman.h +++ b/reactos/tools/cabman/cabman.h @@ -27,7 +27,6 @@ public: private: void Usage(); bool CreateCabinet(); - bool CreateSimpleCabinet(); bool DisplayCabinet(); bool ExtractFromCabinet(); /* Event handlers */ @@ -39,7 +38,6 @@ private: bool ProcessAll; ULONG Mode; bool PromptOnOverwrite; - char Location[MAX_PATH]; char FileName[MAX_PATH]; }; diff --git a/reactos/tools/cabman/dfp.cxx b/reactos/tools/cabman/dfp.cxx index 9443cb7a0a0..df20c71b7cb 100755 --- a/reactos/tools/cabman/dfp.cxx +++ b/reactos/tools/cabman/dfp.cxx @@ -391,7 +391,7 @@ ULONG CDFParser::Parse() if (!InfFileOnly) { - printf("\nWriting cabinet. This may take a while...\n\n"); + printf("\nWriting cabinet. This may take a while...\n\n"); if (DiskCreated) { diff --git a/reactos/tools/cabman/main.cxx b/reactos/tools/cabman/main.cxx index 903ebeed2eb..1c2be936dbc 100755 --- a/reactos/tools/cabman/main.cxx +++ b/reactos/tools/cabman/main.cxx @@ -172,6 +172,7 @@ CCABManager::CCABManager() ProcessAll = false; InfFileOnly = false; Mode = CM_MODE_DISPLAY; + FileName[0] = 0; } @@ -190,8 +191,8 @@ void CCABManager::Usage() { printf("ReactOS Cabinet Manager\n\n"); printf("CABMAN [-D | -E] [-A] [-L dir] cabinet [filename ...]\n"); - printf("CABMAN -C dirfile [-I] [-RC file] [-P dir]\n"); - printf("CABMAN -S cabinet filename\n"); + printf("CABMAN [-M mode] -C dirfile [-I] [-RC file] [-P dir]\n"); + printf("CABMAN [-M mode] -S cabinet filename ...\n"); printf(" cabinet Cabinet file.\n"); printf(" filename Name of the file to extract from the cabinet.\n"); printf(" Wild cards and multiple filenames\n"); @@ -207,7 +208,7 @@ void CCABManager::Usage() printf(" -I Don't create the cabinet, only the .inf file.\n"); printf(" -L dir Location to place extracted or generated files\n"); printf(" (default is current directory).\n"); - printf(" -M Specify the compression method to use\n"); + printf(" -M mode Specify the compression method to use:\n"); printf(" raw - No compression\n"); printf(" mszip - MsZip compression (default)\n"); printf(" -N Don't create the .inf file, only the cabinet.\n"); @@ -351,10 +352,23 @@ bool CCABManager::ParseCmdline(int argc, char* argv[]) } else { - if ((FoundCabinet) || (Mode == CM_MODE_CREATE)) + if(Mode == CM_MODE_CREATE) { - /* FIXME: There may be many of these if Mode != CM_MODE_CREATE */ - strcpy(FileName, argv[i]); + if(FileName[0]) + { + printf("You may only specify one directive file!\n"); + return false; + } + else + { + // For creating cabinets, this argument is the path to the directive file + strcpy(FileName, argv[i]); + } + } + else if(FoundCabinet) + { + // For creating simple cabinets, displaying or extracting them, add the argument as a search criteria + AddSearchCriteria(argv[i]); } else { @@ -366,14 +380,21 @@ bool CCABManager::ParseCmdline(int argc, char* argv[]) if (ShowUsage) { - Usage(); - return false; + Usage(); + return false; } // Select MsZip by default for creating cabinets if( (Mode == CM_MODE_CREATE || Mode == CM_MODE_CREATE_SIMPLE) && !IsCodecSelected() ) SelectCodec(CAB_CODEC_MSZIP); + // Search criteria (= the filename argument) is necessary for creating a simple cabinet + if( Mode == CM_MODE_CREATE_SIMPLE && !HasSearchCriteria()) + { + printf("You have to enter input file names!\n"); + return false; + } + return true; } @@ -397,43 +418,6 @@ bool CCABManager::CreateCabinet() return (Status == CAB_STATUS_SUCCESS ? true : false); } - -bool CCABManager::CreateSimpleCabinet() -/* - * FUNCTION: Create cabinet - */ -{ - ULONG Status; - - Status = NewCabinet(); - if (Status != CAB_STATUS_SUCCESS) - { - DPRINT(MIN_TRACE, ("Cannot create cabinet (%u).\n", (UINT)Status)); - return false; - } - - Status = AddFile(FileName); - if (Status != CAB_STATUS_SUCCESS) - { - DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status)); - return false; - } - - Status = WriteDisk(false); - if (Status == CAB_STATUS_SUCCESS) - Status = CloseDisk(); - if (Status != CAB_STATUS_SUCCESS) - { - DPRINT(MIN_TRACE, ("Cannot write disk (%u).\n", (UINT)Status)); - return false; - } - - CloseCabinet(); - - return true; -} - - bool CCABManager::DisplayCabinet() /* * FUNCTION: Display cabinet contents @@ -448,7 +432,7 @@ bool CCABManager::DisplayCabinet() { printf("Cabinet %s\n\n", GetCabinetName()); - if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) + if (FindFirst(&Search) == CAB_STATUS_SUCCESS) { do { @@ -467,6 +451,8 @@ bool CCABManager::DisplayCabinet() } while (FindNext(&Search) == CAB_STATUS_SUCCESS); } + DestroySearchCriteria(); + if (FileCount > 0) { if (FileCount == 1) printf(" 1 file "); @@ -503,6 +489,7 @@ bool CCABManager::ExtractFromCabinet() * FUNCTION: Extract file(s) from cabinet */ { + bool bRet = true; CAB_SEARCH Search; ULONG Status; @@ -510,34 +497,46 @@ bool CCABManager::ExtractFromCabinet() { printf("Cabinet %s\n\n", GetCabinetName()); - if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) + if (FindFirst(&Search) == CAB_STATUS_SUCCESS) { do { - switch (Status = ExtractFile(Search.FileName)) { + switch (Status = ExtractFile(Search.FileName)) + { case CAB_STATUS_SUCCESS: break; case CAB_STATUS_INVALID_CAB: printf("Cabinet contains errors.\n"); - return false; + bRet = false; + break; case CAB_STATUS_UNSUPPCOMP: printf("Cabinet uses unsupported compression type.\n"); - return false; + bRet = false; + break; case CAB_STATUS_CANNOT_WRITE: printf("You've run out of free space on the destination volume or the volume is damaged.\n"); - return false; + bRet = false; + break; default: printf("Unspecified error code (%u).\n", (UINT)Status); - return false; + bRet = false; + break; } + + if(!bRet) + break; } while (FindNext(&Search) == CAB_STATUS_SUCCESS); + + DestroySearchCriteria(); } - return true; - } else + + return bRet; + } + else printf("Cannot open file: %s.\n", GetCabinetName()); return false; @@ -555,19 +554,15 @@ bool CCABManager::Run() { case CM_MODE_CREATE: return CreateCabinet(); - break; case CM_MODE_DISPLAY: return DisplayCabinet(); - break; case CM_MODE_EXTRACT: return ExtractFromCabinet(); - break; case CM_MODE_CREATE_SIMPLE: return CreateSimpleCabinet(); - break; default: break;