Author: cfinck Date: Tue Feb 5 01:48:42 2008 New Revision: 32127
URL: http://svn.reactos.org/svn/reactos?rev=32127&view=rev Log: - Add a function MatchFileNamePattern for matching a file against a pattern (imported from Busybox under GPL2 or later license, heavily modified for our purposes, more information in the function comments) - Add support for multiple search criterias, which are actually checked. This makes it possible to pass parameters like "*.rbuild *.txt" to the cabman command line for adding, displaying and extracting files in a cabinet. - Overhaul CreateSimpleCabinet, make it able to add multiple files to the cabinet using the new search criteria functions. - Fix some comments and indentation here and there.
Modified: trunk/reactos/tools/cabman/cabinet.cxx trunk/reactos/tools/cabman/cabinet.h trunk/reactos/tools/cabman/cabman.h trunk/reactos/tools/cabman/dfp.cxx trunk/reactos/tools/cabman/main.cxx
Modified: trunk/reactos/tools/cabman/cabinet.cxx URL: http://svn.reactos.org/svn/reactos/trunk/reactos/tools/cabman/cabinet.cxx?re... ============================================================================== --- trunk/reactos/tools/cabman/cabinet.cxx (original) +++ trunk/reactos/tools/cabman/cabinet.cxx Tue Feb 5 01:48:42 2008 @@ -18,6 +18,9 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#if !defined(WIN32) +# include <dirent.h> +#endif #if defined(__FreeBSD__) || defined(__APPLE__) # include <sys/stat.h> #endif // __FreeBSD__ @@ -315,10 +318,12 @@ 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; @@ -509,6 +514,82 @@ ConvertPath(DestPath, false); if (strlen(DestPath) > 0) 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) @@ -821,19 +902,16 @@ }
-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 @@ * Status of operation */ { + bool bFound = false; + PSEARCH_CRITERIA Criteria; ULONG Status;
if (RestartSearch) @@ -867,7 +947,32 @@ 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 @@ 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::DestroyFileNodes() /* * FUNCTION: Destroys file nodes - * ARGUMENTS: - * FolderNode = Pointer to folder node */ { PCFFILE_NODE PrevNode; @@ -2641,8 +2889,8 @@
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::ReadBlock(void* Buffer, - ULONG Size, - PULONG BytesRead) + ULONG Size, + PULONG BytesRead) /* * FUNCTION: Read a block of data from file * ARGUMENTS: @@ -2716,6 +2964,67 @@ if (!ReadFileData(FileHandle, Buffer, Size, BytesRead)) return CAB_STATUS_INVALID_CAB; 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&... + */ +{ + 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
Modified: trunk/reactos/tools/cabman/cabinet.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/tools/cabman/cabinet.h?rev=... ============================================================================== --- trunk/reactos/tools/cabman/cabinet.h (original) +++ trunk/reactos/tools/cabman/cabinet.h Tue Feb 5 01:48:42 2008 @@ -224,13 +224,18 @@ 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 @@ /* 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 @@ 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 @@ PCFDATA_NODE CurrentDataNode; PCFFILE_NODE FileListHead; PCFFILE_NODE FileListTail; + PSEARCH_CRITERIA CriteriaListHead; + PSEARCH_CRITERIA CriteriaListTail; CCABCodec *Codec; LONG CodecId; bool CodecSelected;
Modified: trunk/reactos/tools/cabman/cabman.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/tools/cabman/cabman.h?rev=3... ============================================================================== --- trunk/reactos/tools/cabman/cabman.h (original) +++ trunk/reactos/tools/cabman/cabman.h Tue Feb 5 01:48:42 2008 @@ -27,7 +27,6 @@ private: void Usage(); bool CreateCabinet(); - bool CreateSimpleCabinet(); bool DisplayCabinet(); bool ExtractFromCabinet(); /* Event handlers */ @@ -39,7 +38,6 @@ bool ProcessAll; ULONG Mode; bool PromptOnOverwrite; - char Location[MAX_PATH]; char FileName[MAX_PATH]; };
Modified: trunk/reactos/tools/cabman/dfp.cxx URL: http://svn.reactos.org/svn/reactos/trunk/reactos/tools/cabman/dfp.cxx?rev=32... ============================================================================== --- trunk/reactos/tools/cabman/dfp.cxx (original) +++ trunk/reactos/tools/cabman/dfp.cxx Tue Feb 5 01:48:42 2008 @@ -391,7 +391,7 @@
if (!InfFileOnly) { - printf("\nWriting cabinet. This may take a while...\n\n"); + printf("\nWriting cabinet. This may take a while...\n\n");
if (DiskCreated) {
Modified: trunk/reactos/tools/cabman/main.cxx URL: http://svn.reactos.org/svn/reactos/trunk/reactos/tools/cabman/main.cxx?rev=3... ============================================================================== --- trunk/reactos/tools/cabman/main.cxx (original) +++ trunk/reactos/tools/cabman/main.cxx Tue Feb 5 01:48:42 2008 @@ -172,6 +172,7 @@ ProcessAll = false; InfFileOnly = false; Mode = CM_MODE_DISPLAY; + FileName[0] = 0; }
@@ -190,8 +191,8 @@ { 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 @@ 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 @@ } else { - if ((FoundCabinet) || (Mode == CM_MODE_CREATE)) - { - /* FIXME: There may be many of these if Mode != CM_MODE_CREATE */ - strcpy(FileName, argv[i]); + if(Mode == CM_MODE_CREATE) + { + 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 @@
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; }
@@ -396,43 +417,6 @@
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() /* @@ -448,7 +432,7 @@ { printf("Cabinet %s\n\n", GetCabinetName());
- if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) + if (FindFirst(&Search) == CAB_STATUS_SUCCESS) { do { @@ -466,6 +450,8 @@ } } while (FindNext(&Search) == CAB_STATUS_SUCCESS); } + + DestroySearchCriteria();
if (FileCount > 0) { if (FileCount == 1) @@ -503,6 +489,7 @@ * FUNCTION: Extract file(s) from cabinet */ { + bool bRet = true; CAB_SEARCH Search; ULONG Status;
@@ -510,34 +497,46 @@ { 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 @@ { 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;