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
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 */
|