/*** extract.c - Main program for EXTRACT.EXE * * Microsoft Confidential * Copyright (C) Microsoft Corporation 1994-1997 * All Rights Reserved. * * Author: * Benjamin W. Slivka * * History: * 19-Feb-1994 bens Initial version (started with diamond.c) * 22-Feb-1994 bens Implement file extract * 03-Mar-1994 bens Split cabinet paths from cabinet file names * 08-Mar-1994 bens Add date/time/attribute display * 09-Mar-1994 bens Improve response to FDI errors * 16-Mar-1994 bens More FDI error codes * 21-Mar-1994 bens Log all open/close calls to see if we are * losing file handles in FDI.LIB. * 28-Mar-1994 bens Handle fdintCABINET_INFO, support /A switch * 30-Mar-1994 bens Move MS-DOS/Win32 knowledge to fileutil.* * 31-Mar-1994 bens Add SMALL_DOS to test small model FDI client * 01-Apr-1994 bens Add /D and /E switches, support full command * line behavior. * 07-Apr-1994 bens Add crypto support (at least for debugging) * 06-May-1994 bens Improve /D display for long filenames * 13-May-1994 bens Add prompting for next cabinet, DMF support * 27-May-1994 bens Include correct strings for localization * 03-Jun-1994 bens Report error on correct cabinet * 07-Jun-1994 bens Localization enabled * 21-Jun-1994 bens Localization enabled * 08-Jul-1994 bens Quantum Spill File, self-extracting cabinets! * 11-Jul-1994 bens Use 24-hour time format if am/pm strings are empty * 26-Jul-1994 bens Add /C switch; no switches give /? help * 05-Aug-1994 bens Chicago bug 13214 (don't show partial file info * unless name matches request). Chicago bug 13221 * (truncate extracted file to specified size, in * case file already existed and was larger!). * Chicago bug 9646 (give details of Quantum * decompress failure -- out of RAM, spill file). * Implement overwrite prompt and /Y switch. * 14-Dec-1994 bens Include Floppy changeline fix from * ..\dmf\dmftsr\fixchg.c * 12-Mar-1995 bens Define NOT_US_PC flag to disable use of DMF hook * and FixChangeline code. Also, check COMSPEC to * detect boot drive, instead of hard-coding C:, so * that the Quantum spill file can default to the * boot drive if no TEMP path is found. In the far * east, the hard disk boot drive is A:, so that's * why we have to check! * 31-Mar-1995 jeffwe Fix Command line ambiguity when no /D or /E * option is specified * 2-Apr-1995 jeffwe Fix file time/date set to change the correct * file when rename option being used * 28-Feb-1997 msliger Added /Z option to zap paths from CABs. Fixed * 32-bit self-extract feature. * 18-Mar-1997 msliger Mask attribs to watch out for UTF et al. * 24-Mar-1997 msliger Fix self-extract on NT (don't use argv[0]) * 13-May-1997 msliger Merged in deltas for GUI Extrac32.EXE * 26-Jun-1997 msliger Support XIMPLANT self-extract (CAB is an added * section in the PE file with a certain name; this * makes self-extractors Authenticode 2 compatible. * 01-Jul-1997 msliger Fix reporting wrong cab name for corrupt cabinets. * 22-Mar-1999 msliger Added support for CAB-destructive extraction. * * * Notes: * A self-extracting cabinet file can be created using DIAMOND.EXE and * EXTRACT.EXE very simply: * 1) Create a cabinet file using DIAMOND.EXE * 2) COPY /B EXTRACT.EXE+foo.cab foo.exe * When EXTRACT starts executing, it compares the file size indicated * in the EXE headers (MZ or PE, as appropriate) with the size of the * file indicated in argv[0]. If the argv[0] size is greater, and a * cabinet file appears there, then EXTRACT goes into self-extracting * mode! * * However, the EXE created this way is not Authenticode 2-compatible. * * For Authenticode compatibility, 32-bit versions can also: * 1) Create a cabinet file using DIAMOND.EXE * 2) XIMPLANT EXTRACT.EXE foo.cab foo.exe * This buries the cabinet inside the PE image in a way that doesn't * offend Authenticode 2. */ //#include "resource.h" //#include "pch.h" #ifdef WIN32GUI #include "extrac32.rc" #else #include "extract.rc" #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BIT16 #include #include "fixchg.h" #else // !BIT16 //** Get minimal Win32 definitions #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #undef ERROR // Override "#define ERROR 0" in wingdi.h #endif // !BIT16 #ifdef WIN32GUI #include #include "extrcids.h" #endif #include "types.h" #include "asrt.h" #include "error.h" #include "mem.h" #include "message.h" #include "filelist.h" #include "fileutil.h" #include "wildcard.h" #include "dmfon.h" // DMF support #include // LOCALIZED for EXTRACT.EXE -- specify "cl /Ipath" #include "fdi.h" #include "oldnames.h" //** Constants #define cbMAX_LINE 256 // Maximum output line length #define cMAX_CAB_FILE_OPEN 2 // Maximum simultaneous opens on a single // cabinet file //** Error causes for failure of spill file typedef enum { seNONE, // No error seNOT_ENOUGH_MEMORY, // Not enough RAM seCANNOT_CREATE, // Cannot create spill file seNOT_ENOUGH_SPACE, // Not enough space for spill file } SPILLERR; /* se */ //** Types typedef enum { actBAD, // Invalid action actHELP, // Show help actDEFAULT, // Perform default action based on command line arguments actDIRECTORY, // Force display of cabinet directory actEXTRACT, // Force file extraction actCOPY, // Do single file-to-file copy } ACTION; /* act */ typedef struct { char achCabPath[cbFILE_NAME_MAX]; // Cabinet file path char achCabFilename[cbFILE_NAME_MAX]; // Cabinet file name.ext char achDiskName[cbFILE_NAME_MAX]; // User readable disk label USHORT setID; USHORT iCabinet; } CABINET; /* cab */ typedef CABINET *PCABINET; /* pcab */ #ifdef ASSERT #define sigSESSION MAKESIG('S','E','S','S') // SESSION signature #define AssertSess(psess) AssertStructure(psess,sigSESSION); #else // !ASSERT #define AssertSess(psess) #endif // !ASSERT typedef struct { #ifdef ASSERT SIGNATURE sig; // structure signature sigSESSION #endif ACTION act; // Action to perform HFILELIST hflist; // List of files specified on cmd line BOOL fAllCabinets; // TRUE => Process continutation cabs BOOL fOverwrite; // TRUE => Overwrite existing files BOOL fNoLineFeed; // TRUE if last printf did not have \n BOOL fSelfExtract; // TRUE if self-extracting long cbSelfExtract; // Size of EXE portion of self-ex cabinet long cbSelfExtractSize; // Size of CAB portion of self-ex cabinet int ahfSelf[cMAX_CAB_FILE_OPEN]; // Cabinet file handles int cErrors; // Count of errors encountered HFDI hfdi; // FDI context ERF erf; // FDI error structure long cFiles; // Total files processed long cbTotalBytes; // Total bytes extracted PERROR perr; // Pass through FDI SPILLERR se; // Spill file error long cbSpill; // Size of spill file requested char achSelf[cbFILE_NAME_MAX]; // Name of our EXE file char achMsg[cbMAX_LINE*2]; // Message formatting buffer char achLine[cbMAX_LINE]; // Line formatting buffer char achLocation[cbFILE_NAME_MAX]; // Output directory char achFile[cbFILE_NAME_MAX]; // Current filename being extracted char achDest[cbFILE_NAME_MAX]; // Forced destination file name char achCabPath[cbFILE_NAME_MAX]; // Path to look for cabinet file BOOL fContinuationCabinet; // TRUE => not 1st cabinet processed BOOL fShowReserveInfo; // TRUE => show RESERVEd cabinet info //** fNextCabCalled allows us to figure out which of the acab[] entries // to use if we are processing all file in a cabinet set (i.e., if // fAllCabinet is TRUE). If fdintNEXT_CABINET has never been called, // then acab[1] has the information for the next cabinet. But if // it has been called, then fdintCABINET_INFO will have been called // at least twice (once for the first cabinet, and once at least for // a continuation cabinet), and so acab[0] is the cabinet we need to // pass to a subsequent FDICopy() call. BOOL fNextCabCalled; // TRUE => GetNextCabinet called CABINET acab[2]; // Last two fdintCABINET_INFO data sets char achZap[cbFILE_NAME_MAX]; // prefix to strip from filename char achCabinetFile[cbFILE_NAME_MAX]; // current cabinet file int cArgv; // default self-ext argc char **pArgv; // default self-ext argv[] int fDestructive; // TRUE => minimize disk space needed USHORT iCurrentFolder; // iff fDestructive, only extract this one } SESSION; /* sess */ typedef SESSION *PSESSION; /* psess */ /* ** Spill file statics for Quantum */ INT_PTR hfSpillFile; // File handle char achSpillFile[cbFILE_NAME_MAX]; // File path /* ** Global state for self-extract */ PSESSION psessG; #ifdef WIN32GUI /* ** GUI support */ static INT_PTR hCabFile1 = -1; static INT_PTR hCabFile2 = -1; static unsigned long ibCabFilePosition1; static unsigned long ibCabFilePosition2; static unsigned long cbCabFileMax; static unsigned long cbCabFileTotal; static unsigned long cbCabFileScale; static int iPercentLast; static void ProgressReport(unsigned long cbCabFileMax); LRESULT CALLBACK ProgressWndProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam); static HINSTANCE g_hinst; static HWND g_hwndProgress = NULL; #endif //** Function Prototypes FNASSERTFAILURE(fnafReport); HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr); BOOL checkWildMatches(PSESSION psess, char *pszFile, PERROR perr); BOOL doCabinet(PSESSION psess, PERROR perr); BOOL doCopy(PSESSION psess, PERROR perr); BOOL ensureCabinet(PSESSION psess, char *pszPath, int cbPath, char *pszFile, char *pszLabel, USHORT setID, USHORT iCabinet, BOOL fLoop, BOOL fPromptOnly, PERROR perr); BOOL checkOverwrite(PSESSION psess, char *pszFile, PERROR perr, int *prc); BOOL checkSelfExtractingCab(PSESSION psess, int cArg, char *apszArg[], PERROR perr); char *getBootDrive(void); BOOL parseCommandLine(PSESSION psess,int cArg,char *apszArg[],PERROR perr); void printError(PSESSION psess, PERROR perr); void pszFromAttrFAT(char *psz, int cb, WORD attrFAT); void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time); int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin); //** FDI callbacks and related functions FNALLOC(fdiAlloc); FNFREE(fdiFree); FNFDINOTIFY(fdiNotifyDir); FNFDINOTIFY(fdiNotifyExt); FNFDINOTIFY(doGetNextCab); FNFDIDECRYPT(fdiDecryptDir); FNFDIDECRYPT(fdiDecryptExt); void mapFDIError(PERROR perr,PSESSION psess, char *pszCabinet, PERF perf); //** File I/O wrapper functions INT_PTR FAR DIAMONDAPI wrap_open(char FAR *, int, int); UINT FAR DIAMONDAPI wrap_read(INT_PTR, void FAR *, unsigned int); UINT FAR DIAMONDAPI wrap_write(INT_PTR, void FAR *, unsigned int); int FAR DIAMONDAPI wrap_close(INT_PTR); long FAR DIAMONDAPI wrap_lseek(INT_PTR, long, int); #ifdef SMALL_DOS #define STRCPY(dst,src) _fstrcpy((char far *)dst,(char far *)src) #else #define STRCPY(dst,src) strcpy(dst,src) #endif //FEATURE: 08-Jul-1994 bens Generate debug output //#define DEBUG_FDI 1 #ifdef DEBUG_FDI #define dbg(a) a #else #define dbg(a) #endif #define HELP_MAX_SIZE 4096 #define MAX_MESSAGE_SIZE 256 #define EMPTY_SPACE L" " //** Functions /*** main - Extract main program * * See DIAMOND.DOC for spec and operation. * * NOTE: We're sloppy, and don't free resources allocated by * functions we call, on the assumption that program exit * will clean up memory and file handles for us. */ int __cdecl wmain(DWORD cArg, LPWSTR apszArg[]) { ERROR err; PSESSION psess; WCHAR szTemp[HELP_MAX_SIZE] = L""; DWORD dw = 0; LPSTR *szArg = NULL; LPSTR szTempArg; // #define NTVCPP_DEBUG_HACK #ifdef NTVCPP_DEBUG_HACK _chdir("\\elroy\\diamond\\layout\\testnew"); #endif AssertRegisterFunc(fnafReport); // Register assertion reporter ErrClear(&err); // No error err.pszFile = NULL; // No file being processed, yet achSpillFile[0] = '\0'; // No name constructed, yet #ifdef BIT16 #ifndef NOT_US_PC //** Make sure we can read DMF disks -- only for pre-Chicago systems EnableDMFSupport(); //** Turn off floppy disk changeline support to make sure systems // with faulty change lines don't choke on disk 2 in the case // where disk 1 is non-DMF and disk 2 is DMF. FixChangelines(); #endif #endif if( cArg<=1 ) { fwprintf( stderr, pszINVALID_SYNTAX ); fwprintf( stderr, pszHELP_MESSAGE ); return( EXIT_FAILURE); } //** Initialize session psess = MemAlloc(sizeof(SESSION)); if (!psess) { ErrSet(&err,pszEXTERR_NO_SESSION); printError(psess,&err); exit(1); } SetAssertSignature((psess),sigSESSION); psessG = psess; // Save for wrap_open/wrap_close psess->fOverwrite = FALSE; // Default to being save psess->fAllCabinets = FALSE; // Don't do continuation cabinets psess->fNextCabCalled = FALSE; psess->fContinuationCabinet = FALSE; psess->fShowReserveInfo = FALSE; psess->fSelfExtract = FALSE; psess->hflist = NULL; psess->hfdi = NULL; psess->fNoLineFeed = 0; // TRUE if last printf did not have \n psess->cFiles = 0; // No files, yet psess->cbTotalBytes = 0; // No bytes, yet psess->se = seNONE; // No spill file error psess->achZap[0] = '\0'; // No Zap pattern, yet psess->cArgv = 0; // No default self-ext cmd line psess->fDestructive = FALSE; // Not truncating cab during extract //** Print Extract banner if (psess->act == actHELP) { // Do help if any args, for now fwprintf(stdout, L"\n"); // Separate banner from help MultiByteToWideChar( CP_THREAD_ACP, 0, pszBANNER, strlen(pszBANNER), szTemp, HELP_MAX_SIZE ); fwprintf(stderr, szTemp); ZeroMemory(szTemp, HELP_MAX_SIZE); } //** Parse command line, convert command line wide char strings to LPSTR szArg = (LPSTR *)malloc( cArg*sizeof(LPSTR*) ); if( NULL == szArg ) { fwprintf( stderr, L"ERROR: Memory Allocation failed.\n" ); //** Free resources AssertSess(psess); ClearAssertSignature((psess)); MemFree(psess); return EXIT_FAILURE; } for(dw=0; dwact == actHELP) { // Do help if any args, for now fwprintf(stdout, L"\n"); // Separate banner from help MultiByteToWideChar( CP_THREAD_ACP, 0, pszCMD_LINE_HELP, strlen(pszCMD_LINE_HELP), szTemp, HELP_MAX_SIZE); fwprintf( stderr, szTemp ); //** Free resources AssertSess(psess); ClearAssertSignature((psess)); MemFree(psess); return 0; } ZeroMemory(szTemp, HELP_MAX_SIZE); //** Quick out for COPY command if (psess->act == actCOPY) { if (!doCopy(psess,&err)) { printError(psess,&err); //** Free resources AssertSess(psess); ClearAssertSignature((psess)); MemFree(psess); return 1; } //** Success return 0; } //** Have some work to do -- go do it if (!doCabinet(psess,&err)) { printError(psess,&err); //** Make sure we delete spill file if (hfSpillFile != -1) { wrap_close(hfSpillFile); // Close and delete it } //** Free resources AssertSess(psess); ClearAssertSignature((psess)); MemFree(psess); return 1; } //** See if we actually got any files if (psess->cFiles == 0) { MsgSet(psess->achMsg,pszEXT_NO_MATCHING_FILES); MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg), szTemp, HELP_MAX_SIZE); fwprintf(stderr, L"%s\n",szTemp); ZeroMemory(szTemp, HELP_MAX_SIZE); } else if (psess->act == actDIRECTORY) { //** Print out file and byte count MsgSet(psess->achMsg, psess->cFiles == 1 ? pszEXT_SUMMARY1 : pszEXT_SUMMARY2, "%,13ld%,13ld", psess->cFiles, psess->cbTotalBytes); MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg), szTemp, HELP_MAX_SIZE); fwprintf(stdout, L"%s\n",szTemp); ZeroMemory(szTemp, HELP_MAX_SIZE); } //** Free resources AssertSess(psess); ClearAssertSignature((psess)); MemFree(psess); //** Success return 0; } /* main */ /*** doCopy - Copy one file * * Entry: * psess - Description of operation to perform * perr - ERROR structure * * Exit-Success: * Returns TRUE; file copied * * Exit-Failure: * Returns FALSE; error * * NOTE: * Supported SRC/DST syntax: * Src Dst Example * ---- ---- -------------------------- * file dir "foo.exe ."; "foo.exe c:\dir" * file file "foo.exe c:bar.exe" */ BOOL doCopy(PSESSION psess, PERROR perr) { char achDst[cbFILE_NAME_MAX]; // Buffer for src file name HFILESPEC hfspec; char *pszSrc; char *pszSrcJustFile; char *pszDst; int rc; struct _stat stat; WCHAR szTemp[MAX_MESSAGE_SIZE]; //** Get the source file hfspec = FLFirstFile(psess->hflist); Assert(hfspec != NULL); pszSrc = FLGetSource(hfspec); Assert(pszSrc!=NULL); Assert(*pszSrc); //** Get the destination file hfspec = FLNextFile(hfspec); // We have something Assert(hfspec != NULL); pszDst = FLGetSource(hfspec); Assert(pszDst!=NULL); Assert(*pszDst); //** Determine if destination is a directory if (-1 != _stat(pszDst,&stat)) { // File/Dir exists //** Destination exists if (stat.st_mode & _S_IFDIR) { // It is a directory //** It is a directory; get just file name and extension of source if (!(pszSrcJustFile = getJustFileNameAndExt(pszSrc,perr))) { return FALSE; } //** Construct destination name if (!catDirAndFile( achDst, // Buffer for full path sizeof(achDst), // Size of buffer pszDst, // Destination directory pszSrcJustFile, // File name NULL, // Don't have alternate name perr)) { return FALSE; // Failure } //** Use constructed name pszDst = achDst; } } //** Make sure it's OK to overwrite destination file if (!checkOverwrite(psess,pszDst,perr,&rc)) { //** Ignore skip/abort return code in rc return TRUE; // Skip file copy, everything is OK } //** Tell user we're copying the file MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE2,"%s%s",pszSrc,pszDst); MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg) , szTemp, MAX_MESSAGE_SIZE ); *(szTemp+strlen(psess->achMsg)) = '\0'; fwprintf(stdout, L"%s\n",szTemp); //** Do the file copy return CopyOneFile(pszDst, // destination pszSrc, // source TRUE, // do the copy 32768U, // copy buffer size NULL, // don't override date/time/attr NULL, // no callback context perr); } /* doCopy() */ /*** doCabinet - Show contents of one or more cabinet files * * Entry: * psess - Description of operation to perform * perr - ERROR structure * * Exit-Success: * Returns TRUE; directory displayed * * Exit-Failure: * Returns FALSE; perr filled in with details. */ BOOL doCabinet(PSESSION psess, PERROR perr) { char achFile[cbFILE_NAME_MAX]; // Buffer for cabinet file FDICABINETINFO fdici; BOOL fCompatibilityCabinet; // TRUE => Exactly 1 file in 1 cabinet INT_PTR hfCab = -1; // File handle for peeking at cabinet HFILESPEC hfspec; int iCab; PFNFDINOTIFY pfnfdin; PFNFDIDECRYPT pfnfdid; char FAR *pbMemReserve; // Make sure we have some working mem char *pszCabinet; // Cabinet filespec char *pszCabFile; // Cabinet filename.ext char *pszDestOrPattern; // Destination or first pattern WCHAR szTemp[MAX_MESSAGE_SIZE]; //** Get the cabinet name hfspec = FLFirstFile(psess->hflist); Assert(hfspec != NULL); // Must have at least one file pszCabinet = FLGetSource(hfspec); Assert(pszCabinet!=NULL); Assert(*pszCabinet); //** Get the destination file name or first pattern, if present if (NULL != (hfspec = FLNextFile(hfspec))) { // We have something pszDestOrPattern = FLGetSource(hfspec); Assert(pszDestOrPattern!=NULL); //** NOTE: hfspec must remain pointing to this 2nd file all the // way down below where we may need to change its value! } else { pszDestOrPattern = NULL; // No second argument on command line } //** Remember that we have not yet created a spill file hfSpillFile = -1; // No spill file, yet //** Prevent FDI from consuming all available memory // Why 2048? That's enough for 4 paths, which is more than we // will ever need. pbMemReserve = fdiAlloc(2048); if (!pbMemReserve) { ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet); return FALSE; } //** Create FDI context so we can get info from the cabinet file if (!(psess->hfdi = FDICreate(fdiAlloc, fdiFree, wrap_open, wrap_read, wrap_write, wrap_close, wrap_lseek, cpuUNKNOWN, // Let FDI do the CPU detection &(psess->erf) ))) { //** FDICreate failed, generate error message mapFDIError(perr,psess,pszCabinet,&(psess->erf)); fdiFree(pbMemReserve); // Free reserved memory return FALSE; } fdiFree(pbMemReserve); // Free it so we can use it //** Make sure file is a cabinet, and get cabinet info if (-1 == (hfCab = wrap_open(pszCabinet,_O_BINARY | _O_RDONLY,0))) { ErrSet(perr,pszEXTERR_CANNOT_OPEN_FILE,"%s",pszCabinet); goto cleanup; } if (!FDIIsCabinet(psess->hfdi,hfCab,&fdici)) { if (!ErrIsError(perr)) { // Have to set error message ErrSet(perr,pszEXTERR_NOT_A_CABINET,"%s",pszCabinet); } goto cleanup; } wrap_close(hfCab); hfCab = -1; //** No Default Destination psess->achDest[0] = '\0'; //** If no mode specified, figure out what mode we should be in: // // The extract command has ambiguous syntax so we apply the following // rules to resolve the ambiguity. // // Most cabinet file authors use DIAMOND.EXE to create a set of // cabinet files with many files in it. A typical cabinet set would // look like: // (1) Cab#1 // foo.1 // foo.2 // foo.3 - partial // Cab#2 // foo.3 - continued // ... // // However, there are some "old-style" customers of DIAMOND.EXE that // like to compress each file independently, producing a "set" of // cabinet files that each contain exactly one file, i.e.: // (2) excel.ex_ // excel.in_ // setup.in_ // // The "_" character in the extension is a hint that the file is // compressed. However, this isn't useful to this program // // Now, the question is, what does the customer want to have happen // when she types "EXTRACT foo.cab bar"? For the multi-file cabinet // case (1) above, this means "seach foo.cab and extract all files // that are named bar". BUT, for the case (2), we have a compatibility // constraint -- she thinks this means "extract the compressed // file foo.cab and call the resulting uncompressed file bar". // // Another question is what does the customer want to have happen // when she types "EXTRACT foo.cab"? For the multi-file cabinet // case (1) above this means list the contents of the cabinet. // But for case (2), we have a compatibility constraint -- customers // think this means "extract the compressed file foo.cab". // // // A cabinet is of type (1) if it contains more than one file, // or has either a previous or next cabinet. Otherwise, it is of // type (2), i.e., the cabinet has exactly one file, and has no // previous or next cabinet. if (psess->act == actDEFAULT) { // No action specified on command line //** Determine if cabinet is of type (2) described above. fCompatibilityCabinet = (fdici.cFiles == 1) && (! (fdici.hasprev || fdici.hasnext)); //** Now figure out what customer really wants if (pszDestOrPattern) { // extract foo.cab bar psess->act = actEXTRACT; if (fCompatibilityCabinet) { // Special Case Rename (see above (2)) strcpy(psess->achDest, pszDestOrPattern); if (!FLSetSource(hfspec,pszALL_FILES,perr)) { goto cleanup; } } } else { // extract foo.cab if (fCompatibilityCabinet) { // Special Case Extract (see above (2)) psess->act = actEXTRACT; } else { psess->act = actDIRECTORY; } } } //** Supply a default pattern if no pattern is present if (!pszDestOrPattern) { if (addFileSpec(psess,pszALL_FILES,perr) == NULL) { ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",pszALL_FILES); goto cleanup; } } //** Now, select the appropriate FDI notification function Assert((psess->act == actEXTRACT) || (psess->act == actDIRECTORY)); pfnfdin = (psess->act == actEXTRACT) ? fdiNotifyExt : fdiNotifyDir; if (fdici.fReserve) { // Reserved area(s) present pfnfdid = (psess->act == actEXTRACT) ? fdiDecryptExt : fdiDecryptDir; } else { pfnfdid = NULL; // No reserved areas } //** Split cabinet spec into path and filename.ext pszCabFile = getJustFileNameAndExt(pszCabinet,perr); if (pszCabFile == NULL) { goto cleanup; // perr is already filled in } strcpy(achFile,pszCabFile); //** Need to trim off file name and keep just cabinet path strcpy(psess->achCabPath,pszCabinet); // Store in our buffer psess->achCabPath[pszCabFile - pszCabinet] = '\0'; // Trim off file name if (psess->fDestructive) { // don't do destructive extract on chained cabinets if ((psess->act != actEXTRACT) || fdici.hasprev || fdici.hasnext) { psess->fDestructive = FALSE; } else { psess->iCurrentFolder = fdici.cFolders - 1; } } psess->perr = perr; // Pass perr through FDI //** Do cabinets until there are no more, or an error occurs while ( (strlen(achFile) > 0) && !ErrIsError(perr) ) { //** Show which cabinet we are processing MsgSet(psess->achMsg,pszEXT_CABINET_HEADER,"%s",achFile); MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen( psess->achMsg ), szTemp, MAX_MESSAGE_SIZE ); *(szTemp+strlen(psess->achMsg)) = '\0'; fwprintf(stdout, L"\n%s\n\n",szTemp); strcpy(psess->achCabinetFile,achFile); //** Do the cabinet if (!FDICopy(psess->hfdi, // FDI context achFile, // Cabinet file name.ext psess->achCabPath, // Path to cabinet 0, // Flags (???) pfnfdin, // Notifcation callback pfnfdid, // Decrypt callback psess // Our context )) { //** NOTE: psess->achCabPath *may* get changed during an // fdintNEXT_CABINET callback if we had to prompt the // use for a different path! //** FDICopy failed, construct error message if (!ErrIsError(perr)) { // Need to set error message //** Construct error message mapFDIError(perr,psess,psess->achCabinetFile,&(psess->erf)); //** Delete file if created, with the assumption that // we were not able to completely write the file // (for example, if the destination disk ran out of space!) if (psess->erf.erfOper == FDIERROR_TARGET_FILE) { //** Ignore errors, if any _unlink(psess->achFile); } } } else { //** OK so far, see if any more cabinets to process if (psess->fDestructive) { if (psess->iCurrentFolder != 0) { FDITruncateCabinet(psess->hfdi, pszCabinet, psess->iCurrentFolder); psess->iCurrentFolder--; // move down to next folder } else { _unlink(pszCabinet); // delete the CAB! achFile[0] = '\0'; // Done } } else if (psess->fAllCabinets) { //** Skip "starts in ..." messages for subsequent cabinets psess->fContinuationCabinet = TRUE; //** Copy next cabinet file (ach[] is empty if no more!) iCab = psess->fNextCabCalled ? 0 : 1; // Select correct cabinet strcpy(achFile,psess->acab[iCab].achCabFilename); strcpy(psess->achCabinetFile,achFile); psess->fNextCabCalled = FALSE; // Reset flag //** If there is another cabinet to process, make sure it // is available; psess->achCabPath may be edited if we // can't find the cabinet until the user supplies another // path; perr will be set if an error occurs. if (achFile[0] != '\0') { // Another cabinet ensureCabinet(psess, psess->achCabPath, sizeof(psess->achCabPath), achFile, psess->acab[iCab].achDiskName, psess->acab[iCab].setID, (USHORT)(psess->acab[iCab].iCabinet+1), TRUE, // Loop until right cab or abort FALSE, // Check cabinet perr); } } else { achFile[0] = '\0'; // Done } } } cleanup: if (hfCab != -1) { wrap_close(hfCab); } if (!FDIDestroy(psess->hfdi)) { //** Only set error if we don't already have one if (!ErrIsError(perr)) { ErrSet(perr,pszEXTERR_FDIDESTROY_FAILED); } } psess->perr = NULL; //** Return success/failure indication return !ErrIsError(perr); } /* doCabinet() */ /*** fdiNotifyDir - Callback from FDICopy for Directory display * * Entry: * fdint - type of notification * pfdin - data for notification * * Exit-Success: * Return value varies (see FDI.H:PFNFDINOTIFY type) * * Exit-Failure: * Return value varies (see FDI.H:PFNFDINOTIFY type) */ FNFDINOTIFY(fdiNotifyDir) { char achAttr[10]; PERROR perr; #ifdef SMALL_DOS PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv; char szLocal[cbFILE_NAME_MAX]; #else PSESSION psess=(PSESSION)pfdin->pv; #endif WCHAR szTemp[MAX_MESSAGE_SIZE]; AssertSess(psess); perr = psess->perr; switch (fdint) { case fdintCABINET_INFO: return updateCabinetInfo(psess,pfdin); case fdintCOPY_FILE: //** See if filspec matches specified patterns #ifdef SMALL_DOS _fstrcpy(szLocal,pfdin->psz1); #else #define szLocal pfdin->psz1 #endif if (!checkWildMatches(psess,szLocal,perr)) { //** Either no match, or failure -- figure out which if (ErrIsError(perr)) { return -1; // Error, abort } else { return 0; // No error, skip this file } } //** Show directory pszFromMSDOSTime(psess->achMsg, sizeof(psess->achMsg), pfdin->date, pfdin->time); pszFromAttrFAT(achAttr, sizeof(achAttr), pfdin->attribs); MsgSet(psess->achLine, pszEXT_FILE_DETAILS, #ifdef SMALL_DOS "%s%s%,13ld%-Fs", #else "%s%s%,13ld%-s", #endif psess->achMsg,achAttr,pfdin->cb,pfdin->psz1); MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achLine, strlen(psess->achLine), szTemp, MAX_MESSAGE_SIZE ); *(szTemp+strlen(psess->achLine)) = '\0'; fwprintf( stdout,L"%s\n",szTemp ); psess->cFiles++; psess->cbTotalBytes += pfdin->cb; return 0; // Skip file, do not copy case fdintPARTIAL_FILE: //** Construct output filespec #ifdef SMALL_DOS _fstrcpy(szLocal,pfdin->psz1); #else #define szLocal pfdin->psz1 #endif //** See if filspec matches specified patterns if (!checkWildMatches(psess,szLocal,perr)) { //** Either no match, or failure -- figure out which if (ErrIsError(perr)) { return -1; // Error, abort } else { return 0; // No error, skip this file } } //** Only show partial file messages for first cabinet if (!psess->fContinuationCabinet) { // First cabinet MsgSet(psess->achMsg,pszEXT_PARTIAL_FILE, #ifdef SMALL_DOS "%Fs%Fs%Fs", #else "%s%s%s", #endif pfdin->psz1,pfdin->psz2,pfdin->psz3); //do the localization print the message MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achLine, strlen(psess->achLine), szTemp, MAX_MESSAGE_SIZE ); *(szTemp+strlen(psess->achLine)) = '\0'; fwprintf( stdout,L"%s\n",szTemp ); fwprintf( stdout, L"%s\n",szTemp); } return 0; // Continue case fdintNEXT_CABINET: return doGetNextCab(fdint,pfdin); case fdintENUMERATE: return 0; default: fwprintf(stdout, L"UNKNOWN NOTIFICATION: %d\n",fdint); return 0; /* ??? */ } } /* fdiNotifyDir() */ /*** fdiNotifyExt - Callback from FDICopy for file extraction * * <<< Extract files! >>> * * Entry: * fdint - type of notification * pfdin - data for notification * * Exit-Success: * Return value varies (see FDI.H:PFNFDINOTIFY type) * * Exit-Failure: * Return value varies (see FDI.H:PFNFDINOTIFY type) */ FNFDINOTIFY(fdiNotifyExt) { INT_PTR fh; FILETIMEATTR fta; PERROR perr; char *pszDestinationFile; int rc; #ifdef SMALL_DOS PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv; char szLocal[cbFILE_NAME_MAX]; #else PSESSION psess=(PSESSION)pfdin->pv; #endif WCHAR szTemp[MAX_MESSAGE_SIZE]; AssertSess(psess); perr = psess->perr; //** Reset the spill file error code; // We know that FDI is OK right now if it is asking us if we want // to extract this file, so reset the spill file error code. If // we did not, then it may have seNOT_ENOUGH_MEMORY (for example) // as a result of Quantum trying to eat up all available memory, // and a real decompression failure would be reported as an out // of memory problem. psess->se = seNONE; switch (fdint) { case fdintCABINET_INFO: return updateCabinetInfo(psess,pfdin); case fdintCOPY_FILE: if (psess->fDestructive && (pfdin->iFolder != psess->iCurrentFolder)) { return(0); // only do files in the current folder } //** Construct output filespec #ifdef SMALL_DOS _fstrcpy(szLocal,pfdin->psz1); #else #define szLocal pfdin->psz1 #endif //** See if filspec matches specified patterns if (!checkWildMatches(psess,szLocal,perr)) { //** Either no match, or failure -- figure out which if (ErrIsError(perr)) { return -1; // Error, abort } else { return 0; // No error, skip this file } } //** Figure out what destination file name should be if (psess->achDest[0] != '\0') { // Override name from cabinet pszDestinationFile = psess->achDest; } else { pszDestinationFile = szLocal; } if (psess->achZap[0] != '\0') // if prefix-zapping { if (!strncmp(pszDestinationFile,psess->achZap,strlen(psess->achZap))) { pszDestinationFile += strlen(psess->achZap); } } //** Construct full destination file name if (!catDirAndFile(psess->achFile, // Buffer for output filespec sizeof(psess->achFile), // Size of output buffer psess->achLocation, // Output directory pszDestinationFile, // Output file name NULL, // Don't have alternate name perr)) { return -1; // Abort with error; } //** Make sure output directory exists if (!ensureDirectory(psess->achFile,TRUE,perr)) { return -1; // perr already filled in } //** Do overwrite processing if (!checkOverwrite(psess,psess->achFile,perr,&rc)) { return rc; // Either Skip or Abort } //** Create file fh = wrap_open(psess->achFile, _O_BINARY | _O_RDWR | _O_CREAT | _O_TRUNC, // No translation, R/W _S_IREAD | _S_IWRITE); // Attributes when file is closed if (fh == -1) { ErrSet(psess->perr,pszEXTERR_CANNOT_CREATE_FILE,"%s",psess->achFile); return -1; // Failure } #if 0 // jforbes: if'd this out, added _O_TRUNC above //** Truncate file (in case it already existed and was larger) if (0 != _chsize(fh, 0)) { //** Not the best error, but avoids more localization! ErrSet(psess->perr,pszEXTERR_CANNOT_CREATE_FILE,"%s",psess->achFile); wrap_close(fh); } #endif //** Show status if (pszDestinationFile == szLocal) { // File name is not changed MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE,"%s",psess->achFile); } else { // Destination file is different MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE2,"%s%s", szLocal,psess->achFile); } //add the multibyte conversion MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg), szTemp, strlen(psess->achMsg) ); *(szTemp+strlen(psess->achMsg)) = '\0'; fwprintf( stdout,L"%s\n",szTemp ); psess->fNoLineFeed = TRUE; psess->cFiles++; psess->cbTotalBytes += pfdin->cb; return fh; // Return open file handle case fdintCLOSE_FILE_INFO: //** Close the file wrap_close(pfdin->hf); //** Construct output filespec #ifdef SMALL_DOS _fstrcpy(szLocal,pfdin->psz1); #else #define szLocal pfdin->psz1 #endif //** Figure out what destination file name should be if (psess->achDest[0] != '\0') { // Override name from cabinet pszDestinationFile = psess->achDest; } else { pszDestinationFile = szLocal; } if (psess->achZap[0] != '\0') // if prefix-zapping { if (!strncmp(pszDestinationFile,psess->achZap,strlen(psess->achZap))) { pszDestinationFile += strlen(psess->achZap); } } //** Construct full destination file name if (!catDirAndFile(psess->achFile, // Buffer for output filespec sizeof(psess->achFile), // Size of output buffer psess->achLocation, // Output directory pszDestinationFile, // Output file name NULL, // Don't have alternate name perr)) { return FALSE; // Abort with error } //** Set file date, time, and attributes fta.date = pfdin->date; fta.time = pfdin->time; fta.attr = pfdin->attribs & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); if (!SetFileTimeAndAttr(psess->achFile, &fta, perr)) { return FALSE; // Abort with error } return TRUE; // Success case fdintPARTIAL_FILE: //** Construct output filespec #ifdef SMALL_DOS _fstrcpy(szLocal,pfdin->psz1); #else #define szLocal pfdin->psz1 #endif //** See if filspec matches specified patterns if (!checkWildMatches(psess,szLocal,perr)) { //** Either no match, or failure -- figure out which if (ErrIsError(perr)) { return -1; // Error, abort } else { return 0; // No error, skip this file } } //** Only show partial file messages for first cabinet if (!psess->fContinuationCabinet) { // First cabinet MsgSet(psess->achMsg,pszEXT_PARTIAL_FILE, #ifdef SMALL_DOS "%Fs%Fs%Fs", #else "%s%s%s", #endif pfdin->psz1,pfdin->psz2,pfdin->psz3); printf("%s\n",psess->achMsg); } return 0; // Continue case fdintNEXT_CABINET: return doGetNextCab(fdint,pfdin); case fdintENUMERATE: return 0; default: printf("UNKNOWN NOTIFICATION: %d\n",fdint); return 0; /* ??? */ } } /* fdiNotifyExt() */ /*** checkOverwrite - Check for file existence and do overwrite processing * * Entry: * psess - Session * pszFile - File to check * perr - Error structure * prc - Gets return code * * Exit-Success: * Returns TRUE; file can be overwritten * * Exit-Failure: * Returns FALSE; perr filled in if error, * *prc == 0 -> Skip file * *prc == -1 -> Abort */ BOOL checkOverwrite(PSESSION psess, char *pszFile, PERROR perr, int *prc) { char ch; BOOL fGotReply; BOOL fOverwrite; BOOL fOverwriteAll; struct _stat stat; //** Check to see if file already exists if (-1 == _stat(pszFile,&stat)) { // File does not exist return TRUE; // Write it } //** Prompt if we're supposed to if (!psess->fOverwrite) { //** Display prompt -- no CR/LF MsgSet(psess->achMsg,pszEXT_OVERWRITE_PROMPT,"%s",pszFile); printf("%s",psess->achMsg); //** Get valid single character response and ENTER key; // any illegal keys are just ignored fGotReply = FALSE; while (!fGotReply || (ch != '\r')) { ch = (char)_getch(); // Get a keystroke switch (toupper(ch)) { case chOVERWRITE_YES: fGotReply = TRUE; fOverwrite = TRUE; fOverwriteAll = FALSE; printf("%c\b",ch); // Echo character and backspace over it break; case chOVERWRITE_NO: fGotReply = TRUE; fOverwrite = FALSE; fOverwriteAll = FALSE; printf("%c\b",ch); // Echo character and backspace over it break; case chOVERWRITE_ALL: fGotReply = TRUE; fOverwrite = TRUE; fOverwriteAll = TRUE; printf("%c\b",ch); // Echo character and backspace over it break; default: break; // Ignore character } } //** Do the line feed printf("\n"); //** Respect user's wish if (!fOverwrite) { // Don't overwrite file *prc = 0; // Indicate skip return FALSE; } else { // Overwrite once or all psess->fOverwrite = fOverwriteAll; // Set accordingly } } //** Make sure file is writeable, if it isn't already if (!(stat.st_mode & _S_IWRITE)) { // File is not writeable _chmod(pszFile, _S_IREAD | _S_IWRITE); //** Ignore error code, because the open will fail and catch it } //** Done return TRUE; // Overwrite that file } /* checkOverwrite() */ /*** updateCabinetInfo - update history of cabinets seen * * Entry: * psess - Session * pfdin - FDI info structurue * * Exit: * Returns 0; */ int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin) { AssertSess(psess); //** Save older cabinet info psess->acab[0] = psess->acab[1]; //** Save new cabinet info STRCPY(psess->acab[1].achCabPath ,pfdin->psz3); STRCPY(psess->acab[1].achCabFilename ,pfdin->psz1); STRCPY(psess->acab[1].achDiskName ,pfdin->psz2); psess->acab[1].setID = pfdin->setID; psess->acab[1].iCabinet = pfdin->iCabinet; return 0; } /*** ensureCabinet - Make sure desired cabinet is available * * Make sure requested cabinet is available. * * Entry: * psess - Session * pszPath - Path buffer (modified if necessary on output) * cbPath - Size of path buffer * pszFile - Cabinet file name * pszLabel - Label for disk with cabinet file * setID - setID for cabinet * iCabinet - iCabinet for cabinet * fLoop - TRUE => Loop until right cabinet or user aborts * FALSE => Only try once * fPromptOnly - TRUE => Caller knows cabinet is bad, just prompt * FALSE => Check cabinet, prompt if necessary * perr - Error structure * * Exit-Success: * Returns TRUE; desired cabinet is present * * Exit-Failure: * Returns FALSE; perr filled in. * Returns -1; user aborted */ BOOL ensureCabinet(PSESSION psess, char *pszPath, int cbPath, char *pszFile, char *pszLabel, USHORT setID, USHORT iCabinet, BOOL fLoop, BOOL fPromptOnly, PERROR perr) { char ach[cbFILE_NAME_MAX]; int cch; int cLoop=0; char chDrive; BOOL fCabinetExists; FDICABINETINFO fdici; INT_PTR hfCab; char ch; int i=0; AssertSess(psess); do { cLoop++; // Count loops ErrClear(perr); // Make sure no error is set //** Construct fully qualified cabinet file path if (!catDirAndFile(ach, // Buffer for output filespec sizeof(ach), // Size of output buffer pszPath, // Path pszFile, // Filename NULL, // Don't have alternate name perr)) { return FALSE; // Abort with error } //** Check cabinet only if asked if (!fPromptOnly) { //** Make sure cabinet is the one we want if (-1 == (hfCab = wrap_open(ach,_O_BINARY | _O_RDONLY,0))) { ErrSet(perr,pszEXTERR_CANNOT_OPEN_FILE,"%s",ach); } else if (!FDIIsCabinet(psess->hfdi,hfCab,&fdici)) { if (!ErrIsError(perr)) { // Have to set error message ErrSet(perr,pszEXTERR_NOT_A_CABINET,"%s",ach); } } else if ((fdici.setID != setID) || (fdici.iCabinet != iCabinet)) { ErrSet(perr,pszFDIERR_WRONG_CABINET,"%s",ach); } //** Close the cabinet file (if we got it opened) if (hfCab != -1) { wrap_close(hfCab); fCabinetExists = TRUE; } else { fCabinetExists = FALSE; } //** Did we get the cabinet we wanted? if (!ErrIsError(perr)) { return TRUE; // Yup, return success } //** Don't show message if first time and cabinet not there, // since this is the common case cabinets on separate floppy // disks, and we don't want to whine before we ask them to // insert the right floppy. // if ((cLoop > 1) || fCabinetExists) { MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach); printf("\n%s\n",psess->achMsg); } } //** Tell user what we want if (IsPathRemovable(ach,&chDrive)) { MsgSet(psess->achMsg,pszEXT_FLOPPY_PROMPT,"%s%s%c", pszFile,pszLabel,chDrive); } else { MsgSet(psess->achMsg,pszEXT_NOFLOPPY_PROMPT,"%s%s%c", pszFile,pszLabel); } printf("%s\n",psess->achMsg); //** Get response do { ch = getchar(); ach[i++]=ch; if( i >= cbFILE_NAME_MAX ) break; }while( ch!='\n' ); ach[i-1]=0; /* if (!gets(ach)) { // Error or EOF ErrSet(perr,pszEXTERR_ABORT); return -1; // User abort } */ if (strlen(ach) > 0) { strcpy(pszPath,ach); // Update path cch = strlen(pszPath); //** Make sure path has path separator, since FDI requires it cch += appendPathSeparator(&(pszPath[cch-1])); //** Update path for next FDICopy() call! if (cch >= sizeof(psess->achCabPath)) { Assert(0); return -1; // Path too big } strcpy(psess->achCabPath,pszPath); } } while (fLoop); //** Did not guarantee desired cabinet return FALSE; } /* ensureCabinet() */ /*** doGetNextCab - Get next cabinet * * Make sure requested cabinet is available. * * Entry: * fdint - type of notification * pfdin - data for notification * * Exit-Success: * Returns anything but -1; * * Exit-Failure: * Returns -1 => abort FDICopy() call. */ FNFDINOTIFY(doGetNextCab) { char ach[cbFILE_NAME_MAX]; static int cErrors=0; // Count of errors for single attempt PERROR perr; int rc; #ifdef SMALL_DOS PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv; static char szCabPath[cbFILE_NAME_MAX]; static char szCabFile[cbFILE_NAME_MAX]; static char szCabLabel[cbFILE_NAME_MAX]; #else PSESSION psess=(PSESSION)pfdin->pv; #endif AssertSess(psess); perr = psess->perr; #ifdef SMALL_DOS _fstrcpy(psess->achCabinetFile,pfdin->psz1); #else strcpy(psess->achCabinetFile,pfdin->psz1); #endif //** Skip "starts in ..." messages for subsequent cabinets psess->fContinuationCabinet = TRUE; //** Keep track of GetNextCabinet calls so we can determine // what cabinet to expect next. psess->fNextCabCalled = TRUE; //** If there is no problem to report, just let FDI do the checks if (pfdin->fdie == FDIERROR_NONE) { cErrors = 0; return 0; } //** If FDI didn't get the correct cabinet last time it called us, // it calls us again with a specific error code. Tell the user // something intelligible. // // pfdin->psz1 = cabinet filename // pfdin->psz2 = disk user-readable name // pfdin->psz3 = current cabinet path #ifdef SMALL_DOS _fstrcpy(szCabFile ,pfdin->psz1); _fstrcpy(szCabLabel,pfdin->psz2); _fstrcpy(szCabPath ,pfdin->psz3); #else #define szCabFile pfdin->psz1 #define szCabLabel pfdin->psz2 #define szCabPath pfdin->psz3 #endif cErrors++; // Count of errors on this cabinet switch (pfdin->fdie) { case FDIERROR_USER_ABORT: Assert(0); //** Should never be called with this error code break; default: //** Construct full path name of cabinet if (!catDirAndFile(ach, // Buffer for output filespec sizeof(ach), // Size of output buffer szCabPath, // Path szCabLabel, // Filename s/b szCabFile? NULL, // Don't have alternate name perr)) { return -1; // Abort with error } //** Construct error string mapFDIError(perr,psess,ach,&(psess->erf)); //** Reset error psess->erf.erfOper = FDIERROR_NONE; } /* switch */ //** Tell user what the problem is, except in the case where the // file was not found *and* this was the first try at finding // the cabinet. if ((cErrors > 1) || (pfdin->fdie != FDIERROR_CABINET_NOT_FOUND)) { MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach); printf("\n%s\n",psess->achMsg); } //** Tell user to swap disks or type in new path rc = ensureCabinet(psess, szCabPath, // cabinet path cbFILE_NAME_MAX, szCabFile, // cabinet file name szCabLabel, // User-readable label pfdin->setID, // Required setID pfdin->iCabinet, // Required iCabinet FALSE, // Do not loop TRUE, // Skip check, just prompt perr); #ifdef SMALL_DOS //** Copy possibly modified cabinet path back to FDI structure _fstrcpy(pfdin->psz3,szCabPath); #endif //** Return result return rc; } /* doGetNextCab() */ /*** fdiDecryptDir - Callback from FDICopy for decryption * * <<< Just indicate calls made >>> * * NOTE: See fdi.h for details. * * Entry: * pfdid - data for decryption * * Exit-Success: * Return TRUE; * * Exit-Failure: * Return -1; */ FNFDIDECRYPT(fdiDecryptDir) { PERROR perr; #ifdef SMALL_DOS PSESSION psess=(PSESSION)(void *)(short)(long)pfdid->pvUser; #else PSESSION psess=(PSESSION)pfdid->pvUser; #endif AssertSess(psess); perr = psess->perr; //** Bail out if we're not supposed to show info if (!psess->fShowReserveInfo) { return TRUE; } switch (pfdid->fdidt) { case fdidtNEW_CABINET: MsgSet(psess->achMsg,pszEXT_DECRYPT_HEADER, "%08lx%u%x%d", pfdid->cabinet.pHeaderReserve, pfdid->cabinet.cbHeaderReserve, pfdid->cabinet.setID, pfdid->cabinet.iCabinet); printf("%s\n",psess->achMsg); break; case fdidtNEW_FOLDER: MsgSet(psess->achMsg,pszEXT_DECRYPT_FOLDER, "%08lx%u%d", pfdid->folder.pFolderReserve, pfdid->folder.cbFolderReserve, pfdid->folder.iFolder); printf("%s\n",psess->achMsg); break; case fdidtDECRYPT: MsgSet(psess->achMsg,pszEXT_DECRYPT_DATA, "%08lx%u%08lx%u%d%u", pfdid->decrypt.pDataReserve, pfdid->decrypt.cbDataReserve, pfdid->decrypt.pbData, pfdid->decrypt.cbData, pfdid->decrypt.fSplit, pfdid->decrypt.cbPartial); printf("%s\n",psess->achMsg); break; default: printf("UNKNOWN DECRYPT COMMAND: %d\n",pfdid->fdidt); return -1; // Abort }; return TRUE; } /* fdiDecryptDir() */ /*** fdiDecryptExt - Callback from FDICopy for real decryption * * NOTE: See fdi.h for details. * * Entry: * pfdid - data for decryption * * Exit-Success: * Return TRUE; * * Exit-Failure: * Return -1; */ FNFDIDECRYPT(fdiDecryptExt) { return fdiDecryptDir(pfdid); } /* fdiDecryptExt() */ /*** checkWildMatchs - Check filespec against list of filespec patterns * * Entry: * psess - SESSION -- has list of filespec patterns * pszFile - Filespec to test (may have path characters) * perr - ERROR structure * * Exit-Success: * Returns TRUE, pszFile matched a pattern * * Exit-Failure: * Returns FALSE, pszFile did not match a pattern -or- an error occurred. * Use ErrIsError(perr) to determine if an error occured. * * 11/12/99 msliger Added PatternMatch(). For backwards compatibility, * if the given wildspec contained no path separators, use only the * base filename/extension. If path separators exist in the * wildspec, use the full pathname from the cab. * FEATURE: PatternMatch is not MBCS-enabled. But then, even with the * CharIncr() refinements, the existing IsWildMatch didn't work either * (trail bytes were not compared.) */ BOOL checkWildMatches(PSESSION psess, char *pszFile, PERROR perr) { HFILESPEC hfspec; // Use to walk list of file patterns char *pszNameExt; // Name.Ext piece char *pszWild; // Filespec pattern int fAllowImpliedDot; // TRUE iff no dot in base name char *psz; // either pszNameExt or pszFile //** Get name.ext piece pszNameExt = getJustFileNameAndExt(pszFile,perr); if (!pszNameExt) { return FALSE; // perr already filled in } if (strchr(pszNameExt, '.') == NULL) { fAllowImpliedDot = TRUE; // allows *.* to match "hosts" } else { fAllowImpliedDot = FALSE; // base name has it's own dot } //** Loop through list of filespec patterns hfspec = FLFirstFile(psess->hflist); Assert(hfspec != NULL); // Skip over cabinet filespec hfspec = FLNextFile(hfspec); Assert(hfspec != NULL); // First wildcard spec while (hfspec != NULL) { // Check patterns pszWild = FLGetSource(hfspec); Assert(pszWild!=NULL); Assert(*pszWild); if (strchr(pszWild, '\\') == NULL) { // path in wildspec? psz = pszNameExt; // no path, match base name } else { psz = pszFile; // path given, match full name if (*pszWild == '\\') { pszWild++; // implied leading '\' in CAB } } if (PatternMatch(psz,pszWild,fAllowImpliedDot)) { return TRUE; // Got a match! } hfspec = FLNextFile(hfspec); // Try next pattern } //** Failure -- none of the patterns matched return FALSE; } /* checkWildMatches() */ /*** fdiAlloc - memory allocator for FDI * * Entry: * cb - size of block to allocate * * Exit-Success: * returns non-NULL pointer to block of size at least cb. * * Exit-Failure: * returns NULL */ FNALLOC(fdiAlloc) { void HUGE *pv; //** Do allocation #ifdef BIT16 pv = _halloc(cb,1); // Use 16-bit function #else // !BIT16 pv = malloc(cb); // Use 32-bit function #endif // !BIT16 //** Remember if error occured, to improve quality of error messages if (pv == NULL) { psessG->se = seNOT_ENOUGH_MEMORY; } //** Return buffer (or failure indication) return pv; } /* fdiAlloc() */ /*** fdiFree - memory free function for FDI * * Entry: * pv - memory allocated by fciAlloc to be freed * * Exit: * Frees memory */ FNFREE(fdiFree) { #ifdef BIT16 //** Use 16-bit function _hfree(pv); #else // !BIT16 //** Use 32-bit function free(pv); #endif // !BIT16 } /*** STATELOC - States for /L (location) parsing * */ typedef enum { slNONE, // No /L seen slEXPECTING, // Just saw /L, need a location slGOT, // We have parsed "/L location" } STATELOC; /* sl */ /*** parseCommandLine - Parse the command line arguments * * Entry: * psess - SESSION * cArg - Count of arguments, including program name * apszArg - Array of argument strings * perr - ERROR structure * * Exit-Success: * Returns TRUE, psess filled in. * * Exit-Failure: * Returns actBAD, perr filled in with error. */ BOOL parseCommandLine(PSESSION psess, int cArg, char *apszArg[], PERROR perr) { int cFile=0; // Count of non-directive file names seen int ch; // Switch value int i; char *pch; STATELOC sl=slNONE; // Location parsing state AssertSess(psess); psess->act = actDEFAULT; // We don't know what we are doing; yet psess->achLocation[0] = '\0'; // Default to current directory //** Empty file handle table for (i=0; iahfSelf[i] = -1; // No open handle } //** See if we are a self-extracting cabinet if (!checkSelfExtractingCab(psess,cArg,apszArg,perr)) { return FALSE; // An error occurred, perr filled in } if (psess->fSelfExtract) { if ((cArg < 2) && (psess->cArgv > 1)) { cArg = psess->cArgv; apszArg = psess->pArgv; } if (addFileSpec(psess,psess->achSelf,perr) == NULL) { ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",psess->achSelf); return FALSE; } cFile++; // Count files } //** Parse args, skipping program name for (i=1; iact = actHELP; // Show help return TRUE; case chSWITCH_ALL: psess->fAllCabinets = TRUE; break; case chSWITCH_COPY: if (psess->act != actDEFAULT) { ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch); return FALSE; } psess->act = actCOPY; break; case chSWITCH_DIRECTORY: if (psess->act != actDEFAULT) { ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch); return FALSE; } psess->act = actDIRECTORY; break; case chSWITCH_EXTRACT: if (psess->act != actDEFAULT) { ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch); return FALSE; } psess->act = actEXTRACT; break; case chSWITCH_LOCATION: //** Make sure we only got location once if (sl == slGOT) { ErrSet(perr,pszEXTERR_LOCATION_TWICE); return FALSE; } sl = slEXPECTING; break; case chSWITCH_OVERWRITE: psess->fOverwrite = TRUE; break; case chSWITCH_RESERVE: psess->fShowReserveInfo = TRUE; break; case chSWITCH_ZAP: pch++; if (*pch == '\0') { if ((i+1) < cArg) { pch = apszArg[++i]; } else { ErrSet(perr,pszEXTERR_MISSING_LOCATION); return(FALSE); } } strcpy(psess->achZap,pch); pch = " "; // continue parse on next arg break; case chSWITCH_ONCE: psess->fDestructive++; // will require "/###" break; default: ErrSet(perr,pszEXTERR_BAD_SWITCH,"%s",apszArg[i]); return FALSE; break; } } } //** Not a command line switch else if (sl == slEXPECTING) { //** Get the location (output directory) STRCPY(psess->achLocation,apszArg[i]); // Save location sl = slGOT; // Done eating location } else { //** We have a file name, add it to our list if (addFileSpec(psess,apszArg[i],perr) == NULL) { ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",apszArg[i]); return FALSE; } cFile++; // Count files } } if (psess->fDestructive < 3) { // require 3 #'s to activate psess->fDestructive = FALSE; } if (psess->fSelfExtract) { psess->fDestructive = FALSE; // can't do it in self-extractor } //** If no arguments and not self-extracting, show help if ((cArg == 1) && !psess->fSelfExtract) { psess->act = actHELP; // Show help return TRUE; } //** Make sure no trailing /L without location if (sl == slEXPECTING) { ErrSet(perr,pszEXTERR_MISSING_LOCATION); return FALSE; } //** Make sure we got right number of arguments for COPY case if ((psess->act == actCOPY) && (cFile != 2)) { //** General purpose error, to minimize localization effort ErrSet(perr,pszEXTERR_BAD_PARAMETERS); return FALSE; } //** Make sure we got at least one filespec if (cFile == 0) { ErrSet(perr,pszEXTERR_MISSING_CABINET); return FALSE; } //** Special processing for self-extract if (psess->fSelfExtract) { psess->fAllCabinets = TRUE; // Always do all cabinets //** Force EXTRACT if no /E or /D specified if (psess->act == actDEFAULT) { psess->act = actEXTRACT; } } //** Success return TRUE; } /*** checkSelfExtractingCab - See if we are a self-extracting cabinet * * Entry: * psess - SESSION * cArg - Count of arguments, including program name * apszArg - Array of argument strings * perr - ERROR structure * * Exit-Success: * Returns TRUE, psess filled in. * psess->fSelfExtract set to TRUE if self-extracting * psess->cbSelfExtract set to EXE size if self-extracting (this is * the offset to the start of the cabinet file header). * psess->cbSelfExtractSize set to the CAB size if self-extractor * psess->achSelf set to the EXE file's name * * Exit-Failure: * Returns actBAD, perr filled in with error. * * Notes: * The strategy is to get the EXE file size indicated in the appropriate * EXE header, and if the actual size of our EXE file is larger than that, * we assume that there is a cabinet file tacked on to the end, and we * extract the files from that! * * Refer to the XIMPLANT source for details on the workings of the XIMPLANT * implementation. */ BOOL checkSelfExtractingCab(PSESSION psess, int cArg, char *apszArg[], PERROR perr) { long cbFile; // EXE file size long cbFromHeader; // Size indicated by EXE header INT_PTR hf; DWORD signature; // should be "MSCF" DWORD alignment; // file structure alignment, ie, 512 #ifdef BIT16 long info; #else // !BIT16 char achFile[cbFILE_NAME_MAX]; IMAGE_DOS_HEADER idh; // MS-DOS header IMAGE_NT_HEADERS inh; // Complete PE header IMAGE_SECTION_HEADER ish; // section header WORD iSection; struct { unsigned long signature; unsigned long reserved; unsigned long cbArgv; unsigned long cbCabinet; } magic; DWORD offDebug; // file offset of debug info DWORD cbDebug; // size of debug info IMAGE_DEBUG_DIRECTORY idd; // debug descriptor #define IMPLANT_SECTION_NAME "Ext_Cab1" /* must be 8 chars */ #endif // !BIT16 // jforbes: removed SFX CAB checking for when generating // debug executable for extract, since it thinks the debug // exe is a cab file #ifdef BIT16 #ifdef _DEBUG psess->fSelfExtract = FALSE; return TRUE; #endif #endif //** Open our EXE file #ifdef BIT16 hf = wrap_open(apszArg[0],_O_BINARY | _O_RDONLY,0); #else if (!GetModuleFileName(NULL,achFile,sizeof(achFile))) { return TRUE; } hf = wrap_open(achFile,_O_BINARY | _O_RDONLY,0); #endif if (hf == -1) { return TRUE; // Something is bogus, just skip selfex } //** Get the expected EXE file size #ifdef BIT16 //** Do it the MS-DOS way if (-1 == wrap_lseek(hf,2,SEEK_SET)) { goto Exit; } if (sizeof(info) != wrap_read(hf,&info,sizeof(info))) { goto Exit; } //** OK, we've got the page count and count of bytes in the last page; // convert to a file size. cbFromHeader = ((info>>16)-1)*512 + (info&0xFFFF); alignment = 16; #else // !BIT16 //** Do it the Win32 way //** Get MS-DOS header if (sizeof(idh) != wrap_read(hf,&idh,sizeof(idh))) { goto Exit; } //** Seek to and read NT header if (-1 == wrap_lseek(hf,idh.e_lfanew,SEEK_SET)) { goto Exit; } if (sizeof(inh) != wrap_read(hf,&inh,sizeof(inh))) { goto Exit; } //** Seek to first section header if (-1 == wrap_lseek(hf,idh.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + inh.FileHeader.SizeOfOptionalHeader, SEEK_SET)) { goto Exit; } cbFromHeader = 0; iSection = inh.FileHeader.NumberOfSections; offDebug = 0; cbDebug = inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; alignment = inh.OptionalHeader.FileAlignment; while (iSection--) { //** Read this section header if (sizeof(ish) != wrap_read(hf,&ish,sizeof(ish))) { goto Exit; } if (memcmp(ish.Name,IMPLANT_SECTION_NAME,sizeof(ish.Name)) == 0) { /* found an implanted section, use the magic */ if ((wrap_lseek(hf,ish.PointerToRawData,SEEK_SET) == -1) || (wrap_read(hf,&magic,sizeof(magic)) != sizeof(magic))) { goto Exit; } if (magic.signature == 0x4E584653) { psess->fSelfExtract = TRUE; psess->cbSelfExtract = ish.PointerToRawData + sizeof(magic) + magic.cbArgv; psess->cbSelfExtractSize = magic.cbCabinet; if (magic.cbArgv) { psess->pArgv = MemAlloc(magic.cbArgv); if ((psess->pArgv != NULL) && (wrap_read(hf,psess->pArgv,magic.cbArgv) == magic.cbArgv)) { psess->cArgv = 1; /* for [0], which is NULL */ while (psess->pArgv[psess->cArgv] != NULL) { psess->pArgv[psess->cArgv] += (LONG_PTR) psess->pArgv; (psess->cArgv)++; } } } break; } } if ((ish.PointerToRawData != 0) && (ish.SizeOfRawData != 0) && (cbFromHeader < (long) (ish.PointerToRawData + ish.SizeOfRawData))) { cbFromHeader = (long) (ish.PointerToRawData + ish.SizeOfRawData); } if (cbDebug != 0) { if ((ish.VirtualAddress <= inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) && ((ish.VirtualAddress + ish.SizeOfRawData) > inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)) { offDebug = inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress - ish.VirtualAddress + ish.PointerToRawData; } } } if (!psess->fSelfExtract && (offDebug != 0)) { if (-1 == wrap_lseek(hf,offDebug,SEEK_SET)) { goto Exit; } while (cbDebug >= sizeof(idd)) { if (sizeof(idd) != wrap_read(hf,&idd,sizeof(idd))) { goto Exit; } if ((idd.PointerToRawData != 0) && (idd.SizeOfData != 0) && (cbFromHeader < (long) (idd.PointerToRawData + idd.SizeOfData))) { cbFromHeader = (long) (idd.PointerToRawData + idd.SizeOfData); } cbDebug -= sizeof(idd); } } #endif // !BIT16 if (!psess->fSelfExtract) { //** Get actual file size cbFile = wrap_lseek(hf,0,SEEK_END); //** Modify state IF we are doing self-extract if (cbFile > cbFromHeader) { if ((cbFromHeader == wrap_lseek(hf,cbFromHeader,SEEK_SET)) && (sizeof(signature) == wrap_read(hf,&signature,sizeof(signature))) && (signature == 0x4643534DLU)) { psess->fSelfExtract = TRUE; psess->cbSelfExtract = cbFromHeader; psess->cbSelfExtractSize = cbFile - cbFromHeader; } else if ((cbFromHeader % alignment) != 0) { cbFromHeader += (alignment - 1); cbFromHeader -= (cbFromHeader % alignment); if ((cbFromHeader == wrap_lseek(hf,cbFromHeader,SEEK_SET)) && (sizeof(signature) == wrap_read(hf,&signature,sizeof(signature))) && (signature == 0x4643534DLU)) { psess->fSelfExtract = TRUE; psess->cbSelfExtract = cbFromHeader; psess->cbSelfExtractSize = cbFile - cbFromHeader; } } } } if (psess->fSelfExtract) { //** Save our file name and use it as the cabinet file name #ifdef BIT16 strcpy(psess->achSelf,apszArg[0]); // Save our name #else strcpy(psess->achSelf,achFile); // Save our name #endif } //** Success Exit: wrap_close(hf); return TRUE; } /* checkSelfExtractingCab() */ /*** addFileSpec - Add filename to session list * * Entry: * psess - Session to update * pszArg - File name to add * perr - ERROR structure * * Exit-Success: * Returns HFILESPEC, psess updated. * * Exit-Failure: * Returns NULL, perr filled in with error. */ HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr) { HFILESPEC hfspec; AssertSess(psess); //** Make sure a list exists if (psess->hflist == NULL) { if (!(psess->hflist = FLCreateList(perr))) { return FALSE; } } //** Add file to list if (!(hfspec = FLAddFile(psess->hflist, pszArg, NULL, perr))) { return NULL; } //** Success return hfspec; } /* addFileSpec() */ #ifdef ASSERT /*** fnafReport - Report assertion failure * * NOTE: See asrt.h for entry/exit conditions. */ FNASSERTFAILURE(fnafReport) { printf("\n%s:(%d) Assertion Failed: %s\n",pszFile,iLine,pszMsg); exit(1); } #endif // ASSERT /*** printError - Display error on stdout * * Entry * perr - ERROR structure to print * * Exit-Success * Writes error message to stdout. */ void printError(PSESSION psess, PERROR perr) { WCHAR szTemp[MAX_MESSAGE_SIZE]; //** Make sure error starts on a new line if (psess) { if (psess->fNoLineFeed) { fwprintf(stdout, L"\n"); psess->fNoLineFeed = FALSE; } } //** General error Assert(perr->pszFile == NULL); MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach); MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg), szTemp, MAX_MESSAGE_SIZE); *(szTemp+strlen(psess->achMsg)) = '\0'; fwprintf( stderr, szTemp ); } /* printError() */ /*** pszFromMSDOSTime - Convert MS-DOS file date/time to string * * Entry: * psz - Buffer to receive formatted date/time * cb - Length of psz buffer * date - MS-DOS FAT file system date format (see below) * time - MS-DOS FAT file system time format (see below) * * Exit: * *psz filled in * * NOTE: This is the interpretation of the MS-DOS date/time values: * * Time Bits cBits Meaning * --------- ----- ---------------------------------------- * 0 - 4 5 Number of two-second increments (0 - 29) * 5 - 10 6 Minutes (0 - 59) * 11 - 15 5 Hours (0 - 23) * * Date Bits cBits Meaning * --------- ----- ---------------------------------------- * 0 - 4 5 Day (1 - 31) * 5 - 8 4 Month (1 - 12) * 9 - 15 7 Year since 1980 (for example, 1994 is stored as 14) */ void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time) { int sec; int min; int hour; int day; int month; int year; char *pszAMPM; // AM/PM string sec = (time & 0x1f) << 1; // 0, 2, ..., 58 min = (time >> 5) & 0x3f; // 0, 1, ..., 59 hour = (time >> 11) & 0x1f; // 0, 1, ..., 23 //** Determine 12-hour vs. 24-hour clock if (strlen(pszEXT_TIME_PM) == 0) { //** 24 hour clock Assert(strlen(pszEXT_TIME_AM) == 0); pszAMPM = pszEXT_TIME_PM; } else { //** Get am/pm extension, and map 0 to 12 if (hour >= 12) { pszAMPM = pszEXT_TIME_PM; hour -= 12; } else { pszAMPM = pszEXT_TIME_AM; } if (hour == 0) { hour = 12; } } day = (date & 0x1f); month = (date >> 5) & 0x0f; year = ((date >> 9) & 0x7f) + 1980; MsgSet(psz,pszEXT_DATE_TIME, "%02d%02d%02d%2d%02d%02d%s", month, day, year, hour, min, sec, pszAMPM); } /* pszFromMSDOSTime() */ /*** pszFromAttrFAT - Convert FAT file attributes to string * * Entry: * attrFAT - file attributes * * Exit: * *psz filled in "----".."A-R-".."AHRS" */ void pszFromAttrFAT(char *psz, int cb, WORD attrFAT) { STRCPY(psz,"----"); if (attrFAT & _A_ARCH) psz[0] = 'A'; if (attrFAT & _A_HIDDEN) psz[1] = 'H'; if (attrFAT & _A_RDONLY) psz[2] = 'R'; if (attrFAT & _A_SYSTEM) psz[3] = 'S'; return; } /* pszFromAttrFAT() */ /*** mapFDIError - Create error message from FDI error codes * * Entry: * perr - ERROR structure to recieve message * psess - Our context * pszCabinet - Cabinet file being processed * perf - FDI error structure * * Exit: * perr filled in with formatted message */ void mapFDIError(PERROR perr,PSESSION psess, char *pszCabinet, PERF perf) { switch (perf->erfOper) { case FDIERROR_NONE: Assert(0); break; case FDIERROR_CABINET_NOT_FOUND: ErrSet(perr,pszFDIERR_CAB_NOT_FOUND,"%s",pszCabinet); break; case FDIERROR_NOT_A_CABINET: ErrSet(perr,pszFDIERR_NOT_A_CABINET,"%s",pszCabinet); break; case FDIERROR_UNKNOWN_CABINET_VERSION: ErrSet(perr,pszFDIERR_BAD_CAB_VER,"%s%04x",pszCabinet,perf->erfType); break; case FDIERROR_CORRUPT_CABINET: ErrSet(perr,pszFDIERR_CORRUPT_CAB,"%s",pszCabinet); break; case FDIERROR_ALLOC_FAIL: ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet); break; case FDIERROR_BAD_COMPR_TYPE: ErrSet(perr,pszFDIERR_BAD_COMPR_TYPE,"%s",pszCabinet); break; case FDIERROR_MDI_FAIL: //** Improve detail of failure message switch (psess->se) { case seNONE: //** Some other decompression error (corrupted data?) ErrSet(perr,pszFDIERR_MDI_FAIL,"%s",pszCabinet); break; case seNOT_ENOUGH_MEMORY: //** Not enough RAM for decompressor itself ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet); break; case seCANNOT_CREATE: //** Could not create a Quantum temporary spill file ErrSet(perr,pszFDIERR_SPILL_CREATE,"%s%s",pszCabinet,achSpillFile); break; case seNOT_ENOUGH_SPACE: //** TMP directory did not have enough space for Quantum // spill file. ErrSet(perr,pszFDIERR_SPILL_SIZE,"%s%s%ld",pszCabinet, achSpillFile, psess->cbSpill); break; default: Assert(0); } break; case FDIERROR_TARGET_FILE: ErrSet(perr,pszFDIERR_TARGET_FILE,"%s%s",psess->achFile,pszCabinet); break; case FDIERROR_RESERVE_MISMATCH: ErrSet(perr,pszFDIERR_RESERVE_MISMATCH,"%s",pszCabinet); break; case FDIERROR_WRONG_CABINET: ErrSet(perr,pszFDIERR_WRONG_CABINET,"%s",pszCabinet); break; case FDIERROR_USER_ABORT: ErrSet(perr,pszFDIERR_USER_ABORT,"%s",pszCabinet); break; default: ErrSet(perr,pszFDIERR_UNKNOWN_ERROR,"%d%s",perf->erfOper,pszCabinet); break; } } /* mapFDIError() */ /*** wrap_close - close an open file * */ int FAR DIAMONDAPI wrap_close(INT_PTR fh) { int i; int rc; #ifdef SMALL_DOS rc = _dos_close((HFILE)fh); if (rc != 0) { // Map random MS-DOS error code to -1 failure rc = -1; } #else rc = _close((HFILE)fh); #endif //** See if we have to destroy the spill file if (fh == hfSpillFile) { _unlink(achSpillFile); // Delete spill file hfSpillFile = -1; // Remember spill file is gone } //** Take handle off list if we are self-extracting if (psessG->fSelfExtract) { //** See if this is a handle to our EXE/cabinet file; for (i=0; (iahfSelf[i] != (HFILE)fh); i++) { ; } if (i < cMAX_CAB_FILE_OPEN) { // Found a match psessG->ahfSelf[i] = -1; // Take it off our list dbg( printf("\nDBG: Close self as handle %d (slot %d)\n",(HFILE)fh,i) ); } } #ifdef WIN32GUI if (fh == hCabFile1) { hCabFile1 = hCabFile2; ibCabFilePosition1 = ibCabFilePosition2; hCabFile2 = -1; if (hCabFile1 == -1) { cbCabFileTotal = 0; cbCabFileMax = 0; iPercentLast = -1; } } else if (fh == hCabFile2) { hCabFile2 = -1; } #endif //** Done dbg( printf("DBG: %d=CLOSE on handle %d\n",rc,(HFILE)fh) ); return rc; } /* wrap_close */ /*** wrap_lseek - seek on a file * */ long FAR DIAMONDAPI wrap_lseek(INT_PTR fh, long pos, int func) { long cbAdjust=0; // Assume file is 0-based int i; long rc; long cbLimit=0; // assume no length clipping //** See if we are self-extracting if (psessG->fSelfExtract) { //** See if this is a handle to our EXE/cabinet file; for (i=0; (iahfSelf[i] != (HFILE)fh); i++) { ; } if (i < cMAX_CAB_FILE_OPEN) { // Found a match cbAdjust = psessG->cbSelfExtract; // So return value gets adjusted cbLimit = psessG->cbSelfExtractSize; // known CAB image size if (func == SEEK_SET) { // Need to adjust absolute position pos += cbAdjust; // Shift up to account for EXE dbg(printf("\nDBG: Seek self to %ld as handle %d (slot %d)\n",pos,(HFILE)fh,i)); } } } #ifdef SMALL_DOS rc = _dos_seek((HFILE)fh,pos,func); #else rc = _lseek((HFILE)fh,pos,func); #endif //** If seek didn't fail, adjust return value for self-extract case if (rc != -1) { rc -= cbAdjust; if ((cbLimit != 0) && (rc > cbLimit)) { rc = cbLimit; } } #ifdef WIN32GUI if (fh == hCabFile1) { ibCabFilePosition1 = rc; } else if (fh == hCabFile2) { ibCabFilePosition2 = rc; } #endif dbg( printf("DBG: %ld=LSEEK on handle %d, pos=%ld, func=%d\n",rc,(HFILE)fh,pos,func) ); return rc; } /* wrap_lseek() */ /*** wrap_open - open a file * */ INT_PTR FAR DIAMONDAPI wrap_open(char FAR *sz, int mode, int share) { int i; int rc; char FAR *psz; PFDISPILLFILE pfdisf; // FDI spill file info #ifdef SMALL_DOS int ignore; char szLocal[cbFILE_NAME_MAX]; #endif //** See if FDI is asking for a spill file (for Quantum) if (*sz == '*') { // Yes, we need to create a spill file Assert(hfSpillFile == -1); // Only support one at a time achSpillFile[0] = '\0'; // No name constructed, yet pfdisf = (PFDISPILLFILE)sz; // Get pointer to spill file size //** Try boot drive if no TEMP variable defined // NOTE: We assume the boot drive is more likely to be writeable // than the current directory, since the customer may be // running EXTRACT.EXE off a write-protected floppy (which // also wouldn't have enough space), or a read-only network // drive. psz = _tempnam(getBootDrive(),"esf"); // Get a temporary file name if (psz == NULL) { psessG->se = seCANNOT_CREATE; return -1; // Could not create } strcpy(achSpillFile,psz); // Remember name for wrap_close free(psz); // Free temporary name buffer mode = _O_CREAT | _O_BINARY | _O_RDWR; // Force open mode psz = achSpillFile; // Use spill file name } else { psz = (char FAR *)sz; // Use passed-in name } //** Open/create file #ifdef SMALL_DOS _fstrcpy(szLocal,psz); if (mode & _O_CREAT) { ignore = _dos_creat(szLocal,_A_NORMAL,&rc); } else { //** Keep only relevant bits for _dos_open! mode &= _O_RDONLY | _O_WRONLY | _O_RDWR; ignore = _dos_open(szLocal,mode,&rc); } if (ignore != 0) { rc = -1; } #else rc = _open(psz,mode,share); #endif //** If this is the spill file, make sure the file was created, // make sure it is the requested size, and save the handle. // If we cannot do this, we set a flag to remember what the // problem was, so that we can report the error intelligently. if (*sz == '*') { // Need to size spill file if (-1 == rc) { // Could not create it psessG->se = seCANNOT_CREATE; return (INT_PTR)rc; } //** Remember file handle, so that wrap_close can do the delete hfSpillFile = rc; //** Don't need to seek/write if zero length requested if (pfdisf->cbFile > 0) { //** Seek to size minus 1 if (-1L == wrap_lseek(rc,pfdisf->cbFile-1,SEEK_SET)) { psessG->se = seNOT_ENOUGH_SPACE; psessG->cbSpill = pfdisf->cbFile; wrap_close(rc); // Close and destroy spill file return -1; } //** Write one byte if (1 != wrap_write(rc,"b",1)) { psessG->se = seNOT_ENOUGH_SPACE; psessG->cbSpill = pfdisf->cbFile; wrap_close(rc); return -1; } } //** Spill file created successfully psessG->se = seNONE; // No error } #ifndef BIT16 #define _fstricmp(a,b) stricmp(a,b) #endif else if (psessG->fSelfExtract && !_fstricmp(sz,psessG->achSelf)) { //** Self-extracting and this is our EXE/cabinet file; // Find a slot to store the file handle. for (i=0; (iahfSelf[i] != -1); i++) { ; } if (i >= cMAX_CAB_FILE_OPEN) { Assert(0); wrap_close(rc); return -1; } dbg( printf("\nDBG: Opened self (%s) as handle %d (slot %d)\n",sz,rc,i) ); //** Save the new handle psessG->ahfSelf[i] = rc; //** Position the file handle to the start of the cabinet file header! // NOTE: Since we just added the handle to the list, wrap_lseek() // will know to do the EXE size adjustment! wrap_lseek(rc,0,SEEK_SET); } #ifdef WIN32GUI if ((mode == (_O_RDONLY|_O_BINARY)) && (rc != -1)) { //* If this is the CAB file, track it's handle and get the file's size // REARCHITECT: this is done this way in the interest of development time. // I'm using _O_RDONLY to identify that it's the CAB file being // opened, and I'm depending upon there being no more than two such // handles. if (hCabFile1 == -1) { hCabFile1 = rc; if (psessG->fSelfExtract) { cbCabFileTotal = psessG->cbSelfExtractSize; } else { cbCabFileTotal = _filelength((HFILE)hCabFile1); } cbCabFileMax = 0; ibCabFilePosition1 = 0; cbCabFileScale = 1; while (cbCabFileTotal > 10000000) { cbCabFileTotal /= 10; cbCabFileScale *= 10; } iPercentLast = -1; if (g_hwndProgress == NULL) { g_hwndProgress = CreateDialog(g_hinst, MAKEINTRESOURCE(DLG_PROGRESS), NULL, ProgressWndProc); } } else if (hCabFile2 == -1) { hCabFile2 = rc; ibCabFilePosition2 = 0; } } #endif //** Done dbg( printf("DBG: %d=OPEN file %s, mode=%d, share=%d\n",rc,sz,mode,share) ); return (INT_PTR)rc; } /* wrap_open() */ /*** wrap_read - read a file * */ UINT FAR DIAMONDAPI wrap_read(INT_PTR fh, void FAR *pb, unsigned int cb) { int rc; #ifdef SMALL_DOS UINT ignore; ignore = _dos_read((HFILE)fh,pb,cb,&rc); if (ignore != 0) { rc = -1; } #else rc = _read((HFILE)fh,pb,cb); #endif #ifdef WIN32GUI if (fh == hCabFile1) { ibCabFilePosition1 += rc; if (ibCabFilePosition1 > cbCabFileMax) { cbCabFileMax = ibCabFilePosition1; ProgressReport(cbCabFileMax); } } else if (fh == hCabFile2) { ibCabFilePosition2 += rc; if (ibCabFilePosition2 > cbCabFileMax) { cbCabFileMax = ibCabFilePosition2; ProgressReport(cbCabFileMax); } } #endif dbg( printf("DBG: %d=READ on handle %d, pb=%08lx, cb=%u\n",rc,(HFILE)fh,pb,cb) ); return rc; } /* wrap_read() */ /*** wrap_write - write a file * * Iff cb == 0, truncate the file at the current position. * (Needed for FDITruncateCabinet.) */ UINT FAR DIAMONDAPI wrap_write(INT_PTR fh, void FAR *pb, unsigned int cb) { int rc; #ifdef SMALL_DOS UINT ignore; ignore = _dos_write((HFILE)fh,pb,cb,&rc); if (ignore != 0) { rc = -1; } #else if (cb == 0) { rc = _chsize((HFILE)fh,_lseek((HFILE)fh,0,SEEK_CUR)); } else { rc = _write((HFILE)fh,pb,cb); } #endif dbg( printf("DBG: %d=WRITE on handle %d, pb=%08lx, cb=%u\n",rc,(HFILE)fh,pb,cb) ); return rc; } /* wrap_write() */ /*** getBootDrive - Returns boot drive path (e.g., "C:\") * * Entry: * none * * Exit: * Returns pointer to static buffer with bootdrive ("C:\") */ char *getBootDrive(void) { char ch; char *psz; static char szBootDrive[]="C:\\"; //** Default to Drive C *szBootDrive = 'C'; //** Get COMSPEC -- we're assuming it's drive letter is the boot drive! psz = getenv("COMSPEC"); if ( psz && // COMSPEC exists *psz && // It is not empty (*(psz+1) == ':')) { // Has the right format -- "?:..." //** We could try to validate that this is really the boot drive, // but we'll just trust that COMSPEC is correct. A test for the // drive being in the range A..C would work for the US, but in // Japan the boot drive can be anything between A..G, and maybe // even higher. So, we'll just make sure it's a drive letter. ch = (char)tolower(*psz); if (('a' <= ch) && (ch <= 'z')) { *szBootDrive = ch; // Use COMSPEC drive letter } } //** Return path of root of boot drive return szBootDrive; } /* getBootDrive() */ #ifdef BIT16 //** Get Changeline fix code //APPCOMPAT: 14-Dec-1994 bens Include *.c file to avoid makefile change! #include "fixchg.c" #endif #ifdef WIN32GUI int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { int result; char *pchCommand; char *argv[50]; int argc; enum { WHITESPACE, UNQUOTED, QUOTED } eState = WHITESPACE; g_hinst = hInstance; pchCommand = strdup(lpCmdLine); /* work on a copy */ argv[0] = ""; /* no EXE name supplied */ argc = 1; /* that was one */ if (pchCommand) { while (*pchCommand) /* walk the string */ { switch (eState) { case WHITESPACE: if (*pchCommand <= ' ') { /* ignore it */ } else if (*pchCommand == '\"') { argv[argc++] = pchCommand + 1; /* skip quote */ eState = QUOTED; } else { argv[argc++] = pchCommand; eState = UNQUOTED; } break; case UNQUOTED: if (*pchCommand <= ' ') { *pchCommand = '\0'; /* nul-terminate */ eState = WHITESPACE; } else { /* keep moving up */ } break; case QUOTED: if (*pchCommand == '\"') { *pchCommand = '\0'; /* turn quote to a nul */ eState = WHITESPACE; } else { /* keep moving up */ } break; } pchCommand++; } } argv[argc] = NULL; /* NULL-terminate the list */ InitCommonControls(); result = main(argc, argv); /* run Extract */ if (g_hwndProgress != NULL) { DestroyWindow(g_hwndProgress); g_hwndProgress = NULL; } return(result); /* return the result */ } static void ProgressReport(unsigned long cbCabFileMax) { MSG msg; int iPercent; if ((cbCabFileTotal > 0) && (g_hwndProgress != NULL)) { cbCabFileMax /= cbCabFileScale; if (cbCabFileMax > cbCabFileTotal) { cbCabFileMax = cbCabFileTotal; } cbCabFileMax *= 100; iPercent = (int) (cbCabFileMax / cbCabFileTotal); if (iPercent != iPercentLast) { SendDlgItemMessage(g_hwndProgress, IDC_PROGRESS, PBM_SETPOS, iPercent, 0); while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { DispatchMessage(&msg); } iPercentLast = iPercent; } } } LRESULT CALLBACK ProgressWndProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_INITDIALOG: SendDlgItemMessage(hdlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 99)); EnableMenuItem(GetSystemMenu(hdlg, FALSE), SC_CLOSE, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED); return TRUE; } return 0; } #endif /* WIN32GUI */