Windows NT 4.0 source code leak
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

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