mirror of https://github.com/lianthony/NT4.0
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.
4159 lines
136 KiB
4159 lines
136 KiB
/*** diamond.c - Main program for DIAMOND.EXE
|
|
*
|
|
* Microsoft Confidential
|
|
* Copyright (C) Microsoft Corporation 1993-1994
|
|
* All Rights Reserved.
|
|
*
|
|
* Author:
|
|
* Benjamin W. Slivka
|
|
*
|
|
* History:
|
|
* 10-Aug-1993 bens Initial version
|
|
* 11-Aug-1993 bens Improved assertion checking technology
|
|
* 14-Aug-1993 bens Removed message test code
|
|
* 20-Aug-1993 bens Add banner and command-line help
|
|
* 21-Aug-1993 bens Add pass 1 and pass 2 variable lists
|
|
* 10-Feb-1994 bens Start of real pass 1/2 work
|
|
* 11-Feb-1994 bens .SET and file copy commands -- try calling FCI!
|
|
* 14-Feb-1994 bens Call FCI for the first time - it works!
|
|
* 15-Feb-1994 bens Support /D and single-file compression
|
|
* 16-Feb-1994 bens Update for improved FCI interfaces; disk labels;
|
|
* ensure output directories exist
|
|
* 20-Feb-1994 bens Move general file routines to fileutil.* so
|
|
* extract.c can use them.
|
|
* 23-Feb-1994 bens Generate INF file
|
|
* 28-Feb-1994 bens Supply new FCI tempfile callback
|
|
* 01-Mar-1994 bens Add timing and generate summary report file
|
|
* 15-Mar-1994 bens Add RESERVE support
|
|
* 21-Mar-1994 bens Updated to renamed FCI.H definitions
|
|
* 22-Mar-1994 bens Add english error messages for FCI errors
|
|
* 28-Mar-1994 bens Add cabinet setID support
|
|
* 29-Mar-1994 bens Fix bug in compressing files w/o extensions
|
|
* 30-Mar-1994 bens Layout files outside of cabinets
|
|
* 18-Apr-1994 bens Add /L switch
|
|
* 20-Apr-1994 bens Fix cabinet/disk size accounting
|
|
* 21-Apr-1994 bens Print out c run-time errno in FCI failures
|
|
* 22-Apr-1994 bens Add checking for unique file names in cabinet set
|
|
* 03-May-1994 bens Add customizable INF stuff
|
|
* 27-May-1994 bens Add Quantum support
|
|
* 03-Jun-1994 bens Add .Option Explicit, .Define support
|
|
* 13-Jul-1994 bens Add DoNotCopyFiles
|
|
* 27-Jul-1994 bens Support quotes in .InfWrite[Xxx]; /SIZE qualifier
|
|
* for reserving space.
|
|
* 14-Dec-1994 bens Implement *csum* support
|
|
*/
|
|
|
|
#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>
|
|
|
|
#ifdef BIT16
|
|
#include <dos.h>
|
|
#else // !BIT16
|
|
//** Get minimal Win32 definitions
|
|
// In particular, variable.h defines VARTYPE and VARFLAGS, which
|
|
// the OLE folks have also defined for OLE automation.
|
|
//#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#undef ERROR // Override stupid "#define ERROR 0" in wingdi.h
|
|
#endif // !BIT16
|
|
|
|
#include "types.h"
|
|
#include "asrt.h"
|
|
#include "error.h"
|
|
#include "mem.h"
|
|
#include "message.h"
|
|
|
|
#include "dfparse.h"
|
|
#include "inf.h"
|
|
#include "filelist.h"
|
|
#include "fileutil.h"
|
|
#include "misc.h"
|
|
#include "glist.h"
|
|
|
|
#include "diamond.msg"
|
|
#include "dfparse.msg" // Get standard variable names
|
|
|
|
#include "crc32.h"
|
|
|
|
|
|
#ifdef BIT16
|
|
#include "chuck\fci.h"
|
|
#else // !BIT16
|
|
#include "chuck\nt\fci.h"
|
|
#endif // !BIT16
|
|
|
|
#ifndef BIT16
|
|
#include "filever.h"
|
|
#endif // !BIT16
|
|
|
|
|
|
//** Macros
|
|
|
|
#define cbDF_BUFFER 4096 // Buffer size for reading directives files
|
|
|
|
#define cbFILE_COPY_BUFFER 32768 // Buffer size for copying files
|
|
|
|
|
|
//** Types
|
|
|
|
typedef struct {
|
|
PSESSION psess;
|
|
PERROR perr;
|
|
} SESSIONANDERROR; /* sae */
|
|
typedef SESSIONANDERROR *PSESSIONANDERROR; /* psae */
|
|
|
|
|
|
//** Function Prototypes
|
|
|
|
FNASSERTFAILURE(fnafReport);
|
|
FNDIRFILEPARSE(fndfpPassONE);
|
|
FNDIRFILEPARSE(fndfpPassTWO);
|
|
|
|
BOOL addCmdLineVar(PSESSION psess, char *pszArg, PERROR perr);
|
|
HFILESPEC addDirectiveFile(PSESSION psess, char *pszArg, PERROR perr);
|
|
HGENERIC addFileToSession(PSESSION psess,
|
|
char *pszSrc,
|
|
char *pszDst,
|
|
long cbFile,
|
|
PCOMMAND pcmd,
|
|
PERROR perr);
|
|
BOOL buildInfAndRpt(PSESSION psess, PERROR perr);
|
|
BOOL ccabFromSession(PCCAB pccab,
|
|
PSESSION psess,
|
|
ULONG cbPrevCab,
|
|
PERROR perr);
|
|
BOOL checkDiskClusterSize(PSESSION psess, PERROR perr);
|
|
BOOL checkReferences(PSESSION psess, PERROR perr);
|
|
BOOL checkVariableDefinitions(PSESSION psess, PERROR perr);
|
|
void computeSetID(PSESSION psess, char *psz);
|
|
BOOL doDefine(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr);
|
|
BOOL doDelete(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr);
|
|
BOOL doDump(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr);
|
|
BOOL doFile(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr);
|
|
BOOL doNew(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr);
|
|
BOOL doOption(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr);
|
|
BOOL doReference(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr);
|
|
BOOL ensureCabinet(PSESSION psess, PERROR perr);
|
|
BOOL ensureCabinetsFlushed(PSESSION psess, PERROR perr);
|
|
BOOL executeCommand(PSESSION psess,PCOMMAND pcmd,BOOL fPass2,PERROR perr);
|
|
BOOL getCompressedFileName(PSESSION psess,
|
|
char * pszResult,
|
|
int cbResult,
|
|
char * pszSrc,
|
|
PERROR perr);
|
|
BOOL getFileChecksum(char *pszFile, ULONG *pchecksum, PERROR perr);
|
|
long getMaxDiskSize(PSESSION psess, PERROR perr);
|
|
BOOL getVarWithOverride(PSESSION psess,
|
|
char *pchDst,
|
|
int cbDst,
|
|
char *pszPattern,
|
|
char *pszVar,
|
|
int i,
|
|
char *pszKind,
|
|
PERROR perr);
|
|
BOOL inCabinet(PSESSION psess, PERROR perr);
|
|
char *mapCRTerrno(int errno);
|
|
BOOL modeInfAddLine(PSESSION psess,
|
|
INFAREA inf,
|
|
char *pszLine,
|
|
PERROR perr);
|
|
BOOL modeInfAddFile(PSESSION psess,
|
|
char *pszFile,
|
|
int iDisk,
|
|
int iCabinet,
|
|
PERROR perr);
|
|
BOOL newDiskIfNecessary(PSESSION psess,
|
|
long cbConsume,
|
|
BOOL fSubOnNewDisk,
|
|
PERROR perr);
|
|
BOOL parseCommandLine(PSESSION psess,int cArg,char *apszArg[],PERROR perr);
|
|
void printError(PSESSION psess, PERROR perr);
|
|
BOOL processDirectives(PSESSION psess, PERROR perr);
|
|
BOOL processFile(PSESSION psess, PERROR perr);
|
|
void resetSession(PSESSION psess);
|
|
BOOL setCabinetReserve(PCCAB pccab, PSESSION psess, PERROR perr);
|
|
BOOL setDiskParameters(PSESSION psess,
|
|
char *pszDiskSize,
|
|
long cbDisk,
|
|
PERROR perr);
|
|
BOOL setVariable(PSESSION psess,
|
|
char *pszName,
|
|
char *pszValue,
|
|
PERROR perr);
|
|
int tcompFromSession(PSESSION psess, PERROR perr);
|
|
void updateHgenLast(PSESSION psess, char *pszDst);
|
|
|
|
FNOVERRIDEFILEPROPERTIES(fnofpDiamond);
|
|
|
|
|
|
//** FCI callbacks
|
|
FNALLOC(fciAlloc);
|
|
FNFREE(fciFree);
|
|
FNFCIGETNEXTCABINET(fciGetNextCabinet);
|
|
FNFCIGETNEXTCABINET(fciGetNextCabinet_NOT);
|
|
FNFCIFILEPLACED(fciFilePlaced);
|
|
FNFCIGETOPENINFO(fciOpenInfo);
|
|
FNFCISTATUS(fciStatus);
|
|
FNFCIGETTEMPFILE(fciTempFile);
|
|
|
|
void mapFCIError(PERROR perr, PSESSION psess, char *pszCall, PERF perf);
|
|
|
|
|
|
//** Functions
|
|
|
|
/*** main - Diamond 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 main (int cArg, char *apszArg[])
|
|
{
|
|
ERROR err;
|
|
HVARLIST hvlist; // Variable list for Pass 1
|
|
PSESSION psess;
|
|
int rc; // Return code
|
|
|
|
AssertRegisterFunc(fnafReport); // Register assertion reporter
|
|
MemSetCheckHeap(FALSE); // Turn off slow heap checking
|
|
|
|
ErrClear(&err); // No error
|
|
err.pszFile = NULL; // No file being processed, yet
|
|
|
|
//** Initialize session
|
|
psess = MemAlloc(sizeof(SESSION));
|
|
if (!psess) {
|
|
ErrSet(&err,pszDIAERR_NO_SESSION);
|
|
printError(psess,&err);
|
|
exit(1);
|
|
}
|
|
SetAssertSignature((psess),sigSESSION);
|
|
#ifndef REMOVE_CHICAGO_M6_HACK
|
|
psess->fFailOnIncompressible = FALSE; // Don't fail on incompressible data
|
|
#endif
|
|
psess->fExplicitVarDefine = FALSE; // Don't require .Define
|
|
psess->fGetVerInfo = FALSE; // Don't get version info
|
|
psess->fGetFileChecksum = FALSE; // Don't compute file checksums
|
|
psess->fPass2 = FALSE; // Pass 1
|
|
psess->hflistDirectives = NULL;
|
|
psess->hvlist = NULL;
|
|
psess->hvlistPass2 = NULL;
|
|
psess->levelVerbose = vbNONE; // Default to no status
|
|
psess->hfci = NULL;
|
|
psess->cbTotalFileBytes = 0; // Total bytes in all files
|
|
psess->cFiles = 0;
|
|
psess->fNoLineFeed = 0; // TRUE if last printf did not have \n
|
|
psess->cchLastLine = 0;
|
|
psess->hinf = NULL;
|
|
psess->hglistFiles = NULL;
|
|
psess->setID = 0; // No set ID, yet
|
|
psess->achCurrOutputDir[0] = '\0'; // Default is current directory
|
|
psess->fForceNewDisk = FALSE;
|
|
|
|
memset(psess->achBlanks,' ',cchSCREEN_WIDTH);
|
|
psess->achBlanks[cchSCREEN_WIDTH] = '\0';
|
|
resetSession(psess); // Reset pass variables
|
|
|
|
//** Print Diamond banner
|
|
MsgSet(psess->achMsg,pszBANNER,"%s",pszDIAMOND_VERSION);
|
|
printf(psess->achMsg);
|
|
|
|
//** Initialize Directive File processor (esp. predefined variables)
|
|
if (!(hvlist = DFPInit(psess,&err))) {
|
|
printError(psess,&err);
|
|
return 1;
|
|
}
|
|
|
|
//** Parse command line
|
|
// NOTE: Must do this after DFPInit, to define standard variables.
|
|
psess->hvlist = hvlist; // Command line may modify variables
|
|
if (!parseCommandLine(psess,cArg,apszArg,&err)) {
|
|
printError(psess,&err);
|
|
return 1;
|
|
}
|
|
|
|
//** Quick out if command line help is requested
|
|
if (psess->act == actHELP) { // Do help if any args, for now
|
|
printf("\n"); // Separate banner from help
|
|
printf(pszCMD_LINE_HELP);
|
|
#ifdef ASSERT
|
|
printf(pszCMD_LINE_HELP_DBG);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
//** Get time at start
|
|
psess->clkStart = clock();
|
|
|
|
//** Process command
|
|
switch (psess->act) {
|
|
case actFILE:
|
|
//** Check for any non-standard variable names
|
|
if (!checkVariableDefinitions(psess,&err)) {
|
|
return 1; // Errors already printed
|
|
}
|
|
//** Compress the file
|
|
if (!processFile(psess,&err)) {
|
|
printError(psess,&err);
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case actDIRECTIVE:
|
|
if (!processDirectives(psess,&err)) {
|
|
printError(psess,&err);
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Assert(0); // Should never get here!
|
|
}
|
|
|
|
//** Determine return code
|
|
if (psess->cErrors > 0) {
|
|
rc = 1;
|
|
}
|
|
else {
|
|
rc = 0;
|
|
}
|
|
|
|
//** Free resources
|
|
AssertSess(psess);
|
|
ClearAssertSignature((psess));
|
|
MemFree(psess);
|
|
|
|
//** Indicate result
|
|
return rc;
|
|
} /* main */
|
|
|
|
|
|
/*** processFile - Process single file compression action
|
|
*
|
|
* Entry:
|
|
* psess - Description of operation to perform
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; file compressed.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with details.
|
|
*/
|
|
BOOL processFile(PSESSION psess, PERROR perr)
|
|
{
|
|
char achDst[cbFILE_NAME_MAX];// Destination file name
|
|
char achDef[cbFILE_NAME_MAX];// Default destination name
|
|
long cbFile; // Size of source file
|
|
CCAB ccab; // Cabinet parameters for FCI
|
|
BOOL f;
|
|
HFILESPEC hfspec;
|
|
char *pszSrc; // Source filespec
|
|
char *pszDst; // Destination (cabinet) file spec
|
|
char *pszFilename; // Name to store in cabinet
|
|
SESSIONANDERROR sae; // Context for FCI calls
|
|
TCOMP tcomp;
|
|
|
|
//** Store context to pass through FCI calls
|
|
sae.psess = psess;
|
|
sae.perr = perr;
|
|
|
|
//** Get src/dst file names
|
|
hfspec = FLFirstFile(psess->hflistDirectives);
|
|
Assert(hfspec != NULL); // Must have at least one file
|
|
pszSrc = FLGetSource(hfspec);
|
|
pszDst = FLGetDestination(hfspec);
|
|
if ((pszDst == NULL) || (*pszDst == '\0')) { // No destination
|
|
//** Generate destination file name
|
|
if (!getCompressedFileName(psess,achDef,sizeof(achDef),pszSrc,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
pszDst = achDef; // Use constructed name
|
|
}
|
|
|
|
//** Construct complete filespec for destination file
|
|
if (!catDirAndFile(achDst, // gets location+destination
|
|
sizeof(achDst),
|
|
psess->achCurrOutputDir, // /L argument
|
|
pszDst, // destination
|
|
"", // no fall back
|
|
perr)) {
|
|
return FALSE; // perr set already
|
|
}
|
|
pszDst = achDst;
|
|
|
|
//** Make sure source file exists
|
|
cbFile = getFileSize(pszSrc,perr);
|
|
if (cbFile == -1) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
psess->cbTotalFileBytes = cbFile; // Save for status callbacks
|
|
|
|
//** Get name to store inside of cabinet
|
|
pszFilename = getJustFileNameAndExt(pszSrc,perr);
|
|
if (pszFilename == NULL) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Cabinet controls
|
|
ccab.szDisk[0] = '\0'; // No disk label
|
|
strcpy(ccab.szCab,pszDst); // Compressed file name (cabinet name)
|
|
ccab.szCabPath[0] = '\0'; // No path for cabinet
|
|
ccab.cb = 0; // No limit on cabinet size
|
|
ccab.cbFolderThresh = ccab.cb; // No folder size limit
|
|
ccab.setID = 0; // Set ID does not matter, but make
|
|
// it deterministic!
|
|
#ifndef REMOVE_CHICAGO_M6_HACK
|
|
//** Pass hack flag on to FCI
|
|
ccab.fFailOnIncompressible = psess->fFailOnIncompressible;
|
|
#endif
|
|
|
|
//** Set reserved sizes (from variable settings)
|
|
if (!setCabinetReserve(&ccab,psess,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Create cabinet
|
|
psess->fGenerateInf = FALSE; // Remember we are NOT creating INF
|
|
psess->hfci = FCICreate(
|
|
&psess->erf, // error code return structure
|
|
fciFilePlaced, // callback for file placement notify
|
|
fciAlloc,
|
|
fciFree,
|
|
fciTempFile,
|
|
&ccab
|
|
);
|
|
if (psess->hfci == NULL) {
|
|
mapFCIError(perr,psess,szFCI_CREATE,&psess->erf);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Get compression setting
|
|
tcomp = tcompFromSession(psess,perr);
|
|
|
|
//** Add file
|
|
strcpy(psess->achCurrFile,pszFilename); // Info for fciStatus
|
|
psess->cFiles = 1; // Info for fciStatus
|
|
psess->iCurrFile = 1; // Info for fciStatus
|
|
fciStatus(statusFile,0,0,&sae); // Show new file name, ignore rc
|
|
f = FCIAddFile(
|
|
psess->hfci,
|
|
pszSrc, // filename to add to cabinet
|
|
pszFilename, // name to store into cabinet file
|
|
FALSE,
|
|
fciGetNextCabinet_NOT, // Should never go to a next cabinet!
|
|
fciStatus, // Status callback
|
|
fciOpenInfo, // Open/get attribs/etc. callback
|
|
tcomp, // compression type
|
|
&sae // context
|
|
);
|
|
if (!f) {
|
|
//** Only set error if we didn't already do so in FCIAddFile callback
|
|
if (!ErrIsError(sae.perr)) {
|
|
mapFCIError(perr,psess,szFCI_ADD_FILE,&psess->erf);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//** Complete cabinet file
|
|
if (!FCIFlushCabinet(psess->hfci,FALSE,
|
|
fciGetNextCabinet_NOT,fciStatus,&sae)) {
|
|
//** Only set error if we didn't already do so in FCIAddFile callback
|
|
if (!ErrIsError(sae.perr)) {
|
|
mapFCIError(perr,psess,szFCI_FLUSH_CABINET,&psess->erf);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//** Destroy FCI context
|
|
if (!FCIDestroy(psess->hfci)) {
|
|
mapFCIError(perr,psess,szFCI_DESTROY,&psess->erf);
|
|
return FALSE;
|
|
}
|
|
psess->hfci = NULL; // Clear out FCI context
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* processFile() */
|
|
|
|
|
|
/*** fciGetNextCabinet_NOT - FCI calls this to get new cabinet info
|
|
*
|
|
* NOTE: This should never get called, as we are compressing a single
|
|
* file into a cabinet in this case. So set an error!
|
|
*
|
|
* Entry:
|
|
* pccab - Points to previous current-cabinet structure
|
|
* cbPrevCab - Size of previous cabinet
|
|
* pv - Really a psae
|
|
*
|
|
* Exit:
|
|
* returns FALSE, we should never be called here
|
|
*/
|
|
FNFCIGETNEXTCABINET(fciGetNextCabinet_NOT)
|
|
{
|
|
PSESSION psess = ((PSESSIONANDERROR)pv)->psess;
|
|
PERROR perr = ((PSESSIONANDERROR)pv)->perr;
|
|
HFILESPEC hfspec;
|
|
char *pszSrc; // Source filespec
|
|
|
|
//** Get source filespec for error message
|
|
AssertSess(psess);
|
|
hfspec = FLFirstFile(psess->hflistDirectives);
|
|
Assert(hfspec != NULL); // Must have at least one file
|
|
pszSrc = FLGetSource(hfspec);
|
|
|
|
//** Set the error message
|
|
ErrSet(perr,pszDIAERR_MULTIPLE_CABINETS,"%s",pszSrc);
|
|
|
|
//** Failure
|
|
return FALSE;
|
|
} /* fnGetNextCab() */
|
|
|
|
|
|
/*** processDirectives - Process directive file(s)
|
|
*
|
|
* Entry:
|
|
* psess - Description of operation to perform
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; directives processed successfully.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with details.
|
|
*/
|
|
BOOL processDirectives(PSESSION psess, PERROR perr)
|
|
{
|
|
ERROR errTmp; // Toss away error
|
|
HFILESPEC hfspec;
|
|
HTEXTFILE htf=NULL;
|
|
HVARIABLE hvar;
|
|
char *pszFile;
|
|
|
|
//** Must have at least one directives file
|
|
AssertSess(psess);
|
|
Assert(psess->hflistDirectives != NULL);
|
|
|
|
//** Tailor status based on verbosity level
|
|
if (psess->levelVerbose == vbNONE) {
|
|
//NOTE: This line gets over written below
|
|
lineOut(psess,pszDIA_PARSING_DIRECTIVES,FALSE);
|
|
}
|
|
else {
|
|
lineOut(psess,pszDIA_PASS_1_HEADER1,TRUE);
|
|
lineOut(psess,pszDIA_PASS_1_HEADER2,TRUE);
|
|
}
|
|
|
|
//** Save a copy of the variable list in this state for Pass 2
|
|
if (!(psess->hvlistPass2 = VarCloneList(psess->hvlist,perr))) {
|
|
goto error; // perr already filled in
|
|
}
|
|
|
|
/*
|
|
** Pass ONE, make sure everything is OK
|
|
*/
|
|
hfspec = FLFirstFile(psess->hflistDirectives);
|
|
Assert(hfspec != NULL); // Must have at least one file
|
|
for (; hfspec != NULL; hfspec = FLNextFile(hfspec)) {
|
|
pszFile = FLGetSource(hfspec);
|
|
perr->pszFile = pszFile; // Set file for error messages
|
|
|
|
//** Open file
|
|
if (!(htf = TFOpen(pszFile,tfREAD_ONLY,cbDF_BUFFER,perr))) {
|
|
goto error; // perr already filled in
|
|
}
|
|
|
|
//** Parse it
|
|
if (!DFPParse(psess,htf,fndfpPassONE,perr)) {
|
|
goto error; // perr already filled in
|
|
}
|
|
|
|
//** Close it
|
|
TFClose(htf);
|
|
htf = NULL; // Clear so error path avoids close
|
|
}
|
|
|
|
//** Not processing directive files; avoid bogus file/line number info
|
|
// in any error messages that might be generated below.
|
|
perr->pszFile = NULL;
|
|
|
|
//** If .Option Explicit, make sure no variables have been .Set without
|
|
// being .Defined.
|
|
// NOTE: No need to check return value, since any error will increment
|
|
// psess->cErrors, and cause us to bail out just below.
|
|
if (psess->fExplicitVarDefine) {
|
|
checkVariableDefinitions(psess,perr);
|
|
}
|
|
|
|
//** If in relational mode, make sure there are no unreferenced files
|
|
// NOTE: No need to check return value, since any error will increment
|
|
// psess->cErrors, and cause us to bail out just below.
|
|
checkReferences(psess,perr);
|
|
|
|
//** Bail out if any errors in pass 1
|
|
if (psess->cErrors > 0) {
|
|
ErrSet(perr,pszDIAERR_ERRORS_IN_PASS_1,"%d",psess->cErrors);
|
|
perr->pszFile = NULL; // Not file-specific
|
|
goto error;
|
|
}
|
|
|
|
//** Make sure we can create INF and RPT files *before* we spend any
|
|
// time doing compression! We have to do this at the end of processing
|
|
// the directive file during pass 1, so that we make sure the INT and
|
|
// RPT file names have been specified. Note that only the last
|
|
// setting will be used.
|
|
//
|
|
hvar = VarFind(psess->hvlist,pszVAR_INF_FILE_NAME,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pszFile = VarGetString(hvar);
|
|
if (!ensureFile(pszFile,pszDIA_INF_FILE,perr)) {
|
|
goto error;
|
|
}
|
|
|
|
hvar = VarFind(psess->hvlist,pszVAR_RPT_FILE_NAME,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pszFile = VarGetString(hvar);
|
|
if (!ensureFile(pszFile,pszDIA_RPT_FILE,perr)) {
|
|
goto error;
|
|
}
|
|
|
|
//** Initialize for INF generation
|
|
// NOTE: We use the variable state at the *end* of pass 1, as this
|
|
// permits the INF area header variables to be defined anywhere!
|
|
//
|
|
// NOTE: We check the InfXxxLineFormat variables for undefined
|
|
// parameters (so that we can error out before we spend a lot
|
|
// of time compressing files!).
|
|
//
|
|
// NOTE: If any of the ver info parameters (*ver*, *vers*, *lang*)
|
|
// are used in the InfFileLineFormat variable, then we note this
|
|
// and collect ver info during pass 2. Otherwise, we skip it to
|
|
// speed up pass 2.
|
|
if (!infCreate(psess,perr)) {
|
|
goto error;
|
|
}
|
|
|
|
/*
|
|
** Pass TWO, do the layout!
|
|
*/
|
|
psess->fPass2 = TRUE; // Remember for asserts, mostly
|
|
|
|
//** Tailor status based on verbosity level
|
|
if (psess->levelVerbose >= vbNONE) {
|
|
MsgSet(psess->achMsg,pszDIA_STATS_BEFORE,"%,ld%,ld",
|
|
psess->cbTotalFileBytes,psess->cFiles);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
//NOTE: This line gets over written below
|
|
lineOut(psess,pszDIA_EXECUTING_DIRECTIVES,FALSE);
|
|
}
|
|
else {
|
|
lineOut(psess,pszDIA_PASS_2_HEADER1,TRUE);
|
|
lineOut(psess,pszDIA_PASS_2_HEADER2,TRUE);
|
|
}
|
|
|
|
//** Reset to initial state for pass 2
|
|
if (!VarDestroyList(psess->hvlist,perr)) {
|
|
goto error; // perr already filled in
|
|
}
|
|
psess->hvlist = psess->hvlistPass2; // Use variables saved for pass 2
|
|
psess->hvlistPass2 = NULL; // Clear so error path does not free
|
|
resetSession(psess); // Reset pass variables
|
|
|
|
//** Process directive files for pass 2
|
|
hfspec = FLFirstFile(psess->hflistDirectives);
|
|
Assert(hfspec != NULL); // Must have at least one file
|
|
for (; hfspec != NULL; hfspec = FLNextFile(hfspec)) {
|
|
pszFile = FLGetSource(hfspec);
|
|
perr->pszFile = pszFile; // Set file for error messages
|
|
|
|
//** Open file
|
|
if (!(htf = TFOpen(pszFile,tfREAD_ONLY,cbDF_BUFFER,perr))) {
|
|
goto error; // perr already filled in
|
|
}
|
|
|
|
//** Parse it
|
|
if (!DFPParse(psess,htf,fndfpPassTWO,perr)) {
|
|
goto error; // perr already filled in
|
|
}
|
|
|
|
//** Close it
|
|
TFClose(htf);
|
|
htf = NULL; // Clear so error path avoids close
|
|
}
|
|
|
|
//** No longer processing directive files; reset ERROR
|
|
perr->pszFile = NULL;
|
|
|
|
//** Flush out cabinets, if we have not already done so
|
|
if (!ensureCabinetsFlushed(psess,perr)) {
|
|
goto error;
|
|
}
|
|
|
|
//** Get ending time, generate INF and RPT files
|
|
psess->clkEnd = clock();
|
|
if (!buildInfAndRpt(psess,perr)) {
|
|
goto error;
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
|
|
error:
|
|
if (psess->hinf != NULL) {
|
|
infDestroy(psess->hinf,&errTmp); // Ignore errors
|
|
}
|
|
|
|
if (htf != NULL) {
|
|
TFClose(htf);
|
|
}
|
|
|
|
if (psess->hvlistPass2 != NULL) {
|
|
VarDestroyList(psess->hvlistPass2,&errTmp); // Ignore any error
|
|
}
|
|
|
|
//** Failure
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*** buildInfAndRpt - Create INF and RPT output files
|
|
*
|
|
* Entry:
|
|
* psess - Description of operation to perform
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; files created
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with details.
|
|
*/
|
|
BOOL buildInfAndRpt(PSESSION psess, PERROR perr)
|
|
{
|
|
int hours;
|
|
HVARIABLE hvar;
|
|
double kbPerSec;
|
|
int minutes;
|
|
char *pszFile;
|
|
double percent;
|
|
FILE *pfileRpt; // Report file
|
|
double seconds;
|
|
double secondsTotal;
|
|
time_t timeNow;
|
|
|
|
Assert(psess->fGenerateInf);
|
|
|
|
//** Compute running time and throughput
|
|
secondsTotal = (psess->clkEnd - psess->clkStart) / (float)CLOCKS_PER_SEC;
|
|
if (secondsTotal == 0.0) { // Don't divide by zero!
|
|
secondsTotal = 1.0;
|
|
}
|
|
kbPerSec = psess->cbFileBytes/secondsTotal/1024L;
|
|
hours = (int)(secondsTotal/(60*60)); // Hours
|
|
minutes = (int)((secondsTotal - hours*60*60)/60); // Minutes
|
|
seconds = secondsTotal - hours*60*60 - minutes*60; // Seconds
|
|
|
|
//** Get date/time stamp
|
|
time(&timeNow);
|
|
|
|
//** Generate INF file
|
|
hvar = VarFind(psess->hvlist,pszVAR_INF_FILE_NAME,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pszFile = VarGetString(hvar);
|
|
if (!infGenerate(psess,pszFile,&timeNow,pszDIAMOND_VERSION,perr)) {
|
|
return FALSE;
|
|
}
|
|
if (!infDestroy(psess->hinf,perr)) {
|
|
return FALSE;
|
|
}
|
|
psess->hinf = NULL; // So caller knows it is gone
|
|
|
|
//** Display summary of results and write report file
|
|
hvar = VarFind(psess->hvlist,pszVAR_RPT_FILE_NAME,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pszFile = VarGetString(hvar);
|
|
pfileRpt = fopen(pszFile,"wt"); // Create setup.rpt
|
|
if (pfileRpt == NULL) { // Could not create
|
|
ErrSet(perr,pszDIAERR_CANT_CREATE_RPT,"%s",pszFile);
|
|
printError(psess,perr);
|
|
ErrClear(perr); // But, continue
|
|
}
|
|
|
|
//** Only put header in report file
|
|
MsgSet(psess->achMsg,pszDIA_RPT_HEADER,"%s",ctime(&timeNow));
|
|
if (pfileRpt) {
|
|
fprintf(pfileRpt,"%s\n",psess->achMsg);
|
|
}
|
|
|
|
//** Show stats on stdout and to report file
|
|
MsgSet(psess->achMsg,pszDIA_STATS_AFTER1,"%,13ld",psess->cFiles);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
if (pfileRpt) {
|
|
fprintf(pfileRpt,"%s\n",psess->achMsg);
|
|
}
|
|
|
|
MsgSet(psess->achMsg,pszDIA_STATS_AFTER2,"%,13ld",psess->cbFileBytes);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
if (pfileRpt) {
|
|
fprintf(pfileRpt,"%s\n",psess->achMsg);
|
|
}
|
|
|
|
MsgSet(psess->achMsg,pszDIA_STATS_AFTER3,"%,13ld",psess->cbFileBytesComp);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
if (pfileRpt) {
|
|
fprintf(pfileRpt,"%s\n",psess->achMsg);
|
|
}
|
|
|
|
//** Compute percentage complete
|
|
if (psess->cbFileBytes > 0) {
|
|
percent = psess->cbFileBytesComp/(float)psess->cbFileBytes;
|
|
percent *= 100.0; // Make it 0..100
|
|
}
|
|
else {
|
|
//** No files, I guess!
|
|
percent = 0.0;
|
|
}
|
|
MsgSet(psess->achMsg,pszDIA_STATS_AFTER4,"%6.2f",percent);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
if (pfileRpt) {
|
|
fprintf(pfileRpt,"%s\n",psess->achMsg);
|
|
}
|
|
|
|
MsgSet(psess->achMsg,pszDIA_STATS_AFTER5,"%9.2f%2d%2d%5.2f",
|
|
secondsTotal,hours,minutes,seconds);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
if (pfileRpt) {
|
|
fprintf(pfileRpt,"%s\n",psess->achMsg);
|
|
}
|
|
|
|
MsgSet(psess->achMsg,pszDIA_STATS_AFTER6,"%9.2f",kbPerSec);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
if (pfileRpt) {
|
|
fprintf(pfileRpt,"%s\n",psess->achMsg);
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* buildInfAndRpt() */
|
|
|
|
|
|
/*** fndfpPassONE - First pass of directives file
|
|
*
|
|
* NOTE: See dfparse.h for entry/exit conditions.
|
|
*/
|
|
FNDIRFILEPARSE(fndfpPassONE)
|
|
{
|
|
long cMaxErrors;
|
|
HVARIABLE hvar;
|
|
static char achDDFName[cbFILE_NAME_MAX] = "";
|
|
|
|
AssertSess(psess);
|
|
|
|
//** Update line count status occassionaly
|
|
if ((psess->levelVerbose == vbNONE) &&
|
|
(!(perr->iLine % 50) || _stricmp(achDDFName,perr->pszFile))) {
|
|
//** Minimal verbosity, and we've processed a 50-line chunk,
|
|
// or we've switched DDF files.
|
|
MsgSet(psess->achMsg,pszDIA_PARSING_PROGRESS,"%s%d",
|
|
perr->pszFile,perr->iLine);
|
|
lineOut(psess,psess->achMsg,FALSE);
|
|
//** Remember this DDF file name if it changed
|
|
if (perr->iLine % 50) {
|
|
strcpy(achDDFName,perr->pszFile);
|
|
}
|
|
}
|
|
|
|
//** Execute only if we have no parse error so far
|
|
if (!ErrIsError(perr)) {
|
|
//** Execute command for pass ONE
|
|
executeCommand(psess,pcmd,FALSE,perr);
|
|
}
|
|
|
|
//** Handle error reporting
|
|
if (ErrIsError(perr)) {
|
|
//** Print out error
|
|
printError(psess,perr);
|
|
|
|
//** Make sure we don't exceed our limit
|
|
ErrClear(perr);
|
|
hvar = VarFind(psess->hvlist,pszVAR_MAX_ERRORS,perr);
|
|
Assert(!perr->fError); // MaxErrors must be defined
|
|
cMaxErrors = VarGetLong(hvar);
|
|
if ((cMaxErrors != 0) && // There is a limit *and*
|
|
(psess->cErrors >= cMaxErrors)) { // the limit is exceeded
|
|
ErrSet(perr,pszDIAERR_MAX_ERRORS,"%d",psess->cErrors);
|
|
perr->pszFile = NULL; // Not specific to a directive file
|
|
return FALSE;
|
|
}
|
|
//** Reset error so we can continue
|
|
ErrClear(perr);
|
|
}
|
|
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* fndfpPassONE() */
|
|
|
|
|
|
/*** fndfpPassTWO - Second pass of directives file
|
|
*
|
|
* NOTE: See dfparse.h for entry/exit conditions.
|
|
*/
|
|
FNDIRFILEPARSE(fndfpPassTWO)
|
|
{
|
|
AssertSess(psess);
|
|
|
|
//** Execute only if we have no parse error so far
|
|
if (!ErrIsError(perr)) {
|
|
//** Execute command for pass TWO
|
|
executeCommand(psess,pcmd,TRUE,perr);
|
|
}
|
|
|
|
if (ErrIsError(perr)) {
|
|
//** Print out error, set abort message and fail
|
|
printError(psess,perr);
|
|
ErrSet(perr,pszDIAERR_ERRORS_IN_PASS_2);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* fndfpPassTWO() */
|
|
|
|
|
|
/*** executeCommand - execute a parse command
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* pcmd - Command to process
|
|
* fPass2 - TRUE if this is pass 2, FALSE if pass 1
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
executeCommand(PSESSION psess,
|
|
PCOMMAND pcmd,
|
|
BOOL fPass2,
|
|
PERROR perr)
|
|
{
|
|
AssertSess(psess);
|
|
AssertCmd(pcmd);
|
|
|
|
//** Execute line
|
|
switch (pcmd->ct) {
|
|
case ctCOMMENT:
|
|
return TRUE;
|
|
|
|
case ctDELETE:
|
|
return doDelete(psess,pcmd,fPass2,perr);
|
|
|
|
case ctDUMP:
|
|
return doDump(psess,pcmd,fPass2,perr);
|
|
|
|
case ctINF_BEGIN: // Nothing to do
|
|
case ctINF_END: // Nothing to do
|
|
return TRUE;
|
|
|
|
case ctINF_WRITE:
|
|
return modeInfAddLine(psess,
|
|
pcmd->inf.inf,
|
|
pcmd->inf.achLine,
|
|
perr);
|
|
|
|
case ctNEW:
|
|
return doNew(psess,pcmd,fPass2,perr);
|
|
|
|
case ctOPTION:
|
|
return doOption(psess,pcmd,fPass2,perr);
|
|
|
|
case ctFILE:
|
|
if (!doFile(psess,pcmd,fPass2,perr) || !fPass2) {
|
|
//** Failed or pass 1, toss parameter list
|
|
if (pcmd->file.hglist) {
|
|
GLDestroyList(pcmd->file.hglist);
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
case ctREFERENCE:
|
|
if (!doReference(psess,pcmd,fPass2,perr) || !fPass2) {
|
|
//** Failed or pass 1, toss parameter list
|
|
if (pcmd->ref.hglist) {
|
|
GLDestroyList(pcmd->ref.hglist);
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
case ctDEFINE:
|
|
return doDefine(psess,pcmd,fPass2,perr);
|
|
|
|
case ctSET:
|
|
return setVariable(psess,
|
|
pcmd->set.achVarName,
|
|
pcmd->set.achValue,
|
|
perr);
|
|
|
|
case ctBAD:
|
|
case ctINF_WRITE_CAB: // dfparse.c maps to ctINF_WRITE
|
|
case ctINF_WRITE_DISK: // dfparse.c maps to ctINF_WRITE
|
|
default:
|
|
Assert(0); // Should never get here
|
|
return FALSE;
|
|
}
|
|
|
|
//** Should never get here
|
|
Assert(0);
|
|
return FALSE;
|
|
} /* executeCommand() */
|
|
|
|
|
|
/*** setVariable - wrapper around VarSet to do special processing
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* pszName - Variable name
|
|
* pszValue - New value
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE, variable is created (if necessary) and value set.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE, cannot set variable value.
|
|
* ERROR structure filled in with details of error.
|
|
*
|
|
* Notes:
|
|
* (1) For *all* variables, we convert special disk size strings into
|
|
* their numeric equivalent. This is the easiest way to ensure
|
|
* that we catch MaxDiskSize[n] variables!
|
|
* (2) We check for variables with the special vflCOPY flag, and if
|
|
* this is *pass 1*, we also set these variables in the *pass 2*
|
|
* list; this permits InfDisk/CabinetLineFormat variables to
|
|
* be defined in the *inf* section of the DDF, but get used in
|
|
* the *layout* section! (Whew, pretty squirrely!)
|
|
*/
|
|
BOOL setVariable(PSESSION psess,
|
|
char *pszName,
|
|
char *pszValue,
|
|
PERROR perr)
|
|
{
|
|
char achSize[50];
|
|
long cbDisk;
|
|
HVARIABLE hvar;
|
|
char *psz;
|
|
|
|
//** Do special disk size list procesing
|
|
if (cbDisk = IsSpecialDiskSize(psess,pszValue)) {
|
|
_ltoa(cbDisk,achSize,10); // Convert number to string
|
|
psz = achSize;
|
|
}
|
|
else { // Not special
|
|
psz = pszValue;
|
|
}
|
|
|
|
//** Set the variable
|
|
if (!(hvar = VarSet(psess->hvlist,pszName,psz,perr))) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Set it in the pass 2 list if:
|
|
// We are not already in pass 2 -and-
|
|
// We have already created the pass 2 list -and-
|
|
// The variable is supposed to be copied
|
|
|
|
if (!psess->fPass2 &&
|
|
psess->hvlistPass2 &&
|
|
(VarGetFlags(hvar) & vflCOPY)) {
|
|
if (!(hvar = VarSet(psess->hvlistPass2,pszName,psz,perr))) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//** If MaxDiskSize, update other variables if appropriate
|
|
if (_stricmp(pszName,pszVAR_MAX_DISK_SIZE) == 0) {
|
|
return setDiskParameters(psess,psz,0,perr);
|
|
}
|
|
|
|
//** If GenerateInf, do special goofy context processing
|
|
if (_stricmp(pszName,pszVAR_GENERATE_INF) == 0) {
|
|
switch (psess->ddfmode) {
|
|
case ddfmodeUNKNOWN:
|
|
//** Let change occur; we don't make up our mind until
|
|
// the first file copy command.
|
|
return TRUE;
|
|
|
|
case ddfmodeUNIFIED: // Doing INF in parallel with file copy
|
|
ErrSet(perr,pszDIA_BAD_INF_MODE);
|
|
return FALSE;
|
|
|
|
case ddfmodeRELATIONAL:
|
|
hvar = VarFind(psess->hvlist,pszVAR_GENERATE_INF,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
//** Don't allow turning off twice!
|
|
if (!VarGetBool(hvar)) {
|
|
ErrSet(perr,pszDIA_BAD_INF_MODE);
|
|
return FALSE;
|
|
}
|
|
//** Now we read reference commands
|
|
psess->fExpectFileCommand = FALSE;
|
|
return TRUE;
|
|
|
|
default:
|
|
Assert(0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
} /* setVariable() */
|
|
|
|
|
|
/*** doDump - Process a .DUMP command (dump all variables)
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* pcmd - Command to process (ct == ctDUMP)
|
|
* fPass2 - TRUE if this is pass 2
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL doDump(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
VARFLAGS vfl;
|
|
char *pszName;
|
|
char *pszValue;
|
|
|
|
AssertSess(psess);
|
|
Assert(pcmd->ct == ctDUMP);
|
|
|
|
//** Print variable dump header (two line feeds to make sure
|
|
// we get a blank line if status output preceded us).
|
|
printf("\n\n%s\n",pszDIA_VAR_DUMP1);
|
|
printf("%s\n",pszDIA_VAR_DUMP2);
|
|
|
|
//** Print out all variables
|
|
for (hvar = VarFirstVar(psess->hvlist);
|
|
hvar;
|
|
hvar = VarNextVar(hvar)) {
|
|
|
|
//** Get variable info
|
|
vfl = VarGetFlags(hvar);
|
|
pszName = VarGetName(hvar);
|
|
pszValue = VarGetString(hvar);
|
|
|
|
//** Print name and flag (indent for readability)
|
|
printf(" %s",pszName);
|
|
if (vfl != vflNONE) {
|
|
printf("(");
|
|
if (vfl & vflPERM) {
|
|
printf("%s",pszDIA_VAR_PERMANENT);
|
|
}
|
|
else if (vfl & vflDEFINE) {
|
|
printf("%s",pszDIA_VAR_DEFINED);
|
|
}
|
|
else {
|
|
Assert(0); // Unknown flag
|
|
}
|
|
printf(")");
|
|
}
|
|
|
|
//** Print value
|
|
printf("=<%s>\n",pszValue);
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* doDump() */
|
|
|
|
|
|
/*** doDefine - Process a .DEFINE command
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pcmd - Command to process (ct == ctDEFINE)
|
|
* fPass2 - TRUE if this is pass 2, where we do the real work!
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL doDefine(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
VARFLAGS vfl;
|
|
|
|
AssertSess(psess);
|
|
Assert(pcmd->ct == ctDEFINE);
|
|
|
|
//** Create variable if necessary
|
|
hvar = VarFind(psess->hvlist,pcmd->set.achVarName,perr);
|
|
if (hvar == NULL) { // Have to create it ourselves
|
|
hvar = VarCreate(psess->hvlist, // var list
|
|
pcmd->set.achVarName, // var name
|
|
"", // default value
|
|
vtypeSTR, // var type
|
|
vflDEFINE, // var is DEFINED
|
|
NULL, // No validation function
|
|
perr);
|
|
if (hvar == NULL) {
|
|
return FALSE; // Could not create variable
|
|
}
|
|
}
|
|
else {
|
|
//** Variable already exists, check if .DEFINE is OK
|
|
vfl = VarGetFlags(hvar);
|
|
if (vfl & vflDEFINE) {
|
|
ErrSet(perr,pszDIAERR_REDEFINE,"%s%s",
|
|
pszCMD_DEFINE,pcmd->set.achVarName);
|
|
return FALSE;
|
|
}
|
|
else if (vfl & vflPERM) {
|
|
ErrSet(perr,pszDIAERR_DEFINE_PERM,"%s%s",
|
|
pszCMD_DEFINE,pcmd->set.achVarName);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//** Everything is fine, set the variable value
|
|
return setVariable(psess,
|
|
pcmd->set.achVarName,
|
|
pcmd->set.achValue,
|
|
perr);
|
|
} /* doDefine() */
|
|
|
|
|
|
/*** doDelete - Process a .DELETE command
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pcmd - Command to process (ct == ctDELETE)
|
|
* fPass2 - TRUE if this is pass 2, where we do the real work!
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL doDelete(PSESSION psess, PCOMMAND pcmd, BOOL fPass2, PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
|
|
AssertSess(psess);
|
|
Assert(pcmd->ct == ctDELETE);
|
|
|
|
//** Make sure variable exists
|
|
if (!(hvar = VarFind(psess->hvlist,pcmd->delete.achVarName,perr))) {
|
|
return FALSE; // Variable does not exist
|
|
}
|
|
|
|
//** Delete it
|
|
VarDelete(hvar);
|
|
return TRUE;
|
|
} /* doDelete() */
|
|
|
|
|
|
/*** doNew - Process a .NEW command
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pcmd - Command to process (ct == ctNEW)
|
|
* fPass2 - TRUE if this is pass 2, where we do the real work!
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL doNew(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr)
|
|
{
|
|
SESSIONANDERROR sae; // Context for FCI calls
|
|
char *pszKind;
|
|
|
|
AssertSess(psess);
|
|
Assert(pcmd->ct == ctNEW);
|
|
/*
|
|
** Pass 1, check for invalid context
|
|
*/
|
|
if (!fPass2) {
|
|
//** Don't permit .New commands in INF section of RELATIONAL DDF
|
|
if (!psess->fExpectFileCommand) {
|
|
ErrSet(perr,pszDIAERR_BAD_CMD_IN_INF_SECT,"%s",pszCMD_NEW);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Don't permit .New cabinet/folder when not in cabinet
|
|
switch (pcmd->new.nt) {
|
|
case newFOLDER:
|
|
case newCABINET:
|
|
if (inCabinet(psess,perr)) {
|
|
return TRUE;
|
|
}
|
|
pszKind = (pcmd->new.nt == newFOLDER) ?
|
|
pszNEW_FOLDER : pszNEW_CABINET;
|
|
ErrSet(perr,pszDIAERR_BAD_NEW_CMD,"%s%s",pszCMD_NEW,pszKind);
|
|
return FALSE;
|
|
|
|
case newDISK:
|
|
return TRUE;
|
|
}
|
|
Assert(0); // Should never get here
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
** Pass 2, finish the current folder, cabinet, or disk
|
|
*/
|
|
//** Store context to pass through FCI calls
|
|
sae.psess = psess;
|
|
sae.perr = perr;
|
|
switch (pcmd->new.nt) {
|
|
case newFOLDER:
|
|
if (!psess->hfci) { // No FCI context yet, so NOP
|
|
return TRUE;
|
|
}
|
|
if (!FCIFlushFolder(psess->hfci,fciGetNextCabinet,fciStatus,&sae)) {
|
|
//** Only set error if we didn't already do so
|
|
if (!ErrIsError(sae.perr)) {
|
|
mapFCIError(perr,psess,szFCI_FLUSH_FOLDER,&psess->erf);
|
|
}
|
|
return FALSE;
|
|
}
|
|
psess->cFilesInFolder = 0; // Reset files in folder count
|
|
break;
|
|
|
|
case newDISK:
|
|
//** Our technique is a lazy one -- we set the flag asking for
|
|
// a new disk, and (if in a cabinet) we flush the current
|
|
// cabinet. Either way, the next file or cabinet will start
|
|
// on a new disk.
|
|
psess->fForceNewDisk = TRUE;
|
|
if (!inCabinet(psess,perr)) {
|
|
return TRUE; // Not in a cabinet, we're done
|
|
}
|
|
|
|
//** OK, we're in a cabinet; We want to flush it and assume
|
|
// that more files are coming for the next cabinet, so
|
|
// just fall through to the .New Cabinet code!
|
|
//
|
|
// ATTENTION: FALLING THROUGH!
|
|
|
|
case newCABINET:
|
|
if (!psess->hfci) { // No FCI context yet, so NOP
|
|
return TRUE;
|
|
}
|
|
//** Flush current cabinet, but tell FCI more are coming!
|
|
if (!FCIFlushCabinet(psess->hfci,TRUE,
|
|
fciGetNextCabinet,fciStatus,&sae)) {
|
|
//** Only set error if we didn't already do so
|
|
if (!ErrIsError(sae.perr)) {
|
|
mapFCIError(perr,psess,szFCI_FLUSH_CABINET,&psess->erf);
|
|
}
|
|
return FALSE;
|
|
}
|
|
psess->cFilesInCabinet = 0; // Reset files in folder count
|
|
break;
|
|
|
|
default:
|
|
Assert(0);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
} /* doNew() */
|
|
|
|
|
|
/*** doOption - Process a .OPTION command
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pcmd - Command to process (ct == ctOPTION)
|
|
* fPass2 - TRUE if this is pass 2
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL doOption(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr)
|
|
{
|
|
AssertSess(psess);
|
|
Assert(pcmd->ct == ctOPTION);
|
|
|
|
//** Check for a change to the Explicit variable definition setting
|
|
if (pcmd->opt.ofMask & optEXPLICIT) {
|
|
//** Change to .Option Explicit
|
|
psess->fExplicitVarDefine = ((pcmd->opt.of & optEXPLICIT) != 0);
|
|
}
|
|
|
|
//** Make sure no other bits get snuck in
|
|
Assert((pcmd->opt.ofMask & ~optEXPLICIT) == 0);
|
|
return TRUE;
|
|
} /* doOption() */
|
|
|
|
|
|
/*** doReference - Process an INF file reference
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pcmd - Command to process (ct == ctREFERENCE)
|
|
* fPass2 - TRUE if this is pass 2, where we do the real work!
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL doReference(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr)
|
|
{
|
|
HGENERIC hgen;
|
|
PFILEINFO pfinfo;
|
|
FILEINFO finfoIgnore; // For ValidateParms
|
|
|
|
AssertSess(psess);
|
|
Assert(!psess->fExpectFileCommand);
|
|
Assert(psess->ddfmode == ddfmodeRELATIONAL);
|
|
|
|
//** Find referenced file
|
|
if (!(hgen = GLFind(psess->hglistFiles,pcmd->ref.achDst,"",perr))) {
|
|
ErrSet(perr,pszDIAERR_REF_FILE_NOT_FOUND,"%s",pcmd->ref.achDst);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Get file info structure
|
|
pfinfo = GLGetValue(hgen);
|
|
AssertFinfo(pfinfo);
|
|
|
|
//** Merge copy of parms from file into reference line
|
|
if (!GLCopyToList(&(pcmd->ref.hglist), // Destination
|
|
pfinfo->hglistParm, // Source
|
|
DuplicateFileParm,
|
|
pszDIA_FILE_PARM,
|
|
perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
** PASS 1 Processing
|
|
*/
|
|
if (!fPass2) {
|
|
//** Validate standard parameters in parameter list
|
|
// NOTE: ValidateParms is usually used for File Copy commands,
|
|
// so /date, /time, etc. parameters will actually make
|
|
// modifications to the finfo structure. However, we don't
|
|
// want any modifications to the fileinfo structure for a
|
|
// File Reference command. So, we copy the info to a temporary
|
|
// structure, and ignore any changes ValidateParms makes.
|
|
finfoIgnore = *pfinfo; // Scratch copy of fileinfo
|
|
if (!ValidateParms(psess,pcmd->ref.hglist,&finfoIgnore,perr)) {
|
|
return FALSE;
|
|
}
|
|
pfinfo->flags |= fifREFERENCED; // Mark referenced bit
|
|
return TRUE; // Everything is fine so far
|
|
}
|
|
|
|
/*
|
|
** PASS 2 Processing
|
|
*/
|
|
//** Make sure cabinets are flushed so all file info is filled in
|
|
if (!ensureCabinetsFlushed(psess,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Add line to file area of INF
|
|
if (!infAddFile(psess,pcmd->ref.achDst,pfinfo,pcmd->ref.hglist,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* doReference() */
|
|
|
|
|
|
/*** doFile - Process a file copy command
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pcmd - Command to process (ct == ctFILE)
|
|
* fPass2 - TRUE if this is pass 2, where we do the real work!
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL doFile(PSESSION psess,PCOMMAND pcmd, BOOL fPass2, PERROR perr)
|
|
{
|
|
//** Minimize stack usage
|
|
static char achSrc[cbFILE_NAME_MAX]; // Full source file name
|
|
static char achDst[cbFILE_NAME_MAX]; // Destination file name
|
|
static char achFull[cbFILE_NAME_MAX]; // Full destination file name
|
|
long cbFile;
|
|
long cMaxFiles;
|
|
BOOL fCopyFile; // See DoNotCopyFiles variable
|
|
BOOL fSuccess;
|
|
HGENERIC hgen;
|
|
HVARIABLE hvar;
|
|
char *pch;
|
|
PFILEINFO pfinfo;
|
|
SESSIONANDERROR sae; // Context for FCI calls
|
|
TCOMP tcomp; // Compression type
|
|
|
|
AssertSess(psess);
|
|
Assert(psess->fExpectFileCommand);
|
|
|
|
/*
|
|
** Determine INF generation mode on first copy file command
|
|
*/
|
|
if (psess->ddfmode == ddfmodeUNKNOWN) {
|
|
hvar = VarFind(psess->hvlist,pszVAR_GENERATE_INF,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
if (VarGetBool(hvar)) { // Generate INF in parallel
|
|
psess->ddfmode = ddfmodeUNIFIED;
|
|
}
|
|
else {
|
|
psess->ddfmode = ddfmodeRELATIONAL;
|
|
//** Make sure UniqueFiles is ON
|
|
hvar = VarFind(psess->hvlist,pszVAR_UNIQUE_FILES,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
if (!VarGetBool(hvar)) { // Must be unique
|
|
ErrSet(perr,pszDIAERR_MUST_BE_UNIQUE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//** Store context to pass through FCI calls
|
|
sae.psess = psess;
|
|
sae.perr = perr;
|
|
|
|
//** Construct final source and destination filespecs
|
|
hvar = VarFind(psess->hvlist,pszVAR_DIR_SRC,perr);
|
|
Assert(!perr->fError); // DestinationDir must be defined
|
|
pch = VarGetString(hvar); // Get destination dir
|
|
if (!catDirAndFile(achSrc,sizeof(achSrc),
|
|
pch,pcmd->file.achSrc,NULL,perr)) {
|
|
return FALSE; // perr set already
|
|
}
|
|
|
|
hvar = VarFind(psess->hvlist,pszVAR_DIR_DEST,perr);
|
|
Assert(!perr->fError); // SourceDir must be defined
|
|
pch = VarGetString(hvar); // Get source dir
|
|
if (!catDirAndFile(achDst,sizeof(achDst),
|
|
pch,pcmd->file.achDst,pcmd->file.achSrc,perr)) {
|
|
return FALSE; // perr set already
|
|
}
|
|
|
|
/*
|
|
** PASS 1 Processing
|
|
*/
|
|
if (!fPass2) {
|
|
if (psess->levelVerbose >= vbFULL) {
|
|
MsgSet(psess->achMsg,pszDIA_FILE_COPY,"%s%s",achSrc,achDst);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
}
|
|
|
|
//** Make sure MaxDiskSize is a multiple of the ClusterSize
|
|
// NOTE: This only catches cases where MaxDiskSize/ClusterSize
|
|
// are not compatible. MaxDiskSizeN variables cannot
|
|
// be checked until pass 2, when we know what disk we are
|
|
// actually on! Usually we won't get an error in that
|
|
// case, since we update the ClusterSize automatically.
|
|
if (!checkDiskClusterSize(psess,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Make sure file exists
|
|
if (-1 == (cbFile = getFileSize(achSrc,perr))) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
computeSetID(psess,achDst); // Accumulate cabinet setID
|
|
psess->cbTotalFileBytes += cbFile; // Count up total file sizes
|
|
psess->cFiles++; // Count files
|
|
|
|
//** Keep track of file names, handle uniqueness checking
|
|
if (!(hgen = addFileToSession(psess,achSrc,achDst,cbFile,pcmd,perr))) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Make sure standard parameters have valid format
|
|
pfinfo = GLGetValue(hgen);
|
|
AssertFinfo(pfinfo);
|
|
if (!ValidateParms(psess,pfinfo->hglistParm,pfinfo,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Allow InfDate, InfTime, InfAttr overrides
|
|
// NOTE: This must be called *after* ValidateParms(), so that
|
|
// it will not override parms specified on the file copy
|
|
// command!
|
|
if (!CheckForInfVarOverrides(psess,pfinfo,perr)) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** PASS 2 Processing
|
|
*/
|
|
|
|
//** Give user status
|
|
strcpy(psess->achCurrFile,achDst); // Info for fciStatus
|
|
psess->iCurrFile++; // Info for fciStatus
|
|
fciStatus(statusFile,0,0,&sae); // Show new file name, ignore rc
|
|
|
|
//** Update psess->hgenLast for INF generation
|
|
updateHgenLast(psess,achDst);
|
|
|
|
#ifndef BIT16
|
|
//** Get Version/Language info (only if needed for INF file)
|
|
// NOTE: We only do this in Win32-land, because the Win32 API can
|
|
// (usually) handle both 16-bit and 32-bit EXE formats, but
|
|
// the 16-bit API cannot handle 32-bit EXE formats.
|
|
// Also, we wait until PASS 2 so we know whether the version
|
|
// information is needed for the INF file, since it slows us
|
|
// down a little to gather it.
|
|
if (psess->fGetVerInfo) {
|
|
hgen = psess->hgenFileLast; // Item for this file
|
|
pfinfo = GLGetValue(hgen); // Get file info
|
|
AssertFinfo(pfinfo);
|
|
if (!getFileVerAndLang(achSrc,
|
|
&(pfinfo->verMS),
|
|
&(pfinfo->verLS),
|
|
&(pfinfo->pszVersion),
|
|
&(pfinfo->pszLang),
|
|
perr)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif // !BIT16
|
|
|
|
//** Compute file checksum (only if needed for INF file)
|
|
if (psess->fGetFileChecksum) {
|
|
hgen = psess->hgenFileLast; // Item for this file
|
|
pfinfo = GLGetValue(hgen); // Get file info
|
|
AssertFinfo(pfinfo);
|
|
if (!getFileChecksum(achSrc,&(pfinfo->checksum),perr)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//** Get compression type
|
|
tcomp = tcompFromSession(psess,perr);
|
|
|
|
//** Determine if we are putting file in cabinet, or straight to disk
|
|
if (inCabinet(psess,perr)) {
|
|
if (!ensureCabinet(psess,perr)) { // Make sure we have a cabinet
|
|
return FALSE;
|
|
}
|
|
|
|
//** Make sure MaxDiskSize is a multiple of the ClusterSize
|
|
if (!checkDiskClusterSize(psess,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Get limits on files per folder
|
|
hvar = VarFind(psess->hvlist,pszVAR_FOLDER_FILE_COUNT_THRESHOLD,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
cMaxFiles = VarGetLong(hvar); // Get file count limit
|
|
|
|
//** Check for overrun of files in folder limit
|
|
if ((cMaxFiles > 0) && // A limit is set
|
|
(psess->cFilesInFolder >= cMaxFiles)) { // Limit is exceeded
|
|
if (!FCIFlushFolder(psess->hfci,fciGetNextCabinet,fciStatus,&sae)) {
|
|
//** Only set error if we didn't already do so
|
|
if (!ErrIsError(sae.perr)) {
|
|
mapFCIError(perr,psess,szFCI_FLUSH_FOLDER,&psess->erf);
|
|
}
|
|
return FALSE;
|
|
}
|
|
psess->cFilesInFolder = 0; // Reset files in folder count
|
|
}
|
|
|
|
//** Add file to folder
|
|
if (!FCIAddFile( // Add file to cabinet
|
|
psess->hfci,
|
|
achSrc, // filename to add to cabinet
|
|
achDst, // name to store into cabinet file
|
|
pcmd->file.fRunFlag, // Flag indicating execute on extract
|
|
fciGetNextCabinet, // callback for continuation cabinet
|
|
fciStatus, // status callback
|
|
fciOpenInfo, // Open/get attribs/etc. callback
|
|
tcomp, // Compression type
|
|
&sae
|
|
)) {
|
|
//** Only set error if we didn't already do so
|
|
if (!ErrIsError(sae.perr)) {
|
|
mapFCIError(perr,psess,szFCI_ADD_FILE,&psess->erf);
|
|
}
|
|
return FALSE;
|
|
}
|
|
//** Update counts *after* FCIAddFile(), since large files may cause
|
|
// us to overflow to a new cabinet (or cabinets), and we want our
|
|
// fciGetNextCabinet() callback to reset these counts!
|
|
psess->cFilesInFolder++; // Added a file to the current folder
|
|
psess->cFilesInCabinet++; // Added a file to the current cabinet
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** OK, we're putting the file on the disk (not in a cabinet)
|
|
*/
|
|
//** Check for error from inCabinet() call
|
|
if (ErrIsError(perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//BUGBUG 19-Apr-1994 bens cabinet=on/cabinet=off disk space accounting broken
|
|
// If we are have an open cabinet, and then the DDF file tells us to go
|
|
// out of cabinet mode (regardless of the compression setting), then we
|
|
// really need to close the open cabinet and update the remaining free space.
|
|
//
|
|
// This means that if cabinet=on is set later, there will be no connection
|
|
// in the cabinet files between the new cabinet set and the old one!
|
|
//
|
|
// NEED TO DOCUMENT THIS BEHAVIOR IN THE SPEC!
|
|
|
|
|
|
//BUGBUG 30-Mar-1994 bens Don't support compressing individual files
|
|
if (tcomp != tcompTYPE_NONE) {
|
|
ErrSet(perr,pszDIAERR_SINGLE_COMPRESS,"%s",pcmd->file.achSrc);
|
|
return FALSE;
|
|
}
|
|
|
|
//** See if we are copying files; this feature of not copying files
|
|
// was implemented for the ACME group, so that their clients can
|
|
// generate "ADMIN" INF files quickly -- they do one Diamond run
|
|
// with one version of InfFileLineFormat with cabinets and compression
|
|
// on, and that generates the "disk" INF. Then they do another
|
|
// Diamond run with a different InfFileLineFormat, and they set
|
|
// DoNotCopyFiles=TRUE, and turn off cabinets and compression, so
|
|
// Diamond goes through all the motions to generate an INF, but
|
|
// skips the step of actually copying any files!
|
|
|
|
hvar = VarFind(psess->hvlist,pszVAR_DO_NOT_COPY_FILES,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
fCopyFile = !VarGetBool(hvar); // Invert setting
|
|
|
|
//** Get file info for this file
|
|
hgen = psess->hgenFileLast; // Item for this file
|
|
pfinfo = GLGetValue(hgen); // Get file info
|
|
AssertFinfo(pfinfo);
|
|
|
|
//** Switch to new disk if necessary, account for file
|
|
if (!newDiskIfNecessary(psess,pfinfo->cbFile,TRUE,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Make sure MaxDiskSize is a multiple of the ClusterSize
|
|
if (!checkDiskClusterSize(psess,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Construct complete filespec for destination file
|
|
if (!catDirAndFile(achFull, // gets "disk1\foo\setup.exe"
|
|
sizeof(achFull),
|
|
psess->achCurrOutputDir, // "disk1"
|
|
achDst, // "foo\setup.exe"
|
|
"",
|
|
perr)) {
|
|
return FALSE; // perr set already
|
|
}
|
|
|
|
//** If copying files, make sure destination directory is created
|
|
if (fCopyFile && !ensureDirectory(achFull,TRUE,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Copy file (or just merge file date/time/attr values)
|
|
//BUGBUG 01-Apr-1994 bens Pass status to CopyOneFile for more granularity
|
|
// Also, should think about separating out data for files that are
|
|
// not compressed versus those that are, so we can provide accurate
|
|
// statistics!
|
|
fSuccess = CopyOneFile(achFull, // Destination file name
|
|
achSrc, // Source file name
|
|
fCopyFile, // Control whether file is copied
|
|
(UINT)cbFILE_COPY_BUFFER, // Copy buffer size
|
|
fnofpDiamond, // Overrides date/time/attr
|
|
psess, // Context for fnofpDiamond
|
|
perr); // ERROR structure
|
|
|
|
//** Use true file size for status, ignore return code
|
|
fciStatus(statusFile,pfinfo->cbFile,pfinfo->cbFile,&sae);
|
|
|
|
//** Add file to INF (cabinet=0 ==> not inside a cabinet)
|
|
if (!modeInfAddFile(psess,achDst,psess->iDisk,0,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
return fSuccess;
|
|
} /* doFile() */
|
|
|
|
|
|
/*** ensureCabinetsFlushed - Make sure FCI is flushed out
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; cabinets flushed (if necessary)
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in
|
|
*/
|
|
BOOL ensureCabinetsFlushed(PSESSION psess, PERROR perr)
|
|
{
|
|
SESSIONANDERROR sae; // Context for FCI calls
|
|
|
|
AssertSess(psess);
|
|
|
|
//** NOP if we already flushed FCI
|
|
if (!psess->hfci) {
|
|
return TRUE;
|
|
}
|
|
|
|
//** Store context to pass through FCI calls
|
|
sae.psess = psess;
|
|
sae.perr = perr;
|
|
|
|
//** Flush out any final cabinet remnants
|
|
if (!FCIFlushCabinet(psess->hfci,FALSE,fciGetNextCabinet,fciStatus,&sae)) {
|
|
//** Only set error if we didn't already do so in FCIAddFile callback
|
|
if (!ErrIsError(sae.perr)) {
|
|
mapFCIError(perr,psess,szFCI_FLUSH_CABINET,&psess->erf);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//** Destroy FCI context
|
|
if (!FCIDestroy(psess->hfci)) {
|
|
mapFCIError(perr,psess,szFCI_DESTROY,&psess->erf);
|
|
return FALSE;
|
|
}
|
|
psess->hfci = NULL; // Clear out FCI context
|
|
|
|
return TRUE;
|
|
} /* ensureCabinetsFlushed() */
|
|
|
|
|
|
/*** computeSetID - accumulate cabinet set ID
|
|
*
|
|
* The cabinet set ID is used by FDI at decompress time to ensure
|
|
* that it received the correct continuation cabinet. We construct
|
|
* the set ID for a cabinet set by doing a computation on all of the
|
|
* destination files (during pass 1). This is likely to give unique
|
|
* set IDs, assuming our function is a good one (which it probably is
|
|
* is not).
|
|
*
|
|
* Entry:
|
|
* psess - session to update
|
|
* psz - String to "add" to set ID
|
|
*
|
|
* Exit:
|
|
* psess->setID updated;
|
|
*/
|
|
void computeSetID(PSESSION psess, char *psz)
|
|
{
|
|
//** Just add up all the characters, ignoring overflow
|
|
while (*psz) {
|
|
psess->setID += (USHORT)*psz++;
|
|
}
|
|
} /* computeSetID() */
|
|
|
|
|
|
/*** inCabinet - Returns indication if cabinet mode is on
|
|
*
|
|
* Entry:
|
|
* psess - Session to check
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; cabinet mode is on
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; cabinet mode is off, or an error (if perr is set)
|
|
*/
|
|
BOOL inCabinet(PSESSION psess, PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
|
|
hvar = VarFind(psess->hvlist,pszVAR_CABINET,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
return VarGetBool(hvar); // Get current setting
|
|
} /* inCabinet() */
|
|
|
|
|
|
/*** ensureCabinet - Make sure FCI has a cabinet open
|
|
*
|
|
* This function is called to create the FCI context. A normal DDF will
|
|
* only cause a single FCICreate() call to be made. But a DDF that turns
|
|
* cabinet mode on and off several times will cause several FCICreate()
|
|
* calls to be made.
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; cabinet is ready to receive files
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in
|
|
*/
|
|
BOOL ensureCabinet(PSESSION psess, PERROR perr)
|
|
{
|
|
CCAB ccab;
|
|
|
|
AssertSess(psess);
|
|
if (psess->hfci == NULL) { // Have to create context
|
|
//** Set CCAB for FCICreate
|
|
if (!ccabFromSession(&ccab,psess,0,perr)) { // No previous cabinet size
|
|
return FALSE;
|
|
}
|
|
|
|
//** Set reserved sizes (from variable settings)
|
|
if (!setCabinetReserve(&ccab,psess,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Create the FCI context
|
|
psess->hfci = FCICreate(
|
|
&psess->erf, // error code return structure
|
|
fciFilePlaced, // callback for file placement notify
|
|
fciAlloc,
|
|
fciFree,
|
|
fciTempFile,
|
|
&ccab
|
|
);
|
|
if (psess->hfci == NULL) {
|
|
mapFCIError(perr,psess,szFCI_CREATE,&psess->erf);
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
} /* ensureCabinet() */
|
|
|
|
|
|
/*** ccabFromSession - Fill in a CCAB structure from a SESSION structure
|
|
*
|
|
* Entry:
|
|
* pccab - CCAB to fill in
|
|
* psess - Session to use
|
|
* cbPrevCab - Size of previous cabinet; 0 if no previous cabinet
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; pccab updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL ccabFromSession(PCCAB pccab, PSESSION psess, ULONG cbPrevCab, PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
|
|
AssertSess(psess);
|
|
|
|
#ifndef REMOVE_CHICAGO_M6_HACK
|
|
//** Pass hack flag on to FCI
|
|
pccab->fFailOnIncompressible = psess->fFailOnIncompressible;
|
|
#endif
|
|
|
|
/*** Switch to new disk, if necessary
|
|
*
|
|
* NOTE: cbPrevCab is an *estimate* from FCI. We use it to decide
|
|
* if we need to switch to a new disk. If we *don't* switch to
|
|
* a new disk, then our free space on disk value (psess->cbDiskLeft)
|
|
* will be slightly off until we get called back by FCI at
|
|
* our fciStatus() function with statusCabinet, at which point
|
|
* we can correct the amount of free space.
|
|
*
|
|
* Also, we save this estimated size *before* we determine if
|
|
* we are switching to a new disk, since newDiskIfNecessary()
|
|
* will clear psess->cbCabinetEstimate if a new disk is needed,
|
|
* to prevent fciStatus() from messing with psess->cbDiskLeft.
|
|
*
|
|
* See fciStatus() for more details.
|
|
*/
|
|
psess->cbCabinetEstimate = cbPrevCab; // Save estimated size first
|
|
if (!newDiskIfNecessary(psess,(long)cbPrevCab,FALSE,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Construct new cabinet information
|
|
psess->iCabinet++; // New cabinet number
|
|
pccab->iCab = psess->iCabinet; // Set cabinet number for FCI
|
|
pccab->iDisk = psess->iDisk; // Set disk number for FCI
|
|
pccab->setID = psess->setID; // Carry over set ID
|
|
|
|
//** Get cabinet file name
|
|
if (!getVarWithOverride(psess,
|
|
pccab->szCab,
|
|
sizeof(pccab->szCab),
|
|
pszPATTERN_VAR_CAB_NAME,
|
|
pszVAR_CAB_NAME,
|
|
psess->iCabinet,
|
|
pszDIA_CABINET,
|
|
perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Get current disk output directory
|
|
Assert(sizeof(pccab->szCabPath) >= sizeof(psess->achCurrOutputDir));
|
|
strcpy(pccab->szCabPath,psess->achCurrOutputDir);
|
|
|
|
//** Set cabinet limits
|
|
hvar = VarFind(psess->hvlist,pszVAR_MAX_CABINET_SIZE,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pccab->cb = VarGetLong(hvar); // Get maximum cabinet size
|
|
if ((pccab->cb == 0) || // Default is max disk size
|
|
(pccab->cb > (ULONG)psess->cbDiskLeft)) { // Disk size is smaller
|
|
pccab->cb = psess->cbDiskLeft; // Use space on disk as cabinet limit
|
|
}
|
|
|
|
//** Set folder limits
|
|
hvar = VarFind(psess->hvlist,pszVAR_FOLDER_SIZE_THRESHOLD,perr);
|
|
Assert(!perr->fError); // FolderSizeThreshold must be defined
|
|
pccab->cbFolderThresh = VarGetLong(hvar); // Get disk size;
|
|
if (pccab->cbFolderThresh == 0) { // Use default value
|
|
pccab->cbFolderThresh = pccab->cb; // Use max cabinet size
|
|
}
|
|
|
|
//** Get user-readable disk label
|
|
Assert(sizeof(pccab->szDisk) >= sizeof(psess->achCurrDiskLabel));
|
|
strcpy(pccab->szDisk,psess->achCurrDiskLabel);
|
|
|
|
//** Save away cabinet and disk info for INF
|
|
if (!infAddCabinet(psess,
|
|
psess->iCabinet,
|
|
psess->iDisk,
|
|
pccab->szCab,
|
|
perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* ccabFromSession() */
|
|
|
|
|
|
long roundUp(long value, long chunk)
|
|
{
|
|
return ((value + chunk - 1)/chunk)*chunk;
|
|
}
|
|
|
|
|
|
/*** newDiskIfNecessary - Determine if new disk is necessary, update Session
|
|
*
|
|
* This function is called in the following cases:
|
|
* (1) No files have been placed on any disks or in any cabinets, yet.
|
|
* ==> This function is used as the common place to initialize all
|
|
* the disk information; we increment the disk number (to 1),
|
|
* set the max disk size, etc.
|
|
* (2) FCI has called us back to get the next cabinet information.
|
|
* ==> FCI has limited the cabinet size to almost exactly the size
|
|
* we specified in the last CCAB structure we passed to FCI
|
|
* (it is not exact, because the cabinet file needs the cabinet
|
|
* file name and disk label name for this new cabinet in order
|
|
* to figure out the precise size!). So we use the cbConsume
|
|
* value to figure out if the current disk is full (the directive
|
|
* file could permit multiple cabinets per disk, though that is
|
|
* kind of obscure).
|
|
* (3) We are placing a file on a disk *outside* of a cabinet.
|
|
* ==> In this case we need to check the limits on files per disk
|
|
* (the root directory size limitation) and the bytes per disk.
|
|
* If we cannot fit the file on the disk, then we increment to
|
|
* the next disk, leaving some free space.
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* cbConsume - Size of cabinet/file to be placed on current disk
|
|
* Pass -1 to force a new disk.
|
|
* Pass 0 if no previous cabinet.
|
|
* fSubOnNewDisk - TRUE => subtract cbConsume from new disk (used
|
|
* when copying a file to a disk outside a cabinet).
|
|
* FALSE => don't subtract cbConsume from new disk (used
|
|
* when copying a file to a cabinet).
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated if necessary for new disk
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in
|
|
*/
|
|
BOOL newDiskIfNecessary(PSESSION psess,
|
|
long cbConsume,
|
|
BOOL fSubOnNewDisk,
|
|
PERROR perr)
|
|
{
|
|
long cbCluster;
|
|
int cch;
|
|
long cMaxFiles;
|
|
HVARIABLE hvar;
|
|
char *pch;
|
|
|
|
AssertSess(psess);
|
|
|
|
//** Get cluster size to help decide if disk is full
|
|
hvar = VarFind(psess->hvlist,pszVAR_CLUSTER_SIZE,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
cbCluster = VarGetLong(hvar); // Get cluster size
|
|
psess->cbClusterCabEst = cbCluster; // Save cluster size for current disk
|
|
|
|
//** Get limits on files per disk (outside of cabinets)
|
|
hvar = VarFind(psess->hvlist,pszVAR_MAX_DISK_FILE_COUNT,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
cMaxFiles = VarGetLong(hvar); // Get file count limit
|
|
|
|
//** Figure out if we need to go to a new disk
|
|
if ((cbConsume == -1) || // Forced new disk
|
|
psess->fForceNewDisk || // .New Disk directive
|
|
(roundUp(cbConsume,cbCluster) > psess->cbDiskLeft) || // No more space
|
|
(!inCabinet(psess,perr) && (psess->cFilesInDisk >= cMaxFiles))) {
|
|
|
|
psess->fForceNewDisk = FALSE; // Reset force flag
|
|
psess->iDisk++; // New disk
|
|
|
|
//** Get max disk size for this disk
|
|
if (-1 == (psess->cbDiskLeft = getMaxDiskSize(psess,perr))) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Update ClusterSize and MaxDiskFileCount (if standard disk size)
|
|
if (!setDiskParameters(psess,NULL,psess->cbDiskLeft,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (fSubOnNewDisk) { // Have to subtract from new disk
|
|
psess->cbDiskLeft -= roundUp(cbConsume,cbCluster);
|
|
}
|
|
psess->cFilesInDisk = 1; // Only one file on new disk so far
|
|
|
|
//** Tell fciStatus() not to update psess->cbDiskLeft!
|
|
psess->cbCabinetEstimate = 0;
|
|
}
|
|
else { // Update space left on current disk
|
|
cbConsume = roundUp(cbConsume,cbCluster);
|
|
psess->cbDiskLeft -= cbConsume; // Update remaining space on disk
|
|
if (!inCabinet(psess,perr)) { // Not in a cabinet
|
|
psess->cFilesInDisk++; // Update count of files on disk
|
|
}
|
|
return TRUE; // Nothing more to do!
|
|
}
|
|
|
|
//** OK, we have a new disk:
|
|
// 1) Create output directory
|
|
// 2) Get user-readable disk label
|
|
// 3) Add disk to INF file
|
|
|
|
//** Get disk output directory name
|
|
if (!getVarWithOverride(psess,
|
|
psess->achCurrOutputDir,
|
|
sizeof(psess->achCurrOutputDir),
|
|
pszPATTERN_VAR_DISK_DIR,
|
|
pszVAR_DISK_DIR_NAME,
|
|
psess->iDisk,
|
|
pszDIA_DISK_DIR,
|
|
perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Make sure destination directory exists
|
|
if (!ensureDirectory(psess->achCurrOutputDir,FALSE,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Append path separator if necessary
|
|
pch = psess->achCurrOutputDir;
|
|
cch = strlen(pch);
|
|
appendPathSeparator(&(pch[cch-1]));
|
|
|
|
//**(2) Get the sticky, user-readable disk label
|
|
if (!getVarWithOverride(psess,
|
|
psess->achCurrDiskLabel,
|
|
sizeof(psess->achCurrDiskLabel),
|
|
pszPATTERN_VAR_DISK_LABEL,
|
|
pszVAR_DISK_LABEL_NAME,
|
|
psess->iDisk,
|
|
pszDIA_DISK_LABEL,
|
|
perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//**(3) Add new disk to INF file
|
|
if (!infAddDisk(psess,psess->iDisk,psess->achCurrDiskLabel,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
} /* newDiskIfNecessary() */
|
|
|
|
|
|
/*** setCabinetReserve - Set reserved size fields in CCAB
|
|
*
|
|
* Entry:
|
|
* pccab - CCAB to fill in
|
|
* psess - Session to use
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; pccab updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL setCabinetReserve(PCCAB pccab, PSESSION psess, PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
|
|
hvar = VarFind(psess->hvlist,pszVAR_RESERVE_PER_CABINET,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pccab->cbReserveCFHeader = (UINT)VarGetLong(hvar);
|
|
|
|
hvar = VarFind(psess->hvlist,pszVAR_RESERVE_PER_FOLDER,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pccab->cbReserveCFFolder = (UINT)VarGetLong(hvar);
|
|
|
|
hvar = VarFind(psess->hvlist,pszVAR_RESERVE_PER_DATA_BLOCK,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pccab->cbReserveCFData = (UINT)VarGetLong(hvar);
|
|
|
|
return TRUE;
|
|
} /* setCabinetReserve() */
|
|
|
|
|
|
/*** tcompFromSession - Get current compression setting
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns one of the tcompXXX equates
|
|
*
|
|
* Exit-Failure:
|
|
* Returns tcompBAD; perr filled in
|
|
*/
|
|
int tcompFromSession(PSESSION psess, PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
int typeC;
|
|
int level; // Quantum compression level
|
|
int memory; // Quantum compression memory
|
|
|
|
AssertSess(psess);
|
|
//** Get compression setting
|
|
hvar = VarFind(psess->hvlist,pszVAR_COMPRESS,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
if (VarGetBool(hvar)) { // Compression is on
|
|
//** Get the compression type
|
|
hvar = VarFind(psess->hvlist,pszVAR_COMPRESSION_TYPE,perr);
|
|
Assert(!ErrIsError(perr)); // Must be defined
|
|
typeC = CompTypeFromPSZ(VarGetString(hvar),perr);
|
|
Assert(!ErrIsError(perr)); // Checked when it was defined
|
|
switch (typeC) {
|
|
case tcompTYPE_MSZIP:
|
|
return tcompTYPE_MSZIP;
|
|
|
|
case tcompTYPE_QUANTUM:
|
|
#ifdef BIT16
|
|
//** Quantum *compression* not supported in 16-bit mode.
|
|
// We should never get here because the dfparse.c
|
|
// validation should generate an error if Quantum is
|
|
// selected.
|
|
Assert(0);
|
|
#endif
|
|
//** Get the compression level setting
|
|
hvar = VarFind(psess->hvlist,pszVAR_COMPRESSION_LEVEL,perr);
|
|
Assert(!ErrIsError(perr)); // Must be defined
|
|
level = CompLevelFromPSZ(VarGetString(hvar),perr);
|
|
Assert(!ErrIsError(perr)); // Checked when it was defined
|
|
|
|
//** Get the compression memory setting
|
|
hvar = VarFind(psess->hvlist,pszVAR_COMPRESSION_MEMORY,perr);
|
|
Assert(!ErrIsError(perr)); // Must be defined
|
|
memory = CompMemoryFromPSZ(VarGetString(hvar),perr);
|
|
Assert(!ErrIsError(perr)); // Checked when it was defined
|
|
|
|
//** Construct TCOMP and return it
|
|
return TCOMPfromTypeLevelMemory(typeC,level,memory);
|
|
}
|
|
//** Unknown compression type
|
|
Assert(0);
|
|
}
|
|
else {
|
|
return tcompTYPE_NONE; // Compression is off
|
|
}
|
|
Assert(0);
|
|
} /* tcompFromSession() */
|
|
|
|
|
|
/*** checkDiskClusterSize - Check disk size and cluster size
|
|
*
|
|
* Make sure disk size is valid for cluster size.
|
|
*
|
|
* Entry:
|
|
* psess - Session to check
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE;
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in
|
|
*/
|
|
BOOL checkDiskClusterSize(PSESSION psess, PERROR perr)
|
|
{
|
|
long cbCluster;
|
|
long cbDisk;
|
|
long cClusters;
|
|
HVARIABLE hvar;
|
|
|
|
//** Get max disk size
|
|
if (-1 == (cbDisk = getMaxDiskSize(psess,perr))) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Get cluster size
|
|
hvar = VarFind(psess->hvlist,pszVAR_CLUSTER_SIZE,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
cbCluster = VarGetLong(hvar);
|
|
|
|
//** Make sure disk size is an exact multiple of the cluster size
|
|
cClusters = cbDisk / cbCluster; // Gets truncated to integer
|
|
|
|
//** Return result
|
|
if (cbDisk == (cClusters*cbCluster)) {
|
|
return TRUE;
|
|
}
|
|
else {
|
|
//** Disk size is not a multiple of the cluster size
|
|
ErrSet(perr,pszDIAERR_DISK_CLUSTER_SIZE,"%ld%ld",cbDisk,cbCluster);
|
|
return FALSE;
|
|
}
|
|
} /* checkDiskClusterSize() */
|
|
|
|
|
|
/*** getMaxDiskSize - Get current maximum disk size setting
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns maximum disk size
|
|
*
|
|
* Exit-Failure:
|
|
* Returns -1; perr filled in
|
|
*/
|
|
long getMaxDiskSize(PSESSION psess, PERROR perr)
|
|
{
|
|
long cb;
|
|
HVARIABLE hvar;
|
|
|
|
//** Build variable name that *may* exist for this disk
|
|
// NOTE: During pass 1, and before newDiskIfNeccessary() is called,
|
|
// psess->iDisk will be 0, so we'll always get the MaxDiskSize
|
|
// variable value (unless someone happens to define MaxDiskSize0!)
|
|
//
|
|
if (!nameFromTemplate(psess->achMsg,
|
|
sizeof(psess->achMsg),
|
|
pszPATTERN_VAR_MAX_DISK_SIZE,
|
|
psess->iDisk,
|
|
pszVAR_MAX_DISK_SIZE,
|
|
perr)) {
|
|
Assert(0); // Should never fail
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** See if this variable exists
|
|
hvar = VarFind(psess->hvlist,psess->achMsg,perr);
|
|
if (hvar != NULL) { // Yes, get its value
|
|
cb = VarGetLong(hvar);
|
|
}
|
|
else { // NO, no MaxDiskSizeN variable
|
|
ErrClear(perr); // Not an error
|
|
//** Use default variable
|
|
hvar = VarFind(psess->hvlist,pszVAR_MAX_DISK_SIZE,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
cb = VarGetLong(hvar);
|
|
}
|
|
|
|
return cb;
|
|
} /* getMaxDiskSize() */
|
|
|
|
|
|
/*** setDiskParameters - Set ClusterSize/MaxDiskFileCount
|
|
*
|
|
* If the specified disk size is on our predefined list, then set the
|
|
* standard values for ClusterSize and MaxDiskFileCount.
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* pszDiskSize - Disk size string (NULL if cbDisk should be used instead)
|
|
* cbDisk - Disk size (only if pszDiskSize == NULL)
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* TRUE; ClusterSize/MaxDiskFileCount adjusted if specified size is
|
|
* on our special list.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns -1; perr filled in
|
|
*/
|
|
BOOL setDiskParameters(PSESSION psess,
|
|
char *pszDiskSize,
|
|
long cbDisk,
|
|
PERROR perr)
|
|
{
|
|
char achSize[50];
|
|
ERROR errIgnore;
|
|
char *psz;
|
|
|
|
//** Determine which parameter to use
|
|
if (pszDiskSize == NULL) {
|
|
_ltoa(cbDisk,achSize,10); // Convert number to string
|
|
psz = achSize;
|
|
}
|
|
else {
|
|
psz = pszDiskSize;
|
|
}
|
|
|
|
//** Look up special list, get integer size for sure
|
|
if (!(cbDisk = IsSpecialDiskSize(psess,psz))) {
|
|
return TRUE; // Not a special size
|
|
}
|
|
|
|
// NOTE: We have to be careful not to set these variables
|
|
// until they are defined!
|
|
if (VarFind(psess->hvlist,pszVAR_CLUSTER_SIZE,&errIgnore)) {
|
|
//** ClusterSize is defined
|
|
if (!VarSetLong(psess->hvlist,pszVAR_CLUSTER_SIZE,cbDisk,perr)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (VarFind(psess->hvlist,pszVAR_MAX_DISK_FILE_COUNT,&errIgnore)) {
|
|
//** MaxDiskFileCount is defined
|
|
if (!VarSetLong(psess->hvlist,pszVAR_MAX_DISK_FILE_COUNT,cbDisk,perr)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
} /* setDiskParameters() */
|
|
|
|
|
|
/*** fnofpDiamond - Override file date/time/attributes
|
|
*
|
|
* NOTE: See fileutil.h for entry/exit conditions.
|
|
*/
|
|
FNOVERRIDEFILEPROPERTIES(fnofpDiamond)
|
|
{
|
|
PFILEINFO pfinfo;
|
|
PSESSION psess;
|
|
|
|
psess = (PSESSION)pv;
|
|
AssertSess(psess);
|
|
|
|
//** Use override values and/or store real values
|
|
if (psess->fGenerateInf) { // Only valid if INF will be made
|
|
pfinfo = GLGetValue(psess->hgenFileLast);
|
|
AssertFinfo(pfinfo);
|
|
if (pfinfo->flags & fifDATE_SET) {
|
|
pfta->date = pfinfo->fta.date; // Override
|
|
}
|
|
else {
|
|
pfinfo->fta.date = pfta->date; // Store for INF generation
|
|
}
|
|
|
|
if (pfinfo->flags & fifTIME_SET) {
|
|
pfta->time = pfinfo->fta.time; // Override
|
|
}
|
|
else {
|
|
pfinfo->fta.time = pfta->time; // Store for INF generation
|
|
}
|
|
|
|
if (pfinfo->flags & fifATTR_SET) {
|
|
pfta->attr = pfinfo->fta.attr; // Override
|
|
}
|
|
else {
|
|
pfinfo->fta.attr = pfta->attr; // Store for INF generation
|
|
}
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* fnofpDiamond() */
|
|
|
|
|
|
/*** fciOpenInfo - Get file date, time, and attributes for FCI
|
|
*
|
|
* Entry:
|
|
* pszName - filespec
|
|
* pdate - buffer to receive date
|
|
* ptime - buffer to receive time
|
|
* pattribs - buffer to receive attributes
|
|
* pv - our context pointer
|
|
*
|
|
* Exit-Success:
|
|
* Returns file handle; *pdate, *ptime, *pattribs filled in.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns -1;
|
|
*/
|
|
FNFCIGETOPENINFO(fciOpenInfo)
|
|
{
|
|
FILETIMEATTR fta;
|
|
int hf;
|
|
PERROR perr = ((PSESSIONANDERROR)pv)->perr;
|
|
PSESSION psess = ((PSESSIONANDERROR)pv)->psess;
|
|
|
|
AssertSess(psess);
|
|
|
|
//** Get real file date, time, and attributes
|
|
if (!GetFileTimeAndAttr(&fta,pszName,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Get/Override file date/time/attributes
|
|
if (!fnofpDiamond(&fta,psess,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Tell FCI what to use
|
|
*pdate = fta.date;
|
|
*ptime = fta.time;
|
|
*pattribs = fta.attr;
|
|
|
|
//** Open the file
|
|
hf = _open(pszName, _O_RDONLY | _O_BINARY);
|
|
if (hf == -1) {
|
|
ErrSet(perr,pszDIAERR_OPEN_FAILED,"%s",pszName);
|
|
return -1; // abort on error
|
|
}
|
|
return hf;
|
|
}
|
|
|
|
|
|
/*** fciFilePlaced - FCI calls this when a file is commited to a cabinet
|
|
*
|
|
* Entry:
|
|
* pccab - Current cabinet structure
|
|
* pszFile - Name of file placed in this cabinet
|
|
* cbFile - File size
|
|
* fContinuation - TRUE => Continued from previous cabinet
|
|
* pv - Really a psae
|
|
*
|
|
* Exit-Success:
|
|
* returns anything but -1;
|
|
*
|
|
* Exit-Failure:
|
|
* returns -1;
|
|
*/
|
|
FNFCIFILEPLACED(fciFilePlaced)
|
|
{
|
|
PSESSION psess = ((PSESSIONANDERROR)pv)->psess;
|
|
PERROR perr = ((PSESSIONANDERROR)pv)->perr;
|
|
|
|
AssertSess(psess);
|
|
|
|
if (psess->levelVerbose >= vbMORE) {
|
|
if (fContinuation) {
|
|
MsgSet(psess->achMsg,pszDIA_FILE_IN_CAB_CONT,"%s%s%d%s",
|
|
pszFile, pccab->szCab, pccab->iCab, pccab->szDisk);
|
|
}
|
|
else {
|
|
MsgSet(psess->achMsg,pszDIA_FILE_IN_CAB,"%s%s%d%s",
|
|
pszFile, pccab->szCab, pccab->iCab, pccab->szDisk);
|
|
}
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
}
|
|
|
|
//** Add file entry to INF temp file
|
|
if (psess->fGenerateInf) { // Only if generating INF file
|
|
if (!fContinuation) { // Only if not a continuation
|
|
if (!modeInfAddFile(psess,
|
|
pszFile,
|
|
pccab->iDisk,
|
|
pccab->iCab,
|
|
perr)) {
|
|
return -1; // Abort with error
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0; // Success
|
|
} /* fciFilePlaced() */
|
|
|
|
|
|
/*** fciGetNextCabinet - FCI calls this to get new cabinet info
|
|
*
|
|
* NOTE: See FCI.H for more details.
|
|
*
|
|
* Entry:
|
|
* pccab - Points to previous current-cabinet structure
|
|
* cbPrevCab - Size of previous cabinet - ESTIMATE!
|
|
* pv - Really a psae
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; pccab updated with info for new cabinet
|
|
*
|
|
* Exit-Failure:
|
|
* returns FALSE; ((psae)pv)->perr filled in
|
|
*/
|
|
FNFCIGETNEXTCABINET(fciGetNextCabinet)
|
|
{
|
|
PSESSION psess = ((PSESSIONANDERROR)pv)->psess;
|
|
PERROR perr = ((PSESSIONANDERROR)pv)->perr;
|
|
|
|
//** Set CCAB for new cabinet (and maybe disk)
|
|
AssertSess(psess);
|
|
if (!ccabFromSession(pccab,psess,cbPrevCab,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Current folder and cabinet are empty
|
|
psess->cFilesInFolder = 0;
|
|
psess->cFilesInCabinet = 0;
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* fciGetNextCabinet() */
|
|
|
|
|
|
/*** fciStatus - FCI calls this periodically when adding files
|
|
*
|
|
* NOTE: See FCI.H for more details.
|
|
*
|
|
* Entry:
|
|
* typeStatus - Type of status call being made
|
|
* cb1 - Size of compressed data generated since last call
|
|
* cb2 - Size of uncompressed data processed since last call
|
|
* pv - Really a psae
|
|
*
|
|
* Exit-Success:
|
|
* returns anything but -1, continue building cabinets
|
|
*
|
|
* Exit-Failure:
|
|
* returns -1, FCI should abort
|
|
*/
|
|
FNFCISTATUS(fciStatus)
|
|
{
|
|
long cbCabinetActual;
|
|
double percent;
|
|
PERROR perr = ((PSESSIONANDERROR)pv)->perr;
|
|
PSESSION psess = ((PSESSIONANDERROR)pv)->psess;
|
|
|
|
AssertSess(psess);
|
|
switch (typeStatus) {
|
|
|
|
case statusFolder:
|
|
//** Adding folder to cabinet (i.e., folder flush)
|
|
psess->cFilesInFolder = 0; // reset count
|
|
return TRUE; // Continue
|
|
|
|
case statusFile:
|
|
//** Compressing a file
|
|
psess->cbFileBytes += cb2; // Update progress data
|
|
psess->cbFileBytesComp += cb1;
|
|
|
|
//** Compute percentage complete
|
|
if (psess->cbTotalFileBytes > 0) {
|
|
percent = psess->cbFileBytes/(float)psess->cbTotalFileBytes;
|
|
percent *= 100.0; // Make it 0..100
|
|
}
|
|
else {
|
|
Assert(0); // Should never get here
|
|
percent = 0.0;
|
|
}
|
|
|
|
//** Amount of status depends upon verbosity
|
|
if (psess->levelVerbose >= vbFULL) {
|
|
MsgSet(psess->achMsg,pszDIA_PERCENT_COMPLETE_DETAILS,
|
|
"%6.2f%,ld%,ld",
|
|
percent,psess->cbFileBytes,psess->cbFileBytesComp);
|
|
lineOut(psess,psess->achMsg,TRUE);
|
|
}
|
|
else {
|
|
MsgSet(psess->achMsg,pszDIA_PERCENT_COMPLETE_SOME,
|
|
"%6.2f%s%,ld%,ld",
|
|
percent, psess->achCurrFile, psess->iCurrFile, psess->cFiles);
|
|
//** NOTE: No line-feed, so that we don't scroll
|
|
lineOut(psess,psess->achMsg,FALSE);
|
|
}
|
|
return TRUE; // Continue
|
|
|
|
case statusCabinet:
|
|
/** Cabinet completed and written to disk
|
|
*
|
|
* If this cabinet did not force a disk change, then we need to
|
|
* correct the amount of free space left on the current disk,
|
|
* since FCI only passed us an *estimated* cabinet size when it
|
|
* called our fciGetNextCabinet() function!
|
|
*
|
|
* Why did FCI estimate the cabinet size, you ask? Because there
|
|
* is a "chicken-and-egg" situation between FCI and Diamond! Except
|
|
* for the last cabinet in a set of cabinets, each cabinet file
|
|
* has a *forward* reference to the next cabinet. FCI calls our
|
|
* fciGetNextCabinet() function to get the cabinet file name and
|
|
* disk name for this forward reference, and passes the *estimated*
|
|
* cabinet size of the current cabinet, because Diamond needs this
|
|
* information to decide if it has to put the next cabinet on a new
|
|
* disk.
|
|
*
|
|
* If Diamond decides to put the next cabinet on a new disk, then
|
|
* everything is fine, and the fact that Diamond had an estimate
|
|
* of the cabinet size is irrelevant. BUT, if more cabinets or
|
|
* files are being placed on the same disk, then Diamond needs an
|
|
* *exact* cabinet size so that the disk free space can be precise.
|
|
*
|
|
* So, FCI calls us back with the original estimate and the final
|
|
* size, and we adjust our disk free space amount as necessary.
|
|
*
|
|
* We also return to FCI the *rounded* cabinet size, so that it
|
|
* can adjust its internal maximum cabinet size, too!
|
|
*/
|
|
if (psess->cbCabinetEstimate != 0) {
|
|
//** Round up to cabinet size on disk
|
|
cbCabinetActual = roundUp(cb2,psess->cbClusterCabEst);
|
|
|
|
//** Need to add back old estimate and subtract actual size
|
|
Assert(psess->cbCabinetEstimate == (long)cb1);
|
|
psess->cbDiskLeft += roundUp(cb1,psess->cbClusterCabEst);
|
|
psess->cbDiskLeft -= roundUp(cb2,psess->cbClusterCabEst);
|
|
return cbCabinetActual; // Tell FCI size we actually used
|
|
}
|
|
return cb2; // Let FCI use size it had
|
|
|
|
default:
|
|
//** Unknown status callback
|
|
Assert(0);
|
|
return TRUE;
|
|
}
|
|
} /* fciStatus() */
|
|
|
|
|
|
/*** fciAlloc - memory allocator for FCI
|
|
*
|
|
* 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(fciAlloc)
|
|
{
|
|
#ifdef BIT16
|
|
//** Use 16-bit function
|
|
return _halloc(cb,1);
|
|
#else // !BIT16
|
|
//** Use 32-bit function
|
|
return malloc(cb);
|
|
#endif // !BIT16
|
|
} /* fciAlloc() */
|
|
|
|
|
|
/*** fciFree - memory free function for FCI
|
|
*
|
|
* Entry:
|
|
* pv - memory allocated by fciAlloc to be freed
|
|
*
|
|
* Exit:
|
|
* Frees memory
|
|
*/
|
|
FNFREE(fciFree)
|
|
{
|
|
#ifdef BIT16
|
|
//** Use 16-bit function
|
|
_hfree(pv);
|
|
#else // !BIT16
|
|
//** Use 32-bit function
|
|
free(pv);
|
|
#endif // !BIT16
|
|
}
|
|
|
|
|
|
/*** fciTempFile - Construct tempfile name for FCI
|
|
*
|
|
* Entry:
|
|
* pszTempName - Buffer to be filled in with tempfile name
|
|
* cbTempName - Size of tempfile name buffer
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psz filled in
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE;
|
|
*/
|
|
FNFCIGETTEMPFILE(fciTempFile)
|
|
{
|
|
char *psz;
|
|
|
|
psz = _tempnam("","dc"); // Get a name
|
|
if ((psz != NULL) && (strlen(psz) < (unsigned)cbTempName)) {
|
|
strcpy(pszTempName,psz); // Copy to caller's buffer
|
|
free(psz); // Free temporary name buffer
|
|
return TRUE; // Success
|
|
}
|
|
//** Failed
|
|
if (psz) {
|
|
free(psz);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*** getCompressedFileName - Generate compressed filename
|
|
*
|
|
* Entry:
|
|
* psess - Session (has variable list)
|
|
* pszResult - Buffer to receive concatentation
|
|
* cbResult - Size of pszResult
|
|
* pszSrc - Filespec to parse
|
|
* perr - ERROR structure to fill in
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; pszResult buffer has generated name
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*
|
|
* Notes:
|
|
* (1) Takes pszSrc filespec, trims off path, and replaces or appends
|
|
* the value of CompressedFileExtensionChar in the extension.
|
|
*
|
|
* Examples:
|
|
* (1) "foo" ,"_" => "foo._"
|
|
* (2) "foo.b" ,"_" => "foo.b_"
|
|
* (3) "foo.ba" ,"_" => "foo.ba_"
|
|
* (4) "foo.bar","_" => "foo.ba_"
|
|
*/
|
|
BOOL getCompressedFileName(PSESSION psess,
|
|
char * pszResult,
|
|
int cbResult,
|
|
char * pszSrc,
|
|
PERROR perr)
|
|
{
|
|
int cch;
|
|
char chTrail; // Trailing character to use
|
|
HVARIABLE hvar;
|
|
int i;
|
|
char *pch;
|
|
char *pchStart;
|
|
|
|
AssertSess(psess);
|
|
|
|
//** Get trailing character
|
|
hvar = VarFind(psess->hvlist,pszVAR_COMP_FILE_EXT_CHAR,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
pch = VarGetString(hvar); // Get trailing string
|
|
chTrail = *pch; // Take first char
|
|
|
|
//** Find name.ext
|
|
pchStart = getJustFileNameAndExt(pszSrc, perr);
|
|
if (pchStart == NULL) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** Make sure name is not too long
|
|
cch = strlen(pchStart); // Length of name.ext
|
|
if (cch >= cbResult) {
|
|
ErrSet(perr,pszDIAERR_PATH_TOO_LONG,"%s",pszSrc);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Find last extension separator
|
|
strcpy(pszResult,pchStart); // Copy name to output buffer
|
|
pchStart = pszResult + cch; // Assume there is no extension ("foo")
|
|
for (pch=pszResult; *pch; pch++) {
|
|
if ((*pch == chNAME_EXT_SEP) && // Got one "."
|
|
(*(pch+1) != chNAME_EXT_SEP) && // Ignore ".."
|
|
(*(pch+1) != chPATH_SEP1) && // Ignore ".\"
|
|
(*(pch+1) != chPATH_SEP2)) { // Ignore "./"
|
|
pchStart = pch+1; // Ext starts after separator
|
|
}
|
|
}
|
|
|
|
//** Increase length if not full extension (need room for '_' character)
|
|
if (strlen(pchStart) < 3) {
|
|
cch++;
|
|
}
|
|
|
|
//** Edit result buffer
|
|
if (*pchStart == '\0') { // Extension is empty or missing
|
|
if (*(pchStart-1) != chNAME_EXT_SEP) { // Need to add "."
|
|
*pchStart++ = chNAME_EXT_SEP;
|
|
cch++; // Account for added "."
|
|
}
|
|
//** Add trail character below
|
|
}
|
|
else { // Extension has at least one character
|
|
//** skip to location to place new character
|
|
for (i=0; (i<2) && *pchStart; i++) {
|
|
pchStart++;
|
|
}
|
|
}
|
|
|
|
//** Check for buffer overflow
|
|
if (cch >= cbResult) {
|
|
ErrSet(perr,pszDIAERR_PATH_TOO_LONG,"%s",pszSrc);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Finally, store trailing character
|
|
*pchStart++ = chTrail; // Store trailing character
|
|
*pchStart++ = '\0'; // Terminate filename
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* getJustFileNameAndExt() */
|
|
|
|
|
|
/*** resetSession - Reset SESSION members for start of a new pass
|
|
*
|
|
* Entry:
|
|
* psess - Description of operation to perform
|
|
*
|
|
* Exit:
|
|
* Initializes per-pass members.
|
|
*/
|
|
void resetSession(PSESSION psess)
|
|
{
|
|
AssertSess(psess);
|
|
psess->act = actBAD;
|
|
psess->iDisk = 0;
|
|
psess->iCabinet = 0;
|
|
psess->iFolder = 0;
|
|
psess->cbDiskLeft = -1; // Force new disk first time
|
|
psess->cErrors = 0;
|
|
psess->cWarnings = 0;
|
|
psess->cbFileBytes = 0;
|
|
psess->cbFileBytesComp = 0;
|
|
psess->iCurrFile = 0;
|
|
psess->fRunSeen = FALSE;
|
|
|
|
psess->cFilesInFolder = 0;
|
|
psess->cFilesInCabinet = 0;
|
|
psess->cFilesInDisk = 0;
|
|
psess->cbCabinetEstimate = 0; // No estimated cabinet size
|
|
psess->ddfmode = ddfmodeUNKNOWN; // Don't know unified vs.
|
|
// relational, yet
|
|
psess->fExpectFileCommand = TRUE; // Default is file copy commands
|
|
psess->fCopyToInf = FALSE; // Not in .InfBegin/End
|
|
psess->hgenFile = (HGENERIC)-1; // Not valid
|
|
psess->hgenFileLast = (HGENERIC)-1; // Not valid
|
|
} /* resetSession() */
|
|
|
|
|
|
/*** parseCommandLine - Parse the command line arguments
|
|
*
|
|
* Entry:
|
|
* 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)
|
|
{
|
|
ACTION act=actBAD; // No action determined, yet
|
|
char *apszFile[2]; // Non-directive file names
|
|
int cFile=0; // Count of non-directive file names seen
|
|
int i;
|
|
BOOL fDefine=FALSE; // TRUE => Last arg was /D
|
|
BOOL fFile=FALSE; // TRUE => Last arg was /F
|
|
BOOL fLocation=FALSE;// TRUE => Last arg was /L
|
|
HFILESPEC hfspec;
|
|
|
|
AssertSess(psess);
|
|
|
|
//** 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 (fDefine) {
|
|
ErrSet(perr,pszDIAERR_MISSING_VAR_DEFINE);
|
|
return FALSE;
|
|
}
|
|
if (fFile) {
|
|
ErrSet(perr,pszDIAERR_MISSING_FILE_NAME);
|
|
return FALSE;
|
|
}
|
|
if (fLocation) {
|
|
ErrSet(perr,pszDIAERR_MISSING_LOCATION);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (toupper(apszArg[i][1])) {
|
|
case chSWITCH_HELP:
|
|
if (apszArg[i][2] != '\0') {
|
|
ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
//** Ignore any remaining parameters
|
|
psess->act = actHELP; // Show help
|
|
return TRUE;
|
|
break;
|
|
|
|
#ifndef REMOVE_CHICAGO_M6_HACK
|
|
case chSWITCH_ANDY:
|
|
psess->fFailOnIncompressible = TRUE;
|
|
break;
|
|
#endif
|
|
|
|
case chSWITCH_DEFINE:
|
|
if (apszArg[i][2] != '\0') {
|
|
ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
else if ((act != actBAD) && (act != actDIRECTIVE)) {
|
|
//* Got /D when we are not doing /F processing
|
|
ErrSet(perr,pszDIAERR_SWITCH_NOT_EXPECTED,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
fDefine = TRUE;
|
|
break;
|
|
|
|
case chSWITCH_FILE:
|
|
if (apszArg[i][2] != '\0') {
|
|
ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
else if ((act != actBAD) && (act != actDIRECTIVE)) {
|
|
//* Got /F after we decided we're not doing /F
|
|
ErrSet(perr,pszDIAERR_SWITCH_NOT_EXPECTED,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
//* Next parm should be a directive file name
|
|
act = actDIRECTIVE; // The die is cast...
|
|
fFile = TRUE;
|
|
break;
|
|
|
|
case chSWITCH_LOCATION:
|
|
if (apszArg[i][2] != '\0') {
|
|
ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
//* Next parm should be a location
|
|
fLocation = TRUE;
|
|
break;
|
|
|
|
#ifdef ASSERT
|
|
case chSWITCH_MEMORY_DBG:
|
|
//** Turn on full memory heap checking (very slow!)
|
|
MemSetCheckHeap(TRUE);
|
|
break;
|
|
#endif
|
|
|
|
case chSWITCH_VERBOSE:
|
|
if (apszArg[i][2] != '\0') {
|
|
psess->levelVerbose = atoi(&apszArg[i][2]);
|
|
}
|
|
else {
|
|
psess->levelVerbose = vbFULL; // Default to FULL
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ErrSet(perr,pszDIAERR_BAD_SWITCH,"%s",apszArg[i]);
|
|
return FALSE;
|
|
break;
|
|
}
|
|
}
|
|
//** Not a command line switch
|
|
else if (fFile) {
|
|
//** Grab a directive file
|
|
if (!addDirectiveFile(psess,apszArg[i],perr)) {
|
|
//** Error adding directive file; perr already set
|
|
return FALSE; // Failure
|
|
}
|
|
fFile = FALSE; // Done eating directive file
|
|
}
|
|
else if (fDefine) {
|
|
//** Grab a define
|
|
if (!addCmdLineVar(psess,apszArg[i],perr)) {
|
|
//** Error adding definition; perr already set
|
|
return FALSE; // Failure
|
|
}
|
|
fDefine = FALSE; // Done eating definition
|
|
}
|
|
else if (fLocation) {
|
|
//** Grab the location
|
|
if (strlen(apszArg[i]) >= sizeof(psess->achCurrOutputDir)) {
|
|
ErrSet(perr,pszDIAERR_LOCATION_TOO_LONG,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
strcpy(psess->achCurrOutputDir,apszArg[i]);
|
|
//** Make sure destination directory exists
|
|
if (!ensureDirectory(psess->achCurrOutputDir,FALSE,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
fLocation = FALSE; // Done eating location
|
|
}
|
|
else {
|
|
//** Should be a file name;
|
|
// Make sure we haven't made up our mind, yet!
|
|
if ((act != actBAD) && (act != actFILE)) {
|
|
//** Not doing single file compress, so this is a bad arg
|
|
ErrSet(perr,pszDIAERR_BAD_PARAMETER,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
act = actFILE; // The die is cast...
|
|
|
|
//** Make sure we haven't seen too many file names
|
|
cFile++; // Count number of files we've seen
|
|
if (cFile > 2) {
|
|
//** Two many file names
|
|
ErrSet(perr,pszDIAERR_TWO_MANY_PARMS,"%s",apszArg[i]);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Store file name
|
|
apszFile[cFile-1] = apszArg[i];
|
|
}
|
|
}
|
|
|
|
//** Make sure no trailing /D or /F
|
|
if (fDefine) {
|
|
ErrSet(perr,pszDIAERR_MISSING_VAR_DEFINE);
|
|
return FALSE;
|
|
}
|
|
if (fFile) {
|
|
ErrSet(perr,pszDIAERR_MISSING_FILE_NAME);
|
|
return FALSE;
|
|
}
|
|
if (fLocation) {
|
|
ErrSet(perr,pszDIAERR_MISSING_LOCATION);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Update Session
|
|
switch (act) {
|
|
case actBAD:
|
|
case actHELP:
|
|
psess->act = actHELP; // If no args, show help
|
|
break;
|
|
|
|
case actDIRECTIVE:
|
|
psess->act = act;
|
|
break;
|
|
|
|
case actFILE:
|
|
psess->act = act;
|
|
//** Add source file specification
|
|
if (!(hfspec = addDirectiveFile(psess,apszFile[0],perr))) {
|
|
//** Error adding source file; perr already set
|
|
return FALSE; // Failure
|
|
}
|
|
|
|
if (cFile == 2) { // Destination filename specified
|
|
if (!FLSetDestination(hfspec,apszFile[1],perr)) {
|
|
//** Error setting destination file; perr already set
|
|
return FALSE; // Failure
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** addDirectiveFile - Add directive file 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 addDirectiveFile(PSESSION psess, char *pszArg, PERROR perr)
|
|
{
|
|
HFILESPEC hfspec;
|
|
|
|
//** Make sure file exists
|
|
if (getFileSize(pszArg,perr) == -1) {
|
|
return NULL; // perr already filled in
|
|
}
|
|
|
|
AssertSess(psess);
|
|
//** Make sure a list exists
|
|
if (psess->hflistDirectives == NULL) {
|
|
if (!(psess->hflistDirectives = FLCreateList(perr))) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//** Add file to list
|
|
if (!(hfspec = FLAddFile(psess->hflistDirectives, pszArg, NULL, perr))) {
|
|
return NULL;
|
|
}
|
|
|
|
//** Success
|
|
return hfspec;
|
|
} /* addDirectiveFile */
|
|
|
|
|
|
/*** addCmdLineVar - Add a command line variable to session list
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pszArg - Variable name=value to add
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; psess updated.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns actBAD; perr filled in with error.
|
|
*/
|
|
BOOL addCmdLineVar(PSESSION psess, char *pszArg, PERROR perr)
|
|
{
|
|
COMMAND cmd; // For var name & value
|
|
BOOL f;
|
|
|
|
//** Make sure asserts work
|
|
SetAssertSignature((&cmd),sigCOMMAND);
|
|
|
|
//** Parse assignment statment
|
|
if (!DFPParseVarAssignment(&cmd,psess,pszArg,perr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Assign variable
|
|
f = setVariable(psess,
|
|
cmd.set.achVarName,
|
|
cmd.set.achValue,
|
|
perr);
|
|
|
|
//** Clear signature
|
|
ClearAssertSignature((&cmd));
|
|
|
|
//** Return result
|
|
return f;
|
|
} /* addCmdLineVar() */
|
|
|
|
|
|
#ifdef ASSERT
|
|
/*** fnafReport - Report assertion failure
|
|
*
|
|
* NOTE: See asrt.h for entry/exit conditions.
|
|
*/
|
|
FNASSERTFAILURE(fnafReport)
|
|
{
|
|
//** Make sure we don't overwrite a status line!
|
|
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)
|
|
{
|
|
//** Make sure error starts on a new line
|
|
if (psess->fNoLineFeed) {
|
|
printf("\n");
|
|
psess->fNoLineFeed = FALSE;
|
|
}
|
|
|
|
//** Determine type of error
|
|
if (perr->pszFile != NULL) {
|
|
//** Error in a directive file
|
|
printf("%s(%d): %s: %s\n",
|
|
perr->pszFile,perr->iLine,pszDIAERR_ERROR,perr->ach);
|
|
}
|
|
else {
|
|
//** General error
|
|
printf("%s: %s\n",pszDIAERR_ERROR,perr->ach);
|
|
}
|
|
|
|
//** Count errors, to determine exit code and early out on MaxErrors
|
|
psess->cErrors++; // Count this error
|
|
}
|
|
|
|
|
|
/*** mapFCIError - Create error message from FCI error codes
|
|
*
|
|
* Entry:
|
|
* perr - ERROR structure to recieve message
|
|
* psess - Our context
|
|
* pszCall - FCI call that failed
|
|
* perf - FCI error structure
|
|
*
|
|
* Exit:
|
|
* perr filled in with formatted message
|
|
*/
|
|
void mapFCIError(PERROR perr, PSESSION psess, char *pszCall, PERF perf)
|
|
{
|
|
char *pszErrno;
|
|
|
|
pszErrno = mapCRTerrno(perf->erfType); // Get mapping, just in case
|
|
|
|
switch (perf->erfOper) {
|
|
|
|
case FCIERR_NONE:
|
|
Assert(0);
|
|
break;
|
|
|
|
case FCIERR_ALLOC_FAIL:
|
|
ErrSet(perr,pszFCIERR_ALLOC_FAIL,"%s",pszCall);
|
|
break;
|
|
|
|
case FCIERR_BAD_COMPR_TYPE:
|
|
ErrSet(perr,pszFCIERR_BAD_COMPR_TYPE,"%s",pszCall);
|
|
break;
|
|
|
|
case FCIERR_MCI_FAIL:
|
|
ErrSet(perr,pszFCIERR_MCI_FAIL,"%s%s",pszCall,psess->achCurrFile);
|
|
break;
|
|
|
|
case FCIERR_USER_ABORT:
|
|
ErrSet(perr,pszFCIERR_USER_ABORT,"%s",pszCall);
|
|
break;
|
|
|
|
case FCIERR_OPEN_SRC:
|
|
ErrSet(perr,pszFCIERR_OPEN_SRC,"%s%s%s",
|
|
pszCall,psess->achCurrFile,pszErrno);
|
|
break;
|
|
|
|
case FCIERR_READ_SRC:
|
|
ErrSet(perr,pszFCIERR_READ_SRC,"%s%s%s",
|
|
pszCall,psess->achCurrFile,pszErrno);
|
|
break;
|
|
|
|
case FCIERR_TEMP_FILE:
|
|
ErrSet(perr,pszFCIERR_TEMP_FILE,"%s%s",pszCall,pszErrno);
|
|
break;
|
|
|
|
case FCIERR_CAB_FILE:
|
|
ErrSet(perr,pszFCIERR_CAB_FILE,"%s%s",pszCall,pszErrno);
|
|
break;
|
|
|
|
case FCIERR_M6_HACK_INCOMPRESSIBLE:
|
|
ErrSet(perr,pszFCIERR_M6_HACK_INCOMPRESSIBLE,"%s%s",
|
|
pszCall,psess->achCurrFile);
|
|
break;
|
|
|
|
default:
|
|
ErrSet(perr,pszFCIERR_UNKNOWN_ERROR,"%s%d",pszCall,perf->erfOper);
|
|
break;
|
|
}
|
|
} /* mapFCIError() */
|
|
|
|
|
|
/*** mapCRTerrno - Get error string from C run-time library errno
|
|
*
|
|
* Entry:
|
|
* errno - C run-time library errno value.
|
|
*
|
|
* Exit:
|
|
* Returns pointer to appropriate causation string.
|
|
*/
|
|
char *mapCRTerrno(int errno)
|
|
{
|
|
switch (errno) {
|
|
case ECHILD: return pszCRTERRNO_ECHILD;
|
|
case EAGAIN: return pszCRTERRNO_EAGAIN;
|
|
case E2BIG: return pszCRTERRNO_E2BIG;
|
|
case EACCES: return pszCRTERRNO_EACCES;
|
|
case EBADF: return pszCRTERRNO_EBADF;
|
|
case EDEADLOCK: return pszCRTERRNO_EDEADLOCK;
|
|
case EDOM: return pszCRTERRNO_EDOM;
|
|
case EEXIST: return pszCRTERRNO_EEXIST;
|
|
case EINVAL: return pszCRTERRNO_EINVAL;
|
|
case EMFILE: return pszCRTERRNO_EMFILE;
|
|
case ENOENT: return pszCRTERRNO_ENOENT;
|
|
case ENOEXEC: return pszCRTERRNO_ENOEXEC;
|
|
case ENOMEM: return pszCRTERRNO_ENOMEM;
|
|
case ENOSPC: return pszCRTERRNO_ENOSPC;
|
|
case ERANGE: return pszCRTERRNO_ERANGE;
|
|
case EXDEV: return pszCRTERRNO_EXDEV;
|
|
default: return pszCRTERRNO_UNKNOWN;
|
|
}
|
|
Assert(0);
|
|
return NULL;
|
|
} /* mapCRTerrno() */
|
|
|
|
|
|
/*** updateHgenLast - Set correct psess->hgenLast
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* pszDst - Destination file name
|
|
*
|
|
* Exit:
|
|
* psess->hgenFileLast set to point to current file
|
|
*/
|
|
void updateHgenLast(PSESSION psess, char *pszDst)
|
|
{
|
|
PFILEINFO pfinfo;
|
|
|
|
Assert(psess->hgenFileLast != NULL); // Catch read off end of list
|
|
if (psess->hgenFileLast == (HGENERIC)-1) { // Get first file on list
|
|
psess->hgenFileLast = GLFirstItem(psess->hglistFiles);
|
|
}
|
|
else { // Get next file
|
|
psess->hgenFileLast = GLNextItem(psess->hgenFileLast);
|
|
}
|
|
|
|
//** Make sure we got the expected entry
|
|
pfinfo = GLGetValue(psess->hgenFileLast);
|
|
AssertFinfo(pfinfo);
|
|
Assert(strcmp(pszDst,GLGetKey(psess->hgenFileLast)) == 0);
|
|
} /* updateHgenLast() */
|
|
|
|
|
|
/*** modeInfAddFile - Add a file line to INF, depending upon DDF mode
|
|
*
|
|
* There are two cases to consider:
|
|
* (1) "relational" mode
|
|
* ==> Augment psess->hglistFiles with placement information
|
|
* (2) "unified" mode
|
|
* ==> Augment psess->hglistFiles with placement information and
|
|
* then format and write out file line to INF file.
|
|
* Entry:
|
|
* psess - Session
|
|
* inf - Area of INF file to receive line
|
|
* pszFile - Destination file name
|
|
* iDisk - Disk number (1-based)
|
|
* iCabinet - Cabinet number (1-based, 0=> not in cabinet)
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; line added to INF file
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL modeInfAddFile(PSESSION psess,
|
|
char *pszFile,
|
|
int iDisk,
|
|
int iCabinet,
|
|
PERROR perr)
|
|
|
|
{
|
|
HGENERIC hgen;
|
|
PFILEINFO pfinfo;
|
|
PFILEPARM pfparm;
|
|
|
|
AssertSess(psess);
|
|
Assert(psess->ddfmode != ddfmodeBAD);
|
|
Assert(psess->ddfmode != ddfmodeUNKNOWN);
|
|
|
|
//** Get file item
|
|
Assert(psess->hgenFile != NULL); // Catch read off end of list
|
|
if (psess->hgenFile == (HGENERIC)-1) { // Get first file on list
|
|
psess->hgenFile = GLFirstItem(psess->hglistFiles);
|
|
}
|
|
hgen = psess->hgenFile;
|
|
Assert(hgen != NULL);
|
|
psess->hgenFile = GLNextItem(hgen); // Advance for next time
|
|
|
|
//** Get fileinfo and augment it
|
|
pfinfo = GLGetValue(hgen);
|
|
AssertFinfo(pfinfo);
|
|
Assert(strcmp(pszFile,GLGetKey(hgen)) == 0); // Verify destination name
|
|
pfinfo->iDisk = iDisk; // Store placement info
|
|
pfinfo->iCabinet = iCabinet; // Store placement info
|
|
|
|
//** Let /SIZE parm override true file size if former is *larger*
|
|
// NOTE: This is only for reporting in the INF file -- this has
|
|
// no affect on space used on the disk!
|
|
pfparm = GLFindAndGetValue(pfinfo->hglistParm,pszPARM_FILESIZE);
|
|
if (pfparm) { // /Size specfied
|
|
AssertFparm(pfparm);
|
|
pfinfo->cbFile = atol(pfparm->pszValue);
|
|
}
|
|
|
|
//** Write INF line if in unified mode
|
|
if (psess->ddfmode == ddfmodeUNIFIED) {
|
|
if (!infAddFile(psess,pszFile,pfinfo,pfinfo->hglistParm,perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* modeInfAddFile() */
|
|
|
|
|
|
/*** modeInfAddLine - Add line to INF, depending upon DDF mode
|
|
*
|
|
* There are several cases to consider:
|
|
* (1) The disk or cabinet area is specified
|
|
* ==> Write line immediately to INF
|
|
* (2) "relational" mode AND GenerateINF is TRUE
|
|
* ==> Write line immediately to INF
|
|
* (3) "relational" mode AND GenerateINF is FALSE AND File area specified
|
|
* => ERROR -- it is unclear what the semantics of this should
|
|
* be, since the INF is not being generated in step with the
|
|
* file copy commands. So writes to the FILE area are not
|
|
* supported in the "layout" section of the DDF.
|
|
* (4) "unified" mode AND File area specified
|
|
* (a) No file copy commands have been processed
|
|
* ==> Write line immediately to INF
|
|
* (b) One or more file copy commands already done
|
|
* ==> This is the tricky case, because we need to make sure that
|
|
* the line is written to the INF in correct synchronization
|
|
* with surrounding file copy commands. If files are being
|
|
* placed in cabinets, then we may have passed file copy
|
|
* commands to FCI, but it may not yet have actually placed them
|
|
* in cabinets, so we won't have written their lines to the INF
|
|
* file! So, if any "unmatched" files are queued up, we append
|
|
* this INF to the last file, and this line (or lines) will be
|
|
* written when this file is actually added to the INF. If
|
|
* there are no queued files (we might not be in a cabinet, or
|
|
* the cabinet may have flushed), then we write to the INF
|
|
* immediately.
|
|
* (5) "unknown" mode (haven't decided between relational or unified)
|
|
* ==> Write line immediately to INF
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* inf - Area of INF file to receive line
|
|
* pszLine - Line to add
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; line added to INF file
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL modeInfAddLine(PSESSION psess,
|
|
INFAREA inf,
|
|
char *pszLine,
|
|
PERROR perr)
|
|
{
|
|
BOOL fGenerateInf;
|
|
HVARIABLE hvar;
|
|
PFILEINFO pfinfo;
|
|
PLINEINFO plinfo;
|
|
|
|
AssertSess(psess);
|
|
hvar = VarFind(psess->hvlist,pszVAR_GENERATE_INF,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
fGenerateInf = VarGetBool(hvar); // Get INF generation setting
|
|
|
|
/*
|
|
* Work common to Pass 1 & 2
|
|
*/
|
|
|
|
//** Check for invalid location of InfWrite FILE
|
|
if ((psess->ddfmode == ddfmodeRELATIONAL) &&
|
|
!fGenerateInf &&
|
|
(inf == infFILE)) {
|
|
//** InfWrite to file area in relation layout section of DDF
|
|
Assert(psess->fExpectFileCommand);
|
|
ErrSet(perr,pszDIAERR_INF_IN_LAYOUT);
|
|
return FALSE;
|
|
}
|
|
|
|
//** If this is pass 1, we're done
|
|
if (!psess->fPass2) {
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Pass 2
|
|
*/
|
|
|
|
//** Check for InfWrite FILE that must be postponed
|
|
if ((psess->ddfmode == ddfmodeUNIFIED) &&
|
|
(inf == infFILE) &&
|
|
(psess->iCurrFile > 0)) {
|
|
//** InfWrite to file area while doing file copy commands
|
|
Assert(fGenerateInf); // Must be generating INF
|
|
pfinfo = GLGetValue(psess->hgenFileLast); // Get last file added
|
|
AssertFinfo(pfinfo);
|
|
|
|
//** Do not have to postpone if preceding file written to INF already
|
|
if (pfinfo->flags & fifWRITTEN_TO_INF) {
|
|
return infAddLine(psess->hinf,inf,pszLine,perr);
|
|
}
|
|
|
|
//** Postpone write
|
|
if (pfinfo->hglistInfLines == NULL) { // Create list
|
|
pfinfo->hglistInfLines = GLCreateList(NULL,
|
|
DestroyLineInfo,
|
|
pszDIA_LINE_INFO,
|
|
perr);
|
|
if (!pfinfo->hglistInfLines) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
}
|
|
|
|
//** Create line info
|
|
if (!(plinfo = MemAlloc(sizeof(LINEINFO)))) {
|
|
ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_LINES);
|
|
return FALSE;
|
|
}
|
|
if (!(plinfo->pszLine = MemStrDup(pszLine))) {
|
|
ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_LINES);
|
|
MemFree(plinfo);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Add line to list
|
|
if (!GLAdd(pfinfo->hglistInfLines, // List
|
|
NULL, // key name
|
|
plinfo, // file info
|
|
pszDIA_LINE_INFO, // Description for error message
|
|
FALSE, // Uniqueness setting
|
|
perr)) {
|
|
MemFree(plinfo->pszLine);
|
|
MemFree(plinfo);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Initialize remainder of structure
|
|
plinfo->inf = inf;
|
|
SetAssertSignature(plinfo,sigLINEINFO);
|
|
return TRUE;
|
|
}
|
|
|
|
//** We're OK to write the line right now!
|
|
return infAddLine(psess->hinf,inf,pszLine,perr);
|
|
} /* modeInfAddLine() */
|
|
|
|
|
|
/*** addFileToSession - Add file to session list
|
|
*
|
|
* Entry:
|
|
* psess - Session to update
|
|
* pszSrc - Source file name
|
|
* pszDst - Destination file name (used as key in list)
|
|
* cbFile - Size of file
|
|
* pcmd - Command to process (ct == ctFILE)
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns HGENERIC; file added to psess->hglistFiles; pcmd->file.hglist
|
|
* moved to newly added FILEINFO entry!
|
|
*
|
|
* Exit-Failure:
|
|
* Returns NULL; perr filled in with error.
|
|
*/
|
|
HGENERIC addFileToSession(PSESSION psess,
|
|
char *pszSrc,
|
|
char *pszDst,
|
|
long cbFile,
|
|
PCOMMAND pcmd,
|
|
PERROR perr)
|
|
{
|
|
BOOL fUnique; // True if file name has to be unique
|
|
HGENERIC hgen;
|
|
HVARIABLE hvar;
|
|
PFILEINFO pfinfo;
|
|
PFILEINFO pfinfoFirst;
|
|
PFILEPARM pfparm;
|
|
|
|
AssertSess(psess);
|
|
Assert(pcmd->ct == ctFILE);
|
|
|
|
//** Get UniqueFiles setting
|
|
hvar = VarFind(psess->hvlist,pszVAR_UNIQUE_FILES,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
fUnique = VarGetBool(hvar); // Get default setting
|
|
|
|
//** See if default is overridden on command line
|
|
pfparm = GLFindAndGetValue(pcmd->file.hglist,pszFILE_UNIQUE);
|
|
if (pfparm) { // /Unique specfied
|
|
AssertFparm(pfparm);
|
|
if (-1 == (fUnique = BOOLfromPSZ(pfparm->pszValue,perr))) {
|
|
return NULL; // perr filled in already
|
|
}
|
|
}
|
|
|
|
//** Relational mode requires unique destination file names
|
|
if ((psess->ddfmode == ddfmodeRELATIONAL) && !fUnique) {
|
|
ErrSet(perr,pszDIAERR_MUST_BE_UNIQUE2);
|
|
return NULL;
|
|
}
|
|
|
|
//** Make sure file list exists
|
|
if (!psess->hglistFiles) {
|
|
psess->hglistFiles = GLCreateList(NULL, // No default
|
|
DestroyFileInfo,
|
|
pszDIA_FILE_INFO,
|
|
perr);
|
|
if (!psess->hglistFiles) {
|
|
return NULL; // perr already filled in
|
|
}
|
|
}
|
|
|
|
//** Create file info
|
|
if (!(pfinfo = MemAlloc(sizeof(FILEINFO)))) {
|
|
ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_FILES);
|
|
return NULL;
|
|
}
|
|
if (!(pfinfo->pszDDF = MemStrDup(perr->pszFile))) {
|
|
ErrSet(perr,pszDIAERR_OUT_OF_MEMORY,"%s",pszDIAOOM_TRACKING_FILES);
|
|
MemFree(pfinfo);
|
|
return NULL;
|
|
}
|
|
|
|
//** Add file to list
|
|
if (!(hgen = GLAdd(psess->hglistFiles, // List
|
|
pszDst, // key name
|
|
pfinfo, // file info
|
|
pszDIA_FILE, // Description for error message
|
|
fUnique, // Uniqueness setting
|
|
perr))) {
|
|
//** See if we need to remap error
|
|
if (perr->code == ERRGLIST_NOT_UNIQUE) {
|
|
//** Give info on where first file name was found
|
|
pfinfoFirst = GLGetValue(perr->pv);
|
|
AssertFinfo(pfinfoFirst);
|
|
ErrSet(perr,pszDIAERR_NOT_UNIQUE,"%s%s%d",
|
|
pszDst,pfinfoFirst->pszDDF,pfinfoFirst->ilineDDF);
|
|
}
|
|
MemFree(pfinfo->pszDDF);
|
|
MemFree(pfinfo);
|
|
return NULL;
|
|
}
|
|
|
|
//** Initialize remainder of structure
|
|
pfinfo->ilineDDF = perr->iLine; // Set line number in DDF
|
|
pfinfo->cbFile = cbFile; // File size
|
|
pfinfo->iDisk = idiskBAD; // Not yet determined
|
|
pfinfo->iCabinet = icabBAD; // Not yet determined
|
|
pfinfo->iFile = (int)psess->cFiles;// File index in layout
|
|
pfinfo->fta.date = 0; // Not yet determined
|
|
pfinfo->fta.time = 0; // Not yet determined
|
|
pfinfo->fta.attr = 0; // Not yet determined
|
|
pfinfo->hglistInfLines = NULL; // No lines to print
|
|
pfinfo->flags = 0; // Reset all flags
|
|
pfinfo->hglistParm = pcmd->file.hglist; // Save file parameters
|
|
pfinfo->checksum = 0; // Not yet determined
|
|
pfinfo->verMS = 0; // Not yet determined
|
|
pfinfo->verLS = 0; // Not yet determined
|
|
pfinfo->pszVersion = NULL; // Not yet determined
|
|
pfinfo->pszLang = NULL; // Not yet determined
|
|
pcmd->file.hglist = NULL; // Nothing to free!
|
|
|
|
//** Set signature after we get structure fully initialized
|
|
SetAssertSignature(pfinfo,sigFILEINFO);
|
|
return hgen; //
|
|
} /* addFileToSession() */
|
|
|
|
|
|
/*** checkReferences - Make sure all files in layout section are referenced
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; everything is dandy.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL checkReferences(PSESSION psess, PERROR perr)
|
|
{
|
|
BOOL fInf;
|
|
BOOL fOK;
|
|
HGENERIC hgen;
|
|
int iLine;
|
|
PFILEINFO pfinfo;
|
|
PFILEPARM pfparm;
|
|
char *pszFile;
|
|
|
|
AssertSess(psess);
|
|
|
|
//** Only need to check if in relational mode
|
|
if (psess->ddfmode != ddfmodeRELATIONAL) {
|
|
return TRUE;
|
|
}
|
|
|
|
//** Check list of files in layout to make sure all were referenced
|
|
fOK = TRUE; // Assume everything is OK
|
|
for (hgen = GLFirstItem(psess->hglistFiles);
|
|
hgen;
|
|
hgen = GLNextItem(hgen)) {
|
|
|
|
pfinfo = GLGetValue(hgen);
|
|
AssertFinfo(pfinfo);
|
|
if (!(pfinfo->flags & fifREFERENCED)) { // Not referenced
|
|
fInf = TRUE; // Assume INF reference is required
|
|
pfparm = GLFindAndGetValue(pfinfo->hglistParm,pszFILE_INF);
|
|
if (pfparm) { // /Inf specfied
|
|
AssertFparm(pfparm);
|
|
if (-1 == (fInf = BOOLfromPSZ(pfparm->pszValue,perr))) {
|
|
return FALSE; // perr filled in already
|
|
}
|
|
}
|
|
if (fInf) { // Should have been referenced
|
|
ErrSet(perr,pszDIAERR_FILE_NOT_REFD,"%s",GLGetKey(hgen));
|
|
|
|
//** Save current position
|
|
pszFile = perr->pszFile;
|
|
iLine = perr->iLine;
|
|
|
|
//** Set position of unreferenced file
|
|
perr->pszFile = pfinfo->pszDDF;
|
|
perr->iLine = pfinfo->ilineDDF;
|
|
|
|
//** Print error
|
|
printError(psess,perr); // Show error
|
|
|
|
//** Restore position
|
|
perr->pszFile = pszFile;
|
|
perr->iLine = iLine;
|
|
|
|
//** Reset error so we can continue
|
|
ErrClear(perr); // Catch all errors
|
|
fOK = FALSE; // Remember we had an error
|
|
}
|
|
}
|
|
}
|
|
//** Return status
|
|
return fOK;
|
|
} /* checkReferences() */
|
|
|
|
|
|
/** apszVarTemplate - List of variable name templates
|
|
*
|
|
* checkVariableDefinitions() uses this list to avoid complaining about
|
|
* "standard" variables that are of the form: Name<integer>.
|
|
*/
|
|
char *apszVarTemplate[] = {
|
|
pszPATTERN_VAR_CAB_NAME,
|
|
pszPATTERN_VAR_DISK_DIR,
|
|
pszPATTERN_VAR_DISK_LABEL,
|
|
pszPATTERN_VAR_INF_DISK_HEADER,
|
|
pszPATTERN_VAR_INF_DISK_LINE_FMT,
|
|
pszPATTERN_VAR_INF_CAB_HEADER,
|
|
pszPATTERN_VAR_INF_CAB_LINE_FMT,
|
|
pszPATTERN_VAR_INF_FILE_HEADER,
|
|
pszPATTERN_VAR_INF_FILE_LINE_FMT,
|
|
pszPATTERN_VAR_INF_HEADER,
|
|
pszPATTERN_VAR_INF_FOOTER,
|
|
pszPATTERN_VAR_MAX_DISK_SIZE,
|
|
NULL,
|
|
};
|
|
|
|
|
|
/** apszParmNames - List of InfXxx suffixes for standard parameters
|
|
*
|
|
* checkVariableDefinitions() uses this list to avoid complaining about
|
|
* "standard" parameters that are of the form: Inf<parm>.
|
|
*/
|
|
char *apszParmNames[] = {
|
|
pszPARM_FILEATTR,
|
|
pszPARM_CAB_NUMBER,
|
|
pszPARM_CAB_FILE,
|
|
pszPARM_CHECKSUM,
|
|
pszPARM_FILEDATE,
|
|
pszPARM_DISK_NUMBER,
|
|
pszPARM_FILENAME,
|
|
pszPARM_FILE_NUMBER,
|
|
pszPARM_INF,
|
|
pszPARM_LABEL,
|
|
pszPARM_LANG,
|
|
pszPARM_FILESIZE,
|
|
pszPARM_FILETIME,
|
|
pszPARM_UNIQUE,
|
|
pszPARM_VERNUM,
|
|
pszPARM_VERSTR,
|
|
};
|
|
|
|
|
|
/*** checkVariableDefinitions - Verify all variables are .Defined or PERM
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; everything is dandy.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL checkVariableDefinitions(PSESSION psess, PERROR perr)
|
|
{
|
|
int cb;
|
|
BOOL f;
|
|
BOOL fOK;
|
|
HVARIABLE hvar;
|
|
VARFLAGS vfl;
|
|
char **ppsz;
|
|
char *psz;
|
|
char *pszName;
|
|
char *pszSuffix;
|
|
|
|
AssertSess(psess);
|
|
|
|
//** Generate errors for any variables that are not permanent or were
|
|
// not .Defined.
|
|
// NOTE: We don't do this checking at .Set time, because user-defined
|
|
// variables may be specified on the command line, and we want
|
|
// to permit that *if* an INF file is specified.
|
|
//
|
|
fOK = TRUE; // Assume everything is OK
|
|
for (hvar = VarFirstVar(psess->hvlist);
|
|
hvar;
|
|
hvar = VarNextVar(hvar)) {
|
|
|
|
vfl = VarGetFlags(hvar);
|
|
if (0 == (vfl & (vflPERM | vflDEFINE))) { //** Not defined
|
|
f = FALSE; // Assume variable is OK
|
|
pszName = VarGetName(hvar);
|
|
|
|
//** See if this is one of the "template" variables
|
|
for (ppsz=apszVarTemplate; *ppsz; ppsz++) {
|
|
cb = strlen(*ppsz) - 1;
|
|
Assert(cb > 0);
|
|
Assert((*ppsz)[cb] == chDF_WILDCARD);
|
|
if (_strnicmp(pszName,*ppsz,cb) == 0) {
|
|
//** The prefix is valid, make sure rest is an integer
|
|
for (psz = pszName + cb; // Point at prefix
|
|
*psz && isdigit(*psz);
|
|
psz++) {
|
|
; // Check that rest of string is digits
|
|
}
|
|
f = (*psz == '\0'); // OK if we ran off end of string
|
|
break; // No need to continue checking
|
|
}
|
|
}
|
|
|
|
//** If no match so far, check for InfXxxx variable name
|
|
if (!f) {
|
|
//** See if this is one of the InfXxx parm default value vars
|
|
cb = strlen(pszPREFIX_INF_VARS);
|
|
if (_strnicmp(pszName,pszPREFIX_INF_VARS,cb) == 0) {
|
|
//** Starts with "Inf" -- check for parm name suffix
|
|
pszSuffix = pszName + cb; // Point at suffix
|
|
for (ppsz=apszParmNames; *ppsz; ppsz++) {
|
|
if (_stricmp(pszSuffix,*ppsz) == 0) {
|
|
f = TRUE; // Variable is OK
|
|
break; // Stop checking
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//** Check result of comparisons
|
|
if (!f) {
|
|
ErrSet(perr,pszDIAERR_UNDEFINED_VAR,"%s%s%s",
|
|
pszCMD_OPTION,
|
|
pszOPTION_EXPLICIT,
|
|
pszName);
|
|
|
|
//** Print error
|
|
printError(psess,perr); // Show error
|
|
|
|
//** Reset error so we can continue
|
|
ErrClear(perr); // Catch all errors
|
|
fOK = FALSE; // Remember we had an error
|
|
}
|
|
}
|
|
}
|
|
|
|
//** Return status
|
|
return fOK;
|
|
} /* checkVariableDefinitions() */
|
|
|
|
|
|
/*** getVarWithOverride - Checks for override for variable, gets value
|
|
*
|
|
* Entry:
|
|
* psess - Session
|
|
* pchDst - Buffer to receive constructed value
|
|
* cbDst - Size of pchDst
|
|
* pszPattern - Variable name pattern (i.e., CabinetLabel*)
|
|
* pszVar - Name of variable with default value (i.e., CabinetNameTemplate)
|
|
* i - Index to be used for pszPattern and pszTemplate
|
|
* pszKind - Description of variable (for error messages)
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE; buffer filled in
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL getVarWithOverride(PSESSION psess,
|
|
char *pchDst,
|
|
int cbDst,
|
|
char *pszPattern,
|
|
char *pszVar,
|
|
int i,
|
|
char *pszKind,
|
|
PERROR perr)
|
|
{
|
|
HVARIABLE hvar;
|
|
char *psz;
|
|
|
|
AssertSess(psess);
|
|
|
|
//** (1) Construct override variable name
|
|
if (!nameFromTemplate(psess->achMsg,
|
|
sizeof(psess->achMsg),
|
|
pszPattern,
|
|
i,
|
|
pszKind,
|
|
perr)) {
|
|
Assert(0); // Should never fail
|
|
return FALSE; // perr already filled in
|
|
}
|
|
|
|
//** (2) See if override variable exists
|
|
hvar = VarFind(psess->hvlist,psess->achMsg,perr);
|
|
if (hvar != NULL) { // Yes, get its value
|
|
psz = VarGetString(hvar); // Get disk label
|
|
if (strlen(psz) >= (size_t)cbDst) {
|
|
ErrSet(perr,pszDIAERR_VALUE_TOO_LONG,"%s%d%s",pszKind,cbDst-1,psz);
|
|
return FALSE;
|
|
}
|
|
strcpy(pchDst,psz);
|
|
}
|
|
else { // NO, no override for this *i*
|
|
ErrClear(perr); // Not an error
|
|
//** Construct default value
|
|
hvar = VarFind(psess->hvlist,pszVar,perr);
|
|
Assert(!perr->fError); // Must be defined
|
|
psz = VarGetString(hvar); // Get template
|
|
if (!nameFromTemplate(pchDst,
|
|
cbDst,
|
|
psz,
|
|
i,
|
|
pszKind,
|
|
perr)) {
|
|
return FALSE; // perr already filled in
|
|
}
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
} /* getVarWithOverride() */
|
|
|
|
//** Get CRC code
|
|
//BUGBUG 14-Dec-1994 bens Include code here to avoid makefile change
|
|
#include "crc32.c"
|
|
|
|
|
|
/*** getFileChecksum - Compute file checksum
|
|
*
|
|
* Entry:
|
|
* pszFile - Filespec
|
|
* pchecksum - Receives 32-bit checksum of file
|
|
* perr - ERROR structure
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE, *pchecksum filled in.
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; perr filled in with error.
|
|
*/
|
|
BOOL getFileChecksum(char *pszFile, ULONG *pchecksum, PERROR perr)
|
|
{
|
|
#define cbCSUM_BUFFER 4096 // File buffer size
|
|
int cb; // Amount of data in read buffer
|
|
ULONG csum=CRC32_INITIAL_VALUE; // Initialize CRC
|
|
char *pb=NULL; // Read buffer
|
|
int hf=-1; // File handle
|
|
int rc;
|
|
BOOL result=FALSE; // Assume failure
|
|
|
|
//** Initialize returned checksum (assume failure)
|
|
*pchecksum = csum;
|
|
|
|
//** Allocate file buffer
|
|
if (!(pb = MemAlloc(cbCSUM_BUFFER))) {
|
|
ErrSet(perr,pszDIAERR_NO_MEMORY_CRC,"%s",pszFile);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Open file
|
|
hf = _open(pszFile,_O_RDONLY | _O_BINARY,&rc);
|
|
if (hf == -1) {
|
|
ErrSet(perr,pszDIAERR_OPEN_FAILED,"%s",pszFile);
|
|
goto Exit;
|
|
}
|
|
|
|
//** Compute checksum
|
|
while (_eof(hf) == 0) {
|
|
cb = _read(hf,pb,cbCSUM_BUFFER);
|
|
if (cb == -1) {
|
|
ErrSet(perr,pszDIAERR_READ_FAIL_CRC,"%s",pszFile);
|
|
goto Exit;
|
|
}
|
|
if (cb != 0) {
|
|
csum = CRC32Compute(pb,cb,csum); // Accumulate CRC
|
|
}
|
|
}
|
|
|
|
//** Success
|
|
result = TRUE;
|
|
*pchecksum = csum; // Store checksum for caller
|
|
|
|
Exit:
|
|
if (hf != -1) {
|
|
_close(hf);
|
|
}
|
|
if (pb != NULL) {
|
|
MemFree(pb);
|
|
}
|
|
return result;
|
|
} /* getFileChecksum() */
|