- 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.

svn path=/trunk/; revision=32127
This commit is contained in:
Colin Finck 2008-02-04 22:48:42 +00:00
parent 38c6c12999
commit 7356090407
5 changed files with 401 additions and 82 deletions

View file

@ -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 @@ 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()

View file

@ -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;

View file

@ -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];
};

View file

@ -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)
{

View file

@ -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;