#include #include // // lz headers // #include "lz_common.h" #include "lz_buffers.h" #include "lz_header.h" // // diamond headers // #include #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 fdintNEXT_CABINET: case fdintPARTIAL_FILE: default: // // Cabinet management functions which we don't use. // Return success. // return(0); 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; } }