Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3232 lines
105 KiB

/*** 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 <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#include <errno.h>
#include <direct.h>
#include <conio.h>
#ifdef BIT16
#include <dos.h>
#include "fixchg.h"
#else // !BIT16
//** Get minimal Win32 definitions
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#undef ERROR // Override "#define ERROR 0" in wingdi.h
#endif // !BIT16
#ifdef WIN32GUI
#include <commctrl.h>
#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 <extract.msg> // 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; dw<cArg; dw++ )
{
szTempArg =(LPSTR) malloc( wcslen(apszArg[dw] ) );
if( NULL == szTempArg )
{
fwprintf( stderr, L"ERROR: Memory Allocation failed.\n" );
if( szArg != NULL )
{
free(szArg );
szArg = NULL;
}
//** Free resources
AssertSess(psess);
ClearAssertSignature((psess));
MemFree(psess);
return EXIT_FAILURE;
}
ZeroMemory( szTempArg, wcslen(apszArg[dw]) );
if( FALSE == WideCharToMultiByte( CP_THREAD_ACP, 0, apszArg[dw], wcslen(apszArg[dw]),
szTempArg, wcslen(apszArg[dw]) , NULL, NULL ) )
fwprintf( stderr, L"Error\n" );
*(szTempArg+wcslen(apszArg[dw])) = '\0';
szArg[dw] = szTempArg;
}
if (!parseCommandLine(psess,(int)cArg,szArg,&err)) {
printError(psess,&err);
if( szArg != NULL )
{
free(szArg );
szArg = NULL;
}
if( szTempArg != NULL )
{
free(szTempArg );
szTempArg = NULL;
}
//** Free resources
AssertSess(psess);
ClearAssertSignature((psess));
MemFree(psess);
return 1;
}
/*
//free the memory for szArg
for( dw=0; dw<cArg; dw++ )
{
szTempArg = szArg[dw];
if( szTempArg != NULL )
free(szTempArg );
} */
if( szTempArg != NULL )
{
free(szTempArg );
szTempArg = NULL;
}
if( szArg != NULL )
{
free(szArg );
szArg = NULL;
}
// szArg = NULL;
//** Quick out if command line help is requested
if (psess->act == 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; i<cMAX_CAB_FILE_OPEN; i++) {
psess->ahfSelf[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; i<cArg; i++) {
if ((apszArg[i][0] == chSWITCH1) ||
(apszArg[i][0] == chSWITCH2) ) {
//** Have a switch to parse, make sure switch is OK here
if (sl == slEXPECTING) {
ErrSet(perr,pszEXTERR_MISSING_LOCATION);
return FALSE;
}
//** Process switches (support string to ease typing)
for (pch=&apszArg[i][1]; *pch; pch++) {
ch = toupper(*pch); // Switch character
switch (ch) {
case chSWITCH_HELP:
psess->act = 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;
(i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[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;
(i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[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;
(i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[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 */