|
|
#include <windows.h>
#include <fcntl.h>
//
// lz headers
//
#include "lz_common.h"
#include "lz_buffers.h"
#include "lz_header.h"
//
// diamond headers
//
#include <diamondd.h>
#include "mydiam.h"
HFDI FdiContext; ERF FdiError;
INT DiamondLastIoError;
BOOL PatternMatch( IN PCSTR pszString, IN PCSTR pszPattern, IN BOOL fImplyDotAtEnd );
INT_PTR DIAMONDAPI SpdFdiOpen( IN PSTR FileName, IN int oflag, IN int pmode );
int DIAMONDAPI SpdFdiClose( IN INT_PTR Handle );
typedef struct _DIAMOND_INFO {
//
// A read handle to the source file.
//
INT_PTR SourceFileHandle;
//
// File names.
//
PSTR SourceFileName; PSTR TargetFileName;
//
// Flag indicating whether to rename the target file.
//
BOOL RenameTargetFile;
//
// Pointer to LZ information structure.
// We'll fill in some of the fields to fool expand.
//
PLZINFO pLZI;
//
// Expand callback/notification.
//
NOTIFYPROC ExpandNotify;
//
// Selective extraction file spec, ie, "aic*.sys" or NULL
//
PSTR SelectiveFilesSpec;
} DIAMOND_INFO, *PDIAMOND_INFO;
PTSTR StringRevChar( IN PTSTR String, IN TCHAR Char ) { //
// Although not the most efficient possible algoeithm in each case,
// this algorithm is correct for unicode, sbcs, or dbcs.
//
PTCHAR Occurrence,Next;
//
// Check each character in the string and remember
// the most recently encountered occurrence of the desired char.
//
for(Occurrence=NULL,Next=CharNext(String); *String; ) {
if(!memcmp(String,&Char,(int)((PUCHAR)Next-(PUCHAR)String))) { Occurrence = String; }
String = Next; Next = CharNext(Next); }
//
// Return address of final occurrence of the character
// (will be NULL if not found at all).
//
return(Occurrence); }
#define WILDCARD '*' /* zero or more of any character */
#define WILDCHAR '?' /* one of any character (does not match END) */
#define END '\0' /* terminal character */
#define DOT '.' /* may be implied at end ("hosts" matches "*.") */
static int __inline Lower(c) { if ((c >= 'A') && (c <= 'Z')) { return(c + ('a' - 'A')); } else { return(c); } }
static int __inline CharacterMatch(char chCharacter, char chPattern) { if (Lower(chCharacter) == Lower(chPattern)) { return(TRUE); } else { return(FALSE); } }
BOOL PatternMatch( PCSTR pszString, PCSTR pszPattern, IN BOOL fImplyDotAtEnd ) { /* RECURSIVE */
//
// This function does not deal with 8.3 conventions which might
// be expected for filename comparisons. (In an 8.3 environment,
// "alongfilename.html" would match "alongfil.htm")
//
// This code is NOT MBCS-enabled
//
for ( ; ; ) { switch (*pszPattern) {
case END:
//
// Reached end of pattern, so we're done. Matched if
// end of string, no match if more string remains.
//
return(*pszString == END);
case WILDCHAR:
//
// Next in pattern is a wild character, which matches
// anything except end of string. If we reach the end
// of the string, the implied DOT would also match.
//
if (*pszString == END) { if (fImplyDotAtEnd == TRUE) { fImplyDotAtEnd = FALSE; } else { return(FALSE); } } else { pszString++; }
pszPattern++;
break;
case WILDCARD:
//
// Next in pattern is a wildcard, which matches anything.
// Find the required character that follows the wildcard,
// and search the string for it. At each occurence of the
// required character, try to match the remaining pattern.
//
// There are numerous equivalent patterns in which multiple
// WILDCARD and WILDCHAR are adjacent. We deal with these
// before our search for the required character.
//
// Each WILDCHAR burns one non-END from the string. An END
// means we have a match. Additional WILDCARDs are ignored.
//
for ( ; ; ) { pszPattern++;
if (*pszPattern == END) { return(TRUE); } else if (*pszPattern == WILDCHAR) { if (*pszString == END) { if (fImplyDotAtEnd == TRUE) { fImplyDotAtEnd = FALSE; } else { return(FALSE); } } else { pszString++; } } else if (*pszPattern != WILDCARD) { break; } }
//
// Now we have a regular character to search the string for.
//
while (*pszString != END) { //
// For each match, use recursion to see if the remainder
// of the pattern accepts the remainder of the string.
// If it does not, continue looking for other matches.
//
if (CharacterMatch(*pszString, *pszPattern) == TRUE) { if (PatternMatch(pszString + 1, pszPattern + 1, fImplyDotAtEnd) == TRUE) { return(TRUE); } }
pszString++; }
//
// Reached end of string without finding required character
// which followed the WILDCARD. If the required character
// is a DOT, consider matching the implied DOT.
//
// Since the remaining string is empty, the only pattern which
// could match after the DOT would be zero or more WILDCARDs,
// so don't bother with recursion.
//
if ((*pszPattern == DOT) && (fImplyDotAtEnd == TRUE)) { pszPattern++;
while (*pszPattern != END) { if (*pszPattern != WILDCARD) { return(FALSE); }
pszPattern++; }
return(TRUE); }
//
// Reached end of the string without finding required character.
//
return(FALSE); break;
default:
//
// Nothing special about the pattern character, so it
// must match source character.
//
if (CharacterMatch(*pszString, *pszPattern) == FALSE) { if ((*pszPattern == DOT) && (*pszString == END) && (fImplyDotAtEnd == TRUE)) { fImplyDotAtEnd = FALSE; } else { return(FALSE); } }
if (*pszString != END) { pszString++; }
pszPattern++; } } }
INT_PTR DIAMONDAPI DiamondNotifyFunction( IN FDINOTIFICATIONTYPE Operation, IN PFDINOTIFICATION Parameters ) { switch(Operation) {
case fdintCABINET_INFO: case fdintPARTIAL_FILE: default:
//
// Cabinet management functions which we don't use.
// Return success.
//
return(0);
case fdintNEXT_CABINET: return (-1); // We don't support multiple cabinets.
case fdintCOPY_FILE:
//
// Diamond is asking us whether we want to copy the file.
//
{ PDIAMOND_INFO Info = (PDIAMOND_INFO)Parameters->pv; HFILE h;
//
// If we were given a filespec, see if the name matches.
//
if (Info->SelectiveFilesSpec != NULL) {
//
// Call PatternMatch(), fAllowImpliedDot TRUE if
// there is no '.' in the file's base name.
//
BOOL fAllowImpliedDot = TRUE; PSTR p;
for (p = Parameters->psz1; *p != '\0'; p++) { if (*p == '.') { fAllowImpliedDot = FALSE; } else if (*p == '\\') { fAllowImpliedDot = TRUE; } } if (PatternMatch( Parameters->psz1, Info->SelectiveFilesSpec, fAllowImpliedDot) == FALSE) {
return(0); // skip this file
} }
//
// If we need to rename the target file, do that here.
// The name stored in the cabinet file will be used as
// the uncompressed name.
//
if(Info->RenameTargetFile) {
PSTR p,q;
//
// Find the start of the filename part of the target.
//
if(p = StringRevChar(Info->TargetFileName,'\\')) { p++; } else { p = Info->TargetFileName; }
//
// Find the start of the filename part of the name in the cabinet.
//
if(q = StringRevChar(Parameters->psz1,'\\')) { q++; } else { q = Parameters->psz1; }
//
// Copy the filename part of the name in the cabinet over
// the filename part of the name in the target spec.
//
lstrcpy(p,q); }
//
// Inform the expand callback what we are doing.
//
if(!Info->ExpandNotify(Info->SourceFileName,Info->TargetFileName,NOTIFY_START_EXPAND)) { return(0); // skip this file.
}
//
// Remember the uncompressed size and open the file.
// Returns -1 if an error occurs opening the file.
//
Info->pLZI->cblOutSize += Parameters->cb; h = _lcreat(Info->TargetFileName,0); if(h == HFILE_ERROR) { DiamondLastIoError = LZERROR_BADOUTHANDLE; return(-1); } return(h); }
case fdintCLOSE_FILE_INFO:
//
// Diamond is done with the target file and wants us to close it.
// (ie, this is the counterpart to fdint_COPY_FILE).
//
{ PDIAMOND_INFO Info = (PDIAMOND_INFO)Parameters->pv; HANDLE TargetFileHandle; FILETIME ftLocal, ftUTC;
_lclose((HFILE)Parameters->hf);
//
// Set the target file's date/time stamp from the value inside
// the CAB.
//
TargetFileHandle = CreateFile(Info->TargetFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (TargetFileHandle != INVALID_HANDLE_VALUE) { if (DosDateTimeToFileTime(Parameters->date, Parameters->time, &ftLocal) && LocalFileTimeToFileTime(&ftLocal, &ftUTC)) { SetFileTime(TargetFileHandle, NULL, NULL, &ftUTC); }
CloseHandle(TargetFileHandle); } } return(TRUE);
}
}
PVOID DIAMONDAPI SpdFdiAlloc( IN ULONG NumberOfBytes )
/*++
Routine Description:
Callback used by FDICopy to allocate memory.
Arguments:
NumberOfBytes - supplies desired size of block.
Return Value:
Returns pointer to a block of memory or NULL if memory cannot be allocated.
--*/
{ return((PVOID)LocalAlloc(LMEM_FIXED,NumberOfBytes)); }
VOID DIAMONDAPI SpdFdiFree( IN PVOID Block )
/*++
Routine Description:
Callback used by FDICopy to free a memory block. The block must have been allocated with SpdFdiAlloc().
Arguments:
Block - supplies pointer to block of memory to be freed.
Return Value:
None.
--*/
{ LocalFree((HLOCAL)Block); }
INT_PTR DIAMONDAPI SpdFdiOpen( IN PSTR FileName, IN int oflag, IN int pmode )
/*++
Routine Description:
Callback used by FDICopy to open files.
Arguments:
FileName - supplies name of file to be opened.
oflag - supplies flags for open.
pmode - supplies additional flags for open.
Return Value:
Handle to open file or -1 if error occurs.
--*/
{ HFILE h; int OpenMode;
if(oflag & _O_WRONLY) { OpenMode = OF_WRITE; } else { if(oflag & _O_RDWR) { OpenMode = OF_READWRITE; } else { OpenMode = OF_READ; } }
h = _lopen(FileName,OpenMode | OF_SHARE_DENY_WRITE);
if(h == HFILE_ERROR) { DiamondLastIoError = LZERROR_BADINHANDLE; return(-1); }
return((INT_PTR)h); }
UINT DIAMONDAPI SpdFdiRead( IN INT_PTR Handle, OUT PVOID pv, IN UINT ByteCount )
/*++
Routine Description:
Callback used by FDICopy to read from a file.
Arguments:
Handle - supplies handle to open file to be read from.
pv - supplies pointer to buffer to receive bytes we read.
ByteCount - supplies number of bytes to read.
Return Value:
Number of bytes read (ByteCount) or -1 if an error occurs.
--*/
{ UINT rc;
rc = _lread((HFILE)Handle,pv,ByteCount);
if(rc == HFILE_ERROR) { rc = (UINT)(-1); DiamondLastIoError = LZERROR_READ; }
return(rc); }
UINT DIAMONDAPI SpdFdiWrite( IN INT_PTR Handle, IN PVOID pv, IN UINT ByteCount )
/*++
Routine Description:
Callback used by FDICopy to write to a file.
Arguments:
Handle - supplies handle to open file to be written to.
pv - supplies pointer to buffer containing bytes to write.
ByteCount - supplies number of bytes to write.
Return Value:
Number of bytes written (ByteCount) or -1 if an error occurs.
--*/
{ UINT rc;
rc = _lwrite((HFILE)Handle,pv,ByteCount);
if(rc == HFILE_ERROR) {
DiamondLastIoError = (GetLastError() == ERROR_DISK_FULL) ? LZERROR_WRITE : LZERROR_BADOUTHANDLE;
} else {
if(rc != ByteCount) { //
// let caller interpret return value but record last error just in case
//
DiamondLastIoError = LZERROR_WRITE; } }
return(rc); }
int DIAMONDAPI SpdFdiClose( IN INT_PTR Handle )
/*++
Routine Description:
Callback used by FDICopy to close files.
Arguments:
Handle - handle of file to close.
Return Value:
0 (success).
--*/
{ _lclose((HFILE)Handle); return(0); }
LONG DIAMONDAPI SpdFdiSeek( IN INT_PTR Handle, IN long Distance, IN int SeekType )
/*++
Routine Description:
Callback used by FDICopy to seek files.
Arguments:
Handle - handle of file to close.
Distance - supplies distance to seek. Interpretation of this parameter depends on the value of SeekType.
SeekType - supplies a value indicating how Distance is to be interpreted; one of SEEK_SET, SEEK_CUR, SEEK_END.
Return Value:
New file offset or -1 if an error occurs.
--*/
{ LONG rc;
rc = _llseek((HFILE)Handle,Distance,SeekType);
if(rc == HFILE_ERROR) { DiamondLastIoError = LZERROR_BADINHANDLE; rc = -1L; }
return(rc); }
INT ExpandDiamondFile( IN NOTIFYPROC ExpandNotify, IN PSTR SourceFileName, IN PSTR TargetFileName, IN BOOL RenameTarget, IN PSTR SelectiveFilesSpec, OUT PLZINFO pLZI ) { BOOL b; INT rc; INT_PTR h; DIAMOND_INFO DiamondInfo;
if(!FdiContext) { return(LZERROR_BADVALUE); }
DiamondLastIoError = TRUE;
//
// Get a handle to the source to use to
// copy the date and time stamp.
//
h = SpdFdiOpen(SourceFileName,_O_RDONLY,0); if(h == -1) { return(LZERROR_BADINHANDLE); }
pLZI->cblInSize = GetFileSize((HANDLE)h,NULL); if(pLZI->cblInSize == -1) { SpdFdiClose(h); return(LZERROR_BADINHANDLE); }
DiamondInfo.SourceFileHandle = h; DiamondInfo.SourceFileName = SourceFileName; DiamondInfo.TargetFileName = TargetFileName; DiamondInfo.RenameTargetFile = RenameTarget; DiamondInfo.ExpandNotify = ExpandNotify; DiamondInfo.SelectiveFilesSpec = SelectiveFilesSpec; DiamondInfo.pLZI = pLZI;
b = FDICopy( FdiContext, SourceFileName, // pass the whole path as the name
"", // don't bother with the path part
0, // flags
DiamondNotifyFunction, NULL, // no decryption
&DiamondInfo );
if(b) {
rc = TRUE;
} else {
switch(FdiError.erfOper) {
case FDIERROR_CORRUPT_CABINET: case FDIERROR_UNKNOWN_CABINET_VERSION: case FDIERROR_BAD_COMPR_TYPE: rc = LZERROR_READ; // causes SID_FORMAT_ERROR message
break;
case FDIERROR_ALLOC_FAIL: rc = LZERROR_GLOBALLOC; break;
case FDIERROR_TARGET_FILE: case FDIERROR_USER_ABORT: rc = DiamondLastIoError; break;
default: //
// The rest of the errors are not handled specially.
//
rc = LZERROR_BADVALUE; break; }
//
// Remove the partial target file.
//
DeleteFile(TargetFileName); }
SpdFdiClose(h);
return(rc); }
BOOL IsDiamondFile( IN PSTR FileName, OUT PBOOL ContainsMultipleFiles ) { FDICABINETINFO CabinetInfo; BOOL b; INT_PTR h;
*ContainsMultipleFiles = FALSE;
if(!FdiContext) { return(FALSE); }
//
// Open the file such that the handle is valid for use
// in the diamond context (ie, seek, read routines above).
//
h = SpdFdiOpen(FileName,_O_RDONLY,0); if(h == -1) { return(FALSE); }
b = FDIIsCabinet(FdiContext,h,&CabinetInfo);
SpdFdiClose(h);
if (b && (CabinetInfo.cFiles > 1)) { *ContainsMultipleFiles = TRUE; }
return(b); }
BOOL InitDiamond( VOID ) { if(FdiContext == NULL) {
FdiContext = FDICreate( SpdFdiAlloc, SpdFdiFree, SpdFdiOpen, SpdFdiRead, SpdFdiWrite, SpdFdiClose, SpdFdiSeek, cpuUNKNOWN, &FdiError ); }
return(FdiContext != NULL); }
VOID TermDiamond( VOID ) { if(FdiContext) { FDIDestroy(FdiContext); FdiContext = NULL; } }
|