NT4/private/windows/diamond/dfparse.c
2020-09-30 17:12:29 +02:00

2141 lines
70 KiB
C

/*** dfparse.c - Directives File parser
*
* Microsoft Confidential
* Copyright (C) Microsoft Corporation 1993-1994
* All Rights Reserved.
*
* Author:
* Benjamin W. Slivka
*
* History:
* 10-Aug-1993 bens Initial version
* 12-Aug-1993 bens Added more entries to aiv[]
* 14-Aug-1993 bens Start working on parser proper
* 20-Aug-1993 bens Add more standard variables
* 22-Aug-1993 bens Do variable substitution in directive lines
* 11-Feb-1994 bens Start parsing individual commands (.Set)
* 16-Feb-1994 bens Handle ClusterSize; add DiskLabelTemplate
* 17-Feb-1994 bens Added 360K/720K disk parms; fixed validaters
* 12-Mar-1994 bens Add .Dump and .Define directives
* 25-Apr-1994 bens Add customizable INF stuff
* 26-May-1994 bens Add CompressionXxxx variables
* 03-Jun-1994 bens Implement .Define, .Option, .Dump
* 11-Jul-1994 bens Check for blank/comments lines *after* variable
* substitution!
* 27-Jul-1994 bens Allow leading blanks in .InfWrite[Xxx]
* 28-Mar-1995 jeffwe Add ChecksumWidth variable
*
* Exported Functions:
* DFPInit - Initialize directive file parser
* DFPParse - Parse a directive file
* DFPParseVarAssignment - Parse var=value string
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "types.h"
#include "asrt.h"
#include "error.h"
#include "variable.h"
#include "dfparse.h"
#include "inf.h"
#include "mem.h"
#include "misc.h"
#include "dfparse.msg"
#include "diamond.msg"
FNVCVALIDATE(fnvcvBool);
FNVCVALIDATE(fnvcvCabName);
FNVCVALIDATE(fnvcvChecksumWidth);
FNVCVALIDATE(fnvcvClusterSize);
FNVCVALIDATE(fnvcvCompType);
FNVCVALIDATE(fnvcvCompLevel);
FNVCVALIDATE(fnvcvCompMemory);
FNVCVALIDATE(fnvcvDateFmt);
FNVCVALIDATE(fnvcvDirDest);
FNVCVALIDATE(fnvcvDirSrc);
FNVCVALIDATE(fnvcvFile);
FNVCVALIDATE(fnvcvFileChar);
FNVCVALIDATE(fnvcvLong);
FNVCVALIDATE(fnvcvMaxDiskFileCount);
FNVCVALIDATE(fnvcvMaxDiskSize);
FNVCVALIDATE(fnvcvSectionOrder);
FNVCVALIDATE(fnvcvWildFile);
FNVCVALIDATE(fnvcvWildPath);
COMMANDTYPE ctFromCommandString(char *pszCmd, PERROR perr);
BOOL getCmdFromLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr);
BOOL getCommand(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr);
char *getQuotedString(char *pszDst,
int cbDst,
char *pszSrc,
char *pszDelim,
char *pszFieldName,
PERROR perr);
int IMDSfromPSZ(char *pszValue);
BOOL parseFileLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr);
char *parseParameterList(HGENLIST *phglist, char *pch, PERROR perr, BOOL *runflg);
BOOL parseReferenceLine(PCOMMAND pcmd,PSESSION psess,char *pszLine,PERROR perr);
BOOL parseSetCommand(PCOMMAND pcmd, PSESSION psess, char *pszArg, PERROR perr);
BOOL processLineWithQuotes(char *pszDst,
int cbDst,
char *pszSrc,
char *pszDelim,
char *pszFieldName,
PERROR perr);
long roundUpToPowerOfTwo(long x);
BOOL substituteVariables(char *pszDst,
int cbDst,
char *pszSrc,
HVARLIST hvlist,
PERROR perr);
//** aiv - list of predefined variables
typedef struct {
char *pszName; // Variable name
char *pszValue; // Default value
VARTYPE vtype; // Variable type
VARFLAGS vfl; // Special flags
PFNVCVALIDATE pfnvcv; // Validation function
} INITVAR; /* iv */
//** NOTE: The vflCOPY flag is used for variables whose *last* value must
//** be carried over to the *Pass 2* variable list. At present, the
//** variables that control the INF file headers and formats
//** require this property!
STATIC INITVAR aiv[] = {
{pszVAR_CABINET ,pszDEF_CABINET ,vtypeBOOL,vflPERM ,fnvcvBool },
{pszVAR_CABINET_FILE_COUNT_THRESHOLD,pszDEF_CABINET_FILE_COUNT_THRESHOLD,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_CAB_NAME ,pszDEF_CAB_NAME ,vtypeSTR ,vflPERM ,fnvcvWildPath},
{pszVAR_CHECKSUM_WIDTH ,pszDEF_CHECKSUM_WIDTH ,vtypeLONG,vflPERM ,fnvcvChecksumWidth},
{pszVAR_CLUSTER_SIZE ,pszDEF_CLUSTER_SIZE ,vtypeLONG,vflPERM ,fnvcvClusterSize},
{pszVAR_COMPRESS ,pszDEF_COMPRESS ,vtypeBOOL,vflPERM ,fnvcvBool },
{pszVAR_COMP_FILE_EXT_CHAR ,pszDEF_COMP_FILE_EXT_CHAR ,vtypeCHAR,vflPERM ,fnvcvFileChar},
{pszVAR_COMPRESSION_TYPE ,pszDEF_COMPRESSION_TYPE ,vtypeSTR ,vflPERM ,fnvcvCompType},
{pszVAR_COMPRESSION_LEVEL ,pszDEF_COMPRESSION_LEVEL ,vtypeLONG,vflPERM ,fnvcvCompLevel},
{pszVAR_COMPRESSION_MEMORY ,pszDEF_COMPRESSION_MEMORY ,vtypeLONG,vflPERM ,fnvcvCompMemory},
{pszVAR_DIR_DEST ,pszDEF_DIR_DEST ,vtypeSTR ,vflPERM ,fnvcvDirDest },
{pszVAR_DISK_DIR_NAME ,pszDEF_DISK_DIR_NAME ,vtypeSTR ,vflPERM ,fnvcvWildPath},
{pszVAR_DISK_LABEL_NAME ,pszDEF_DISK_LABEL_NAME ,vtypeSTR ,vflPERM ,NULL },
{pszVAR_DO_NOT_COPY_FILES ,pszDEF_DO_NOT_COPY_FILES ,vtypeBOOL,vflPERM ,fnvcvBool },
{pszVAR_FOLDER_FILE_COUNT_THRESHOLD ,pszDEF_FOLDER_FILE_COUNT_THRESHOLD ,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_FOLDER_SIZE_THRESHOLD ,pszDEF_FOLDER_SIZE_THRESHOLD ,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_GENERATE_INF ,pszDEF_GENERATE_INF ,vtypeBOOL,vflPERM ,fnvcvBool },
{pszVAR_INF_CAB_HEADER ,pszDEF_INF_CAB_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_CAB_LINE_FMT ,pszDEF_INF_CAB_LINE_FMT ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_COMMENT_STRING ,pszDEF_INF_COMMENT_STRING ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_DATE_FMT ,pszDEF_INF_DATE_FMT ,vtypeSTR ,vflPERM|vflCOPY,fnvcvDateFmt },
{pszVAR_INF_DISK_HEADER ,pszDEF_INF_DISK_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_DISK_LINE_FMT ,pszDEF_INF_DISK_LINE_FMT ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_FILE_HEADER ,pszDEF_INF_FILE_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_FILE_LINE_FMT ,pszDEF_INF_FILE_LINE_FMT ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_FILE_NAME ,pszDEF_INF_FILE_NAME ,vtypeSTR ,vflPERM ,fnvcvFile },
{pszVAR_INF_FOOTER ,pszDEF_INF_FOOTER ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_FOOTER1 ,pszDEF_INF_FOOTER1 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_FOOTER2 ,pszDEF_INF_FOOTER2 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_FOOTER3 ,pszDEF_INF_FOOTER3 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_FOOTER4 ,pszDEF_INF_FOOTER4 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_HEADER ,pszDEF_INF_HEADER ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_HEADER1 ,pszDEF_INF_HEADER1 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_HEADER2 ,pszDEF_INF_HEADER2 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_HEADER3 ,pszDEF_INF_HEADER3 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_HEADER4 ,pszDEF_INF_HEADER4 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_HEADER5 ,pszDEF_INF_HEADER5 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_HEADER6 ,pszDEF_INF_HEADER6 ,vtypeSTR ,vflPERM|vflCOPY,NULL },
{pszVAR_INF_SECTION_ORDER ,pszDEF_INF_SECTION_ORDER ,vtypeSTR ,vflPERM|vflCOPY,fnvcvSectionOrder},
{pszVAR_MAX_CABINET_SIZE ,pszDEF_MAX_CABINET_SIZE ,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_MAX_DISK_FILE_COUNT ,pszDEF_MAX_DISK_FILE_COUNT ,vtypeLONG,vflPERM ,fnvcvMaxDiskFileCount},
{pszVAR_MAX_DISK_SIZE ,pszDEF_MAX_DISK_SIZE ,vtypeLONG,vflPERM ,fnvcvMaxDiskSize},
{pszVAR_MAX_ERRORS ,pszDEF_MAX_ERRORS ,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_RESERVE_PER_CABINET ,pszDEF_RESERVE_PER_CABINET ,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_RESERVE_PER_DATA_BLOCK ,pszDEF_RESERVE_PER_DATA_BLOCK ,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_RESERVE_PER_FOLDER ,pszDEF_RESERVE_PER_FOLDER ,vtypeLONG,vflPERM ,fnvcvLong },
{pszVAR_RPT_FILE_NAME ,pszDEF_RPT_FILE_NAME ,vtypeSTR ,vflPERM ,fnvcvFile },
{pszVAR_DIR_SRC ,pszDEF_DIR_SRC ,vtypeSTR ,vflPERM ,fnvcvDirSrc },
{pszVAR_UNIQUE_FILES ,pszDEF_UNIQUE_FILES ,vtypeBOOL,vflPERM ,fnvcvBool },
};
//** acsatMap -- Map command string to command type
typedef struct {
char *pszName; // Command string
COMMANDTYPE ct; // Command type
} COMMAND_STRING_AND_TYPE; /* csat */
COMMAND_STRING_AND_TYPE acsatMap[] = {
{ pszCMD_DEFINE , ctDEFINE }, // Define
{ pszCMD_DELETE , ctDELETE }, // Delete
{ pszCMD_DUMP , ctDUMP }, // Dump
{ pszCMD_INF_BEGIN , ctINF_BEGIN }, // InfBegin
{ pszCMD_INF_END , ctINF_END }, // InfEnd
{ pszCMD_INF_WRITE , ctINF_WRITE }, // InfWrite
{ pszCMD_INF_WRITE_CAB , ctINF_WRITE_CAB }, // InfWriteCabinet
{ pszCMD_INF_WRITE_DISK , ctINF_WRITE_DISK }, // InfWriteDisk
{ pszCMD_NEW , ctNEW }, // New
{ pszCMD_OPTION , ctOPTION }, // Option
{ pszCMD_SET , ctSET }, // Set
{ NULL , ctBAD }, // end of list
};
/*** mds -- Map special disk size strings to disk attributes
*
* Data for the amds[] array was gathered using CHKDSK to report
* the cluster size and disk size, and a QBASIC program was used
* to fill up the root directory.
*/
typedef struct {
char *pszSpecial; // Name used in directive file
char *pszFilesInRoot; // Maximum number of files in root dir
char *pszClusterSize; // Cluster size in bytes
char *pszDiskSize; // Disk size in bytes
} MAP_DISK_SIZE; /* mds */
MAP_DISK_SIZE amds[] = {
// tag nFiles cbCluster cbDisk
//-------------- ------- --------- ------------
{pszVALUE_360K , "112", "1024", "362496"}, // 360K floppy disk
{pszVALUE_720K , "112", "1024", "730112"}, // 720K floppy disk
{pszVALUE_120M , "224", "512", "1213952"}, // 1.2M floppy disk
{pszVALUE_125M , "192", "1024", "1250304"}, // 1.25M (NEC Japan)
{pszVALUE_144M , "224", "512", "1457664"}, // 1.44M floppy disk
{pszVALUE_168M , "16", "2048", "1716224"}, // DMF "1.68M" floppy
{pszVALUE_DMF168, "16", "2048", "1716224"}, // DMF "1.68M" floppy
{pszVALUE_CDROM , "65535", "2048", "681984000"}, // 5 1/4" CD-ROM
//NOTE: 12-Mar-1994 bens This info supplied by rickdew (Rick Dewitt)
//
// Standard CD has 74-minute capacity.
// Sector size is 2K.
// Sectors per minute is 75.
// Number of sectors = 74*60*75 = 333,000
// Total bytes = 333,000*2048 = 681,984,000
// Number of files in the root is unlimited, but MS-DOS limits to 64K
};
#define nmds (sizeof(amds)/sizeof(MAP_DISK_SIZE))
//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
//
// Exported Functions
//
//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
/*** DFPInit - initialize directive file parser
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
HVARLIST DFPInit(PSESSION psess,PERROR perr)
{
HVARLIST hvlist;
int i;
// Create variable list
if (!(hvlist = VarCreateList(perr))) {
return NULL;
}
//** Define standard variables
for (i=0; i<(sizeof(aiv)/sizeof(INITVAR)); i++) {
if (!VarCreate(hvlist,
aiv[i].pszName,
aiv[i].pszValue,
aiv[i].vtype,
aiv[i].vfl,
aiv[i].pfnvcv,
perr)) {
//** Embellish standard VarCreate error message
strcpy(psess->achMsg,perr->ach); // Save error
ErrSet(perr,pszDFPERR_CREATE_STD_VAR,
"%s%s",aiv[i].pszName,psess->achMsg);
return NULL;
}
}
//** Success
return hvlist;
}
/*** DFPParse - Parse a directive file
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
BOOL DFPParse(PSESSION psess,
HTEXTFILE htfDF,
PFNDIRFILEPARSE pfndfp,
PERROR perr)
{
COMMAND cmd;
char achLine[cbMAX_DF_LINE];
int iLine;
AssertSess(psess);
SetAssertSignature((&cmd),sigCOMMAND);
iLine = 0;
//** Parse directives
while (!TFEof(htfDF)) {
//** Get a line
if (!TFReadLine(htfDF,achLine,sizeof(achLine),perr)) {
if (TFEof(htfDF)) { // No Error
return TRUE;
}
else { // Something is amiss
return FALSE;
}
}
iLine++; // Count it
perr->iLine = iLine; // Set line number for error messages
perr->pszLine = achLine; // Set line ptr for error messages
//** Echo line to output, if verbosity level high enough
if (psess->levelVerbose >= vbMORE) {
printf("%d: %s\n",iLine,achLine);
}
//** Parse it
getCmdFromLine(&cmd,psess,achLine,perr);
// Note: Errors in perr are handled by the client!
//** Give it to client
if (!(*pfndfp)(psess,&cmd,htfDF,achLine,iLine,perr)) {
ClearAssertSignature((&cmd));
return FALSE;
}
}
//** Clear signature
ClearAssertSignature((&cmd));
//** Success
return TRUE;
}
/*** DFPParseVarAssignment - Parse var=value string
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
BOOL DFPParseVarAssignment(PCOMMAND pcmd, PSESSION psess, char *pszArg, PERROR perr)
{
//BUGBUG 11-Feb-1994 bens var1=%var2% broken if var2 has trailing spaces
// The problem is that we do variable substitution before any other
// parsing takes place, so the following directives:
// .set var2="one "
// .set var1=%var2%
// Cause us to see the second line as:
// .set var1=one
// and store "one", not "one " as the user might have expected.
//
int cch;
char *pch;
char *pchEnd;
AssertCmd(pcmd);
AssertSess(psess);
pch = pszArg;
//** Make sure a variable name is present
if (*pch == '\0') {
ErrSet(perr,pszDFPERR_MISSING_VAR_NAME,"%s",pszCMD_SET);
return FALSE;
}
//** Find end of variable name
// Var = Value <eos>
// ^
pchEnd = strpbrk(pch,szDF_SET_CMD_DELIM); // Point after var name
// Var = Value <eos>
// ^
if (pchEnd == NULL) { // No assignment operator
ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL);
return FALSE;
}
//** Make sure variable name is not too long
cch = pchEnd - pch;
if (cch >= cbVAR_NAME_MAX) {
ErrSet(perr,pszDFPERR_VAR_NAME_TOO_LONG,"%d%s",cbVAR_NAME_MAX-1,pch);
return FALSE;
}
//** Copy var name to command structure, and NUL terminate string
memcpy(pcmd->set.achVarName,pch,cch);
pcmd->set.achVarName[cch] = '\0';
//** Make sure assignment operator is present
// Var = Value <eos>
// ^
pch = pchEnd + strspn(pchEnd,szDF_WHITE_SPACE);
// Var = Value <eos>
// ^
if (*pch != chDF_EQUAL) {
ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL);
return FALSE;
}
//** Skip to value. NOTE: Value can be empty, we permit that!
// Var = Value <eos>
// ^
pch++; // Skip over assignment operator
pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space
// Var = Value
// ^
//** Now parse through possibly quoted strings, to end of value
// REMEMBER: We have to trim trailing whitespace
return processLineWithQuotes(pcmd->set.achValue, // destination
sizeof(pcmd->set.achValue), // dest size
pch, // source
szDF_QUOTE_SET, // quoting set
pszDFP_VAR_VALUE, // field name
perr);
} /* DFPParseVarAssignment() */
/*** IsSpecialDiskSize - Check if supplied size is a standard one
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
long IsSpecialDiskSize(PSESSION psess,char *pszDiskSize)
{
int i;
i = IMDSfromPSZ(pszDiskSize); // See if special value
if (i != -1) { // Got a special value
return atol(amds[i].pszDiskSize); // Return disk size
}
else { // Not special
return 0;
}
} /* IsSpecialDiskSize() */
/*** lineOut - write line to stdout with padding
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
void lineOut(PSESSION psess, char *pszLine, BOOL fLineFeed)
{
int cch;
char *pszBlanks;
//** Do /P (pause) processing
AssertSess(psess);
//BUGBUG 21-Feb-1994 bens Do screen pausing (/P)
//** Determine how much blank padding, if any, is needed
cch = strlen(pszLine); // Length of line to be written
if (cch >= psess->cchLastLine) {
pszBlanks = psess->achBlanks + cchSCREEN_WIDTH; // Empty
}
else {
pszBlanks = psess->achBlanks + cchSCREEN_WIDTH -
(psess->cchLastLine - cch);
}
//** Print the line
if (fLineFeed) {
printf("%s%s\n",pszLine,pszBlanks);
cch = 0; // Nothing to overwrite next time
}
else {
printf("%s%s\r",pszLine,pszBlanks);
}
psess->fNoLineFeed = !fLineFeed;
//** Remember how much to overwrite for next time
psess->cchLastLine = cch; // For overwrite next time
} /* lineOut() */
//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
//
// Private Functions
//
//*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
/*** getCmdFromLine - Construct a command from a directive line
*
* Entry:
* pcmd - Command to fill in after line is parsed
* psess - Session state
* pszLine - Line to parse
* perr - ERROR structure
*
* Exit-Success:
* Returns TRUE; pcmd filled in
*
* Exit-Failure:
* Returns FALSE; perr filled in with error.
*/
BOOL getCmdFromLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr)
{
char achLine[cbMAX_DF_LINE]; // Variable-substituted line
int cbDst;
char *pch; // Used to parse pszLine
char *pszSrc;
char *pszDst;
AssertSess(psess);
pch = pszLine + strspn(pszLine,szDF_WHITE_SPACE); // Skip leading space
//** Do variable substitution if not in copy to INF mode
if (psess->fCopyToInf) { // Don't edit lines going to INF
pszDst = achLine;
cbDst = sizeof(achLine);
pszSrc = pszLine;
if (!copyBounded(&pszDst,&cbDst,&pszSrc,0)) {
return FALSE;
}
}
else { //** Edit lines that are NOT going straight to an INF area
//** Perform variable substitution on line, including stripping
// comments and any trailing white space!
if (!substituteVariables(achLine,sizeof(achLine),
pszLine,psess->hvlist,perr)) {
return FALSE; // perr already filled in
}
}
//** Determine the command type, and parse it
pcmd->ct = ctBAD; // Catch errors
pch = achLine + strspn(achLine,szDF_WHITE_SPACE); // Skip leading space
//** Check for comment lines and blank lines
if ((*pch == chDF_COMMENT) || // Only a comment on the line
(*pch == '\0') ) { // Line is completely blank
pcmd->ct = ctCOMMENT;
return TRUE;
}
//** Check for directives, etc.
//** JEFFWE - Allow .\file and ..\file even if command prefix is '.'
if ((*pch == chDF_CMD_PREFIX) &&
((chDF_CMD_PREFIX != '.') ||
(*(pch+1) != '.') &&
(*(pch+1) != '\\'))) {
if (!getCommand(pcmd,psess,achLine,perr)) {
return FALSE;
}
}
else if (psess->fCopyToInf) {
//** Copy a line to an area of the INF file
pcmd->ct = ctINF_WRITE; // Set command type
pcmd->inf.inf = psess->inf; // Use area specified by .InfBegin
pszDst = pcmd->inf.achLine;
cbDst = sizeof(pcmd->inf.achLine);
pszSrc = achLine;
if (!copyBounded(&pszDst,&cbDst,&pszSrc,0)) {
return FALSE;
}
}
else if (psess->fExpectFileCommand) {
//** A file specification
if (!parseFileLine(pcmd,psess,achLine,perr)) {
return FALSE;
}
}
else {
//** An INF file reference
if (!parseReferenceLine(pcmd,psess,achLine,perr)) {
return FALSE;
}
}
//** Success
return TRUE;
}
/*** getCommand - Parse a directive command line
*
* Entry:
* pcmd - Command to fill in after line is parsed
* psess - Session state
* pszLine - Line to parse (already known to have command start char)
* perr - ERROR structure
*
* Exit-Success:
* Returns TRUE; pcmd filled in
*
* Exit-Failure:
* Returns FALSE; perr filled in with error.
*/
BOOL getCommand(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr)
{
char achCmd[cbMAX_COMMAND_NAME]; // Command name
int cbDst;
int cch;
COMMANDTYPE ct;
char *pch; // Used to parse pszLine
char *pchEnd; // Used to parse pszLine
char *pszSrc;
char *pszDst;
AssertCmd(pcmd);
AssertSess(psess);
//** Skip to start of command name
pch = pszLine + strspn(pszLine,szDF_WHITE_SPACE); // Skip white space
Assert(*pch == chDF_CMD_PREFIX);
pch++; // Skip command character
//** Find end of command name; // Compute length of command name
pchEnd = strpbrk(pch,szDF_WHITE_SPACE); // Point at first char after cmd
if (pchEnd == NULL) { // Command name runs to end of line
cch = strlen(pch);
}
else {
cch = pchEnd - pch;
}
//** Copy command name to local buffer
if (cch >= cbMAX_COMMAND_NAME) {
ErrSet(perr,pszDFPERR_CMD_NAME_TOO_LONG,"%s",pszLine);
return FALSE;
}
memcpy(achCmd,pch,cch); // Copy it
achCmd[cch] = '\0'; // Terminate it
//** See if it really is a command
if (ctBAD == (ct = ctFromCommandString(achCmd,perr))) {
return FALSE; // perr has error
}
//** Set command type
pcmd->ct = ct;
//** Find start of first argument (if any)
// pch = start of command name
// cch = length of command name
pch += cch; // Point to where argument could start
pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space
//** Parse remainder of command, as appropriate
// pch = start of first argument (will be '\0' if no arguments present)
switch (ct) {
case ctCOMMENT:
return TRUE; // Nothing to do
case ctDEFINE:
//** Syntax is identical to .Set command
return parseSetCommand(pcmd,psess,pch,perr);
case ctDUMP: // Dump variable settings to stdout
return TRUE;
case ctDELETE:
//** Make sure a variable name is present
if (*pch == '\0') {
ErrSet(perr,pszDFPERR_MISSING_VAR_NAME,"%s",pszCMD_DELETE);
return FALSE;
}
//** Make sure only one variable name is present
pchEnd = strpbrk(pch,szDF_WHITE_SPACE); // Skip to end of var name
if (pchEnd != NULL) {
ErrSet(perr,pszDFPERR_BAD_FORMAT,"%s%s",pszCMD_DELETE,pch);
return FALSE;
}
//** Save variable name
pszDst = pcmd->delete.achVarName;
cbDst = sizeof(pcmd->delete.achVarName);
pszSrc = pch;
if (!copyBounded(&pszDst,&cbDst,&pszSrc,0)) {
return FALSE;
}
return TRUE;
case ctINF_BEGIN:
if (_stricmp(pch,pszBEGIN_FILE) == 0) {
psess->inf = infFILE;
}
else if (_stricmp(pch,pszBEGIN_CAB) == 0) {
psess->inf = infCABINET;
}
else if (_stricmp(pch,pszBEGIN_DISK) == 0) {
psess->inf = infDISK;
}
else {
ErrSet(perr,pszDFPERR_UNKNOWN_KEYWORD,"%s%s",pszCMD_INF_BEGIN,pch);
return FALSE;
}
psess->fCopyToInf = TRUE; // Turn on copy mode
return TRUE;
case ctINF_END:
if (!psess->fCopyToInf) { // Not in .InfBegin block
ErrSet(perr,pszDFPERR_END_WITHOUT_BEGIN,"%s%s",
pszCMD_INF_END,pszCMD_INF_BEGIN);
return FALSE;
}
psess->fCopyToInf = FALSE; // Turn off copy mode
psess->inf = infBAD;
return TRUE;
case ctINF_WRITE:
case ctINF_WRITE_CAB:
case ctINF_WRITE_DISK:
//** Do quote processing and save result in pcmd
if (!processLineWithQuotes(pcmd->inf.achLine, // destination
sizeof(pcmd->inf.achLine), // dest size
pch, // source
szDF_QUOTE_SET, // quoting set
pszDFP_INF_WRITE_STRING, // field name
perr)) {
return FALSE;
}
//** Set are of INF to write
switch (ct) {
case ctINF_WRITE: pcmd->inf.inf = infFILE; break;
case ctINF_WRITE_CAB: pcmd->inf.inf = infCABINET; break;
case ctINF_WRITE_DISK: pcmd->inf.inf = infDISK; break;
default:
Assert(0);
}
//** Map to single INF write command
pcmd->ct = ctINF_WRITE;
return TRUE;
case ctNEW:
if (_stricmp(pch,pszNEW_FOLDER) == 0) {
pcmd->new.nt = newFOLDER;
}
else if (_stricmp(pch,pszNEW_CABINET) == 0) {
pcmd->new.nt = newCABINET;
}
else if (_stricmp(pch,pszNEW_DISK) == 0) {
pcmd->new.nt = newDISK;
}
else {
ErrSet(perr,pszDFPERR_UNKNOWN_KEYWORD,"%s%s",pszCMD_NEW,pch);
pcmd->new.nt = newBAD;
return FALSE;
}
return TRUE;
case ctOPTION:
pcmd->opt.of = 0; // Default is NO<option>
pcmd->opt.ofMask = 0; // No options specified
//** Construct negated string
strcpy(psess->achMsg,pszOPTION_NEG_PREFIX);
strcat(psess->achMsg,pszOPTION_EXPLICIT);
if (_stricmp(pch,pszOPTION_EXPLICIT) == 0) {
pcmd->opt.of |= optEXPLICIT; // Explicit is on
pcmd->opt.ofMask |= optEXPLICIT; // Explicit was set
}
else if (_stricmp(pch,psess->achMsg) == 0) {
pcmd->opt.of &= ~optEXPLICIT; // Explicit is off
pcmd->opt.ofMask |= optEXPLICIT; // Explicit was set
}
else {
ErrSet(perr,pszDFPERR_UNKNOWN_KEYWORD,"%s%s",pszCMD_OPTION,pch);
pcmd->new.nt = newBAD;
return FALSE;
}
return TRUE;
case ctSET:
return parseSetCommand(pcmd,psess,pch,perr);
case ctBAD: // Bad command
case ctFILE: // Caller handles file copy lines
case ctREFERENCE: // Caller handles reference lines
default:
Assert(0); // Should never get here
return FALSE;
} /* switch (ct) */
Assert(0); // Should never get here
} /* getCommand */
/*** parseSetCommand - Parse arguments to .SET command
*
* Entry:
* pcmd - Command to fill in after line is parsed
* psess - Session state
* pszArg - Start of argument string (var=value or var="value")
* perr - ERROR structure
*
* Exit-Success:
* Returns TRUE; pcmd filled in
*
* Exit-Failure:
* Returns FALSE; perr filled in with error.
*
* Syntax:
* .SET var=value
* .SET var="value"
*/
BOOL parseSetCommand(PCOMMAND pcmd, PSESSION psess, char *pszArg, PERROR perr)
{
//** Parse var=value
if (!DFPParseVarAssignment(pcmd,psess,pszArg,perr)) {
return FALSE;
}
//** Show parsed var name and value
if (psess->levelVerbose >= vbFULL) {
MsgSet(psess->achMsg,pszDFP_PARSED_SET_CMD,
"%s%s",pcmd->set.achVarName,pcmd->set.achValue);
printf("%s\n",psess->achMsg);
}
//** Success
return TRUE;
}
/*** processLineWithQuotes - Run getQuotedString over the entire line
*
* Entry:
* pszDst - Buffer to receive parsed value
* cbDst - Size of pszDst buffer
* pszSrc - String to parse
* pszQuotes - String of characters that act as quote characters
* pszFieldName - Name of field being parsed (for error message)
* perr - ERROR structure
*
* Exit-Success:
* Returns TRUE; pszDst filled in with processed string version of pszSrc
* -- quote characters are processed and removed as appropriate, and
* trailing blanks (outside of quotes) are removed.
*
* Exit-Failure:
* Returns FALSE; perr filled in with error.
* Possible errors: Incorrect delimiter format;
* String too large for pszDst buffer
*
* Syntax of pszSrc is:
* See getQuotedString for details, but essentially this function
* permits the following sorts of effects:
* " foo " => < foo >
* foo => <foo>
* " "foo => < foo>
*/
BOOL processLineWithQuotes(char *pszDst,
int cbDst,
char *pszSrc,
char *pszDelim,
char *pszFieldName,
PERROR perr)
{
int cch;
int cchValue;
char *pch;
char *pchValue;
*pszDst = '\0';
pch = pszSrc;
while (*pch) {
//** Point at end of value gathered so far
cchValue = strlen(pszDst);
pchValue = pszDst + cchValue;
//** Copy (possibly quoted) token
pch = getQuotedString(pchValue,
cbDst - cchValue,
pch,
szDF_QUOTE_SET,
pszDFP_INF_WRITE_STRING, // Name of field
perr);
//** Value More <eos>
// ^
if (pch == NULL) {
return FALSE; // Syntax error or buffer overflow
}
//** Update current position in destination and size
cchValue = strlen(pszDst);
pchValue = pszDst + cchValue;
//** Count white space, but copy only if it doesn't end string
cch = strspn(pch,szDF_WHITE_SPACE);
if (*(pch+cch) != '\0') { // Have to copy white space
while ((cch>0) && (cchValue<cbDst)) {
*pchValue++ = *pch++; // Copy character
cchValue++; // Count for buffer overflow test
cch--;
}
if (cchValue >= cbDst) {
ErrSet(perr,pszDFPERR_STRING_TOO_LONG,"%s%d",
pszDFP_INF_WRITE_STRING,cbDst-1);
return FALSE;
}
*pchValue = '\0'; // Keep string well-formed
}
else {
pch += cch; // Make sure we terminate loop
}
}
//** Success
return TRUE;
} /* processLineWithQuotes() */
/*** getQuotedString - Parse value that may be delimited
*
* Entry:
* pszDst - Buffer to receive parsed value
* cbDst - Size of pszDst buffer
* pszSrc - String to parse
* pszQuotes - String of characters that act as quote characters
* pszFieldName - Name of field being parsed (for error message)
* perr - ERROR structure
*
* Exit-Success:
* Returns pointer to first character after parsed string in pszSrc;
* pszDst filled in with null-terminated string
*
* Exit-Failure:
* Returns NULL; perr filled in with error.
* Possible errors: Incorrect delimiter format;
* String too large for pszDst buffer
*
* Notes:
* (1) If the first character of the string is in the set pszDelim,
* then that character is taken to be the *quote* character, and
* is used to find the end of the string. The leading and trailing
* quote characters are not copied to the pszDst buffer.
* EXAMPLE: <"a phrase"> becomes <a phrase>
*
* (2) If the first character is not in the set pszDelim, then whitespace
* signals the end of the string.
* EXAMPLE: <two words> becomes <two>
*
* (3) If the *quote* character is found immediately following itself
* inside the string, then it is replaced by a single copy of the
* *quote* character and copied to pszDst.
* EXAMPLE: <"He said ""Hi!"" again"> becomes <He said "Hi!" again>
*/
char *getQuotedString(char *pszDst,
int cbDst,
char *pszSrc,
char *pszQuotes,
char *pszFieldName,
PERROR perr)
{
int cch;
char chQuote; // Quote character
char *pch; // Start of piece
char *pchDst; // Current location in pszDst
char *pchEnd; // End of piece
Assert(cbDst>0);
//** Early out for empty string
if (*pszSrc == '\0') {
*pszDst = *pszSrc; // Store empty string
return pszSrc; // Success (pointer does not move)
}
//** See if first character of string is a quote
for (pch=pszQuotes; (*pch != '\0') && (*pch != pszSrc[0]); pch++) {
//** Scan through pszQuotes looking for a match
}
if (*pch == '\0') { // String is not quoted
//** Get string length
pchEnd = strpbrk(pszSrc,szDF_WHITE_SPACE);
if (pchEnd == NULL) {
cch = strlen(pszSrc);
pchEnd = pszSrc + cch;
}
else {
cch = pchEnd - pszSrc;
}
//** Make sure buffer can hold it
if (cch >= cbDst) { // Won't fit in buffer (need NUL, still)
//** Use field name, and show max string length as one less,
// since that count includes room for a NUL byte.
ErrSet(perr,pszDFPERR_STRING_TOO_LONG,"%s%d",pszFieldName,cbDst-1);
return NULL; // FAILURE
}
memcpy(pszDst,pszSrc,cch);
pszDst[cch] = '\0';
return pchEnd; // SUCCESS
}
//** Handle quoted string
chQuote = *pszSrc; // Remember the quote character
pch = pszSrc+1; // Skip over quote character
pchDst = pszDst; // Location to add chars to pszDst
//** Copy characters until end of string or quote error or buffer overflow
while ((*pch != '\0') && ((pchDst-pszDst) < cbDst)) {
if (*pch == chQuote) { // Got another quote
//** Check for "He said ""Hi"" again" case
if (*(pch+1) == chQuote) { // Need to collapse two quotes
*pchDst++ = *pch++; // Copy a single quote
pch++; // Skip the 2nd quote
}
else { // String is fine, finish and succeed
*pchDst++ = '\0'; // Terminate string
return pch+1; // Return pointer after string
}
}
else { // Normal character
*pchDst++ = *pch++; // Just copy it
}
}
//** Either we overflowed the buffer, or we missed a closing quote
if ((pchDst-pszDst) >= cbDst) {
ErrSet(perr,pszDFPERR_STRING_TOO_LONG,"%s%d",pszFieldName,cbDst-1);
}
else {
Assert(*pch == '\0');
ErrSet(perr,pszDFPERR_MISSING_QUOTE,"%c%s",chQuote,pszFieldName);
}
return NULL; // FAILURE
}
/*** ctFromCommandString - Map command string to command type
*
* Entry:
* pszCmd - String to check against command list
* perr - ERROR structure
*
* Exit-Success:
* Returns COMMANDTYPE corresponding to pszCmd.
*
* Exit-Failure:
* Returns ctBAD; perr filled in with error.
*/
COMMANDTYPE ctFromCommandString(char *pszCmd, PERROR perr)
{
int i;
//** Search for matching command
for (i=0; acsatMap[i].pszName != NULL; i++) {
if (!(_stricmp(acsatMap[i].pszName,pszCmd))) {
//** Found command
return acsatMap[i].ct; // return command type
}
}
//** Failure
ErrSet(perr,pszDFPERR_UNKNOWN_COMMAND,"%s",pszCmd);
return FALSE;
} /* ctFromCommandString() */
/*** parseReferenceLine - Parse an INF reference line
*
* Entry:
* pcmd - Command to fill in after line is parsed
* psess - Session state
* pszLine - Line to parse
* perr - ERROR structure
*
* Exit-Success:
* Returns TRUE; pcmd filled in
*
* Exit-Failure:
* Returns FALSE; perr filled in with error.
*
* Syntax:
* dstFile [/x1=y [y2=y...]]
*
* NOTES:
* (1) Quotes are allowed in file specs -- can you say Long File Names!
*/
BOOL parseReferenceLine(PCOMMAND pcmd,PSESSION psess,char *pszLine,PERROR perr)
{
int cFiles=0; // Count of file specs seen
char *pch;
BOOL runflag = FALSE;
AssertCmd(pcmd);
AssertSess(psess);
Assert(psess->ddfmode == ddfmodeRELATIONAL);
//** Set command type and default values
pcmd->ct = ctREFERENCE;
pcmd->ref.achDst[0] = '\0';
pcmd->ref.hglist = NULL; // No parameters
//** Process line
pch = pszLine;
while (*pch != '\0') {
//** Skip whitespace
pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space
if (*pch == '\0') { // End of line
break; // Skip loop so we exit
}
//** Is this a file name or a parameter?
if (*pch == chDF_MODIFIER) { // A parameter
pch = parseParameterList(&(pcmd->ref.hglist),pch,perr,&runflag);
if (runflag) {
ErrSet(perr,pszDFPERR_RUN_ON_REFERENCE);
goto done;
}
}
else { // A file
cFiles++;
if (cFiles > 1) { // Two many file names
ErrSet(perr,pszDFPERR_EXTRA_JUNK,"%s",pch);
goto done;
}
pch = getQuotedString(pcmd->ref.achDst,
sizeof(pcmd->ref.achDst),
pch,
szDF_QUOTE_SET,
pszDFPERR_DST_FILE, // Name of field
perr);
}
//** Check for error
if (pch == NULL) {
Assert(ErrIsError(perr));
goto done;
}
}
done:
//** Need a destination file
if ((cFiles == 0) && !ErrIsError(perr)) { // Don't overwrite existing error
ErrSet(perr,pszDFPERR_MISSING_DST_NAME);
return FALSE;
}
//** Clean up and exit if an error occured
if (ErrIsError(perr)) {
if (pcmd->ref.hglist) { // Destroy parameter list
GLDestroyList(pcmd->ref.hglist);
pcmd->ref.hglist = NULL;
}
return FALSE;
}
//** Show parsed dst file name
if (psess->levelVerbose >= vbFULL) {
MsgSet(psess->achMsg,pszDFP_PARSED_REF_CMD,"%s",pcmd->ref.achDst);
printf("%s\n",psess->achMsg);
}
//** Success
return TRUE;
} /* parseReferenceLine() */
/*** parseFileLine - Parse a file specification line
*
* Entry:
* pcmd - Command to fill in after line is parsed
* psess - Session state
* pszLine - Line to parse
* perr - ERROR structure
*
* Exit-Success:
* Returns TRUE; pcmd filled in
*
* Exit-Failure:
* Returns FALSE; perr filled in with error.
*
* Syntax:
* srcFile [dstFile] [/x1=y [y2=y...]]
*
* NOTES:
* (1) Quotes are allowed in file specs -- can you say Long File Names!
*/
BOOL parseFileLine(PCOMMAND pcmd, PSESSION psess, char *pszLine, PERROR perr)
{
int cFiles=0; // Count of file specs seen
char *pch;
BOOL runflag = FALSE;
AssertCmd(pcmd);
AssertSess(psess);
//** Set command type and default values
pcmd->ct = ctFILE;
pcmd->file.achSrc[0] = '\0';
pcmd->file.achDst[0] = '\0';
pcmd->file.hglist = NULL; // No parameters
pcmd->file.fRunFlag = FALSE;
//** Process line
pch = pszLine;
while (*pch != '\0') {
//** Skip whitespace
pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space
if (*pch == '\0') { // End of line
break; // Skip loop so we exit
}
//** Is this a file name or a parameter?
if (*pch == chDF_MODIFIER) { // A parameter
pch = parseParameterList(&(pcmd->file.hglist),pch,perr,&runflag);
if (runflag == TRUE) {
if (psess->fRunSeen == TRUE) {
ErrSet(perr, pszDFPERR_MULTIPLE_RUN);
goto done;
}
psess->fRunSeen = TRUE;
pcmd->file.fRunFlag = TRUE;
}
}
else { // A file
cFiles++;
if (cFiles > 2) { // Two many file names
ErrSet(perr,pszDFPERR_EXTRA_JUNK,"%s",pch);
goto done;
}
if (cFiles == 1) { // Get SOURCE file name
pch = getQuotedString(pcmd->file.achSrc,
sizeof(pcmd->file.achSrc),
pch,
szDF_QUOTE_SET,
pszDFPERR_SRC_FILE, // Name of field
perr);
}
else { // Get DESTINATION file name
pch = getQuotedString(pcmd->file.achDst,
sizeof(pcmd->file.achDst),
pch,
szDF_QUOTE_SET,
pszDFPERR_DST_FILE, // Name of field
perr);
}
}
//** Check for error
if (pch == NULL) {
Assert(ErrIsError(perr));
goto done;
}
}
done:
//** Need at least a source file
if ((cFiles == 0) && !ErrIsError(perr)) { // Don't overwrite existing error
ErrSet(perr,pszDFPERR_MISSING_SRC_NAME);
return FALSE;
}
//** Clean up and exit if an error occured
if (ErrIsError(perr)) {
if (pcmd->file.hglist) { // Destroy parameter list
GLDestroyList(pcmd->file.hglist);
pcmd->file.hglist = NULL;
}
return FALSE;
}
//** Show parsed src and dst file names
if (psess->levelVerbose >= vbFULL) {
MsgSet(psess->achMsg,pszDFP_PARSED_FILE_CMD,
"%s%s",pcmd->file.achSrc,pcmd->file.achDst);
printf("%s\n",psess->achMsg);
}
//** Success
return TRUE;
} /* parseFileLine() */
/*** parseParameterList - Parse /X=Y parameter list into an HGENLIST
*
* Entry:
* phglist - Pointer to hglist
* pch - Pointer to /X=Y string
* perr - ERROR structure
*
* Exit-Success:
* Returns updated pch, pointing to first character after parsed
* parameter; *phglist created/updated
*
* Exit-Failure;
* Returns NULL; perr filled in.
*/
char *parseParameterList(HGENLIST *phglist, char *pch, PERROR perr, BOOL *runflg)
{
char achName[cbPARM_NAME_MAX]; // Name buffer
char achValue[cbMAX_DF_LINE]; // Value buffer
int cch;
char *pchEnd;
PFILEPARM pfparm;
//** Create list if necessary
if (*phglist == NULL) {
//** Create parameter list
*phglist = GLCreateList(NULL, // No default
DestroyFileParm,
pszDFP_FILE_PARM,
perr);
if (!*phglist) {
return NULL;
}
}
//** Parse name and value
// /X = Y
// ^
Assert(*pch == chDF_MODIFIER); // Must point to '/'
pch++; // Skip over switch
//** Make sure parameter name is present
if (*pch == '\0') {
ErrSet(perr,pszDFPERR_MISSING_PARM_NAME);
return NULL;
}
//** Find end of parameter name
// /X = Y
// ^
pchEnd = strpbrk(pch,szDF_SET_CMD_DELIM); // Point after var name
// /X = Y
// ^
if (pchEnd == NULL) { // No assignment operator
// So, Check for /RUN directive
if ((strlen(pch) == strlen(pszCMD_RUN))
&& (strncmp( pch, pszCMD_RUN, strlen(pszCMD_RUN)) == 0)) {
*runflg = TRUE;
pch += strlen( pszCMD_RUN );
return( pch );
} else {
ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL);
return NULL;
}
}
//** Make sure parameter name is not too long
cch = pchEnd - pch;
if (cch >= sizeof(achName)) {
ErrSet(perr,pszDFPERR_PARM_NAME_TOO_LONG,"%d%s",sizeof(achName)-1,pch);
return NULL;
}
//** Copy parameter name, NUL terminate string
memcpy(achName,pch,cch);
achName[cch] = '\0';
//** Make sure assignment operator is present
// /X = Y
// ^
pch = pchEnd + strspn(pchEnd,szDF_WHITE_SPACE);
// Var = Value <eos>
// ^
if (*pch != chDF_EQUAL) {
ErrSet(perr,pszDFPERR_MISSING_EQUAL,"%c",chDF_EQUAL);
return NULL;
}
//** Skip to value.
// /X = Y
// ^
// NOTE: Value can be empty, we permit that! We have to distinguish
// between:
// /X1= /X2=Y
// and
// /X1=/stuff
//
pch++; // Skip over assignment operator
pchEnd = pch; // Remember where we started scanning
pch += strspn(pch,szDF_WHITE_SPACE); // Skip over white space
// /X = Y
// ^
if ((*pch == chDF_MODIFIER) && (pch > pchEnd)) {
//** Got special case of /X1= /X2=Y, value is empty
achValue[0] = '\0'; // Value is blank
}
else {
pch = getQuotedString(achValue,
sizeof(achValue),
pch,
szDF_QUOTE_SET,
pszDFP_PARM_VALUE, // Name of field
perr);
if (pch == NULL) {
return NULL;
}
}
//** Allocate parameter structure
if (!(pfparm = MemAlloc(sizeof(FILEPARM)))) {
ErrSet(perr,pszDFPERR_OUT_OF_MEMORY,"%s",pszDFP_PARM_VALUE);
return NULL;
}
if (!(pfparm->pszValue = MemStrDup(achValue))) {
ErrSet(perr,pszDFPERR_OUT_OF_MEMORY,"%s",pszDFP_PARM_VALUE);
MemFree(pfparm);
return NULL;
}
//** Add parameter to list
if (!GLAdd(*phglist, // List
achName, // parameter name
pfparm, // parameter value structure
pszDFP_FILE_PARM, // Description for error message
TRUE, // parameter name must be unique
perr)) {
MemFree(pfparm->pszValue);
MemFree(pfparm);
return NULL;
}
//** Set signature after we get it successfully on the list
SetAssertSignature(pfparm,sigFILEPARM);
//** Return updated parse position
return pch;
} /* parseParameterList() */
/*** substituteVariables - Perform variable substitution; strip comments
*
* Entry:
* pszDst - Buffer to receive substituted version of pszSrc
* cbDst - Size of pszDst
* pszSrc - String to process
* hvlist - Variable list
* perr - ERROR structure
*
* Exit-Success:
* Returns TRUE; pszDst filled in with substituted form
*
* Exit-Failure:
* Returns FALSE; perr filled in with error
*
* Substitution rules:
* (1) Only one level of substitution is performed
* (2) Variable must be defined in hvlist
* (3) "%%" is replaced by "%", if the first % is not the end of
* of a variable substitution.
* (4) Variable substitution is not performed in quoted strings
* (5) End-of-line comments are removed
* (6) Any trailing white space on the line is removed
*/
BOOL substituteVariables(char *pszDst,
int cbDst,
char *pszSrc,
HVARLIST hvlist,
PERROR perr)
{
char achVarName[cbVAR_NAME_MAX];
int cch;
char chQuote;
HVARIABLE hvar;
char *pch;
char *pszAfterVar; // Points to first char after var subst.
char *pszDstSave; // Original pszDst value
char *pszVarNameStart; // Points to first % in var substitution
pszDstSave = pszDst;
while (*pszSrc != '\0') {
switch (*pszSrc) {
case chDF_QUOTE1:
case chDF_QUOTE2:
/*
* Copy everything up to closing quote, taking care to handle
* the special case of embedded quotes compatibly with
* getQuotedString! The main issue is to make sure we
* correctly determine the end of the quoted string.
* NOTE: We don't check for quote mismatches here -- we
* just avoid doing variable substituion of comment
* character recognition!
*/
chQuote = *pszSrc; // Remember the quote character
pch = pszSrc + 1; // Skip over first quote
//** Find end of quoted string
while (*pch != '\0') {
if (*pch == chQuote) { // Got a quote
pch++; // Skip over it
if (*pch != chQuote) { // Marks the end of quoted str
break; // Exit loop and copy string
}
//** If we didn't break out above, it was because
// we had an embedded quote (""). The pch++ above
// skipped over the first quote, and the pch++
// below skips over the second quote. So, no need
// for any special code!
}
pch++; // Examine next character
}
//** At this point, we've either found the end of the
// quoted string, or the end of the source buffer.
// we don't care which, as we don't check for errors
// in quoted strings. So we just copy what we found
// and keep going.
if (!copyBounded(&pszDst,&cbDst,&pszSrc,pch-pszSrc)) {
goto error_copying;
}
break;
case chDF_COMMENT: // Toss rest of line
goto done; // Finish string and return
case chDF_SUBSTITUTE:
pszVarNameStart = pszSrc; // Save start for error messgages
pszSrc++; // Skip first %
if (*pszSrc == chDF_SUBSTITUTE) { // Have "%%"
//** Collapse two %% into one %
if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) {
goto error_copying;
}
}
else {
//** Attempt variable substitution
pch = strchr(pszSrc,chDF_SUBSTITUTE); // Finding ending %
if (!pch) { // No terminating %
ErrSet(perr,pszDFPERR_MISSING_SUBST,"%c%s",
chDF_SUBSTITUTE,pszVarNameStart);
return FALSE;
}
pszAfterVar = pch+1; // Point after ending %
//** Extract variable name
cch = pch - pszSrc; // Length of variable name
if (cch >= cbVAR_NAME_MAX) {
ErrSet(perr,pszDFPERR_VAR_NAME_TOO_LONG,"%d%s",
cbVAR_NAME_MAX-1,pszVarNameStart);
return FALSE;
}
memcpy(achVarName,pszSrc,cch); // Copy it
achVarName[cch] = '\0'; // Terminate it
//** Look up variable
if (!(hvar = VarFind(hvlist,achVarName,perr))) {
ErrSet(perr,pszDFPERR_VAR_UNDEFINED,"%s",
pszVarNameStart);
return FALSE;
}
//** Substitute variable
pch = VarGetString(hvar); // Get value
if (!copyBounded(&pszDst,&cbDst,&pch,0)) {
ErrSet(perr,pszDFPERR_VAR_SUBST_OVERFLOW,"%s",
pszVarNameStart);
return FALSE;
}
//** copyBounded appended the NULL byte, but we need to
// remove that so that any subsequent characters on
// the line get tacked on!
pszDst--; // Back up over NULL byte
cbDst++; // Don't count NULL byte
//** Skip over variable name
pszSrc = pszAfterVar;
}
break;
default:
//** Just copy the character
if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) {
goto error_copying;
}
break;
} /* switch */
} /* while */
done:
//** Terminate processed string
if (cbDst == 0) { // No room for terminator
goto error_copying;
}
*pszDst++ = '\0'; // Terminate string
//** Trim off any trailing white space
pch = pszDstSave; // Start at front
while (pch && *pch) { // Process entire string
//** Skip over non-white space
pch = strpbrk(pch,szDF_WHITE_SPACE);
if (pch != NULL) { // Not at the end of the string
//** Skip over white space
cch = strspn(pch,szDF_WHITE_SPACE);
if (*(pch+cch) == '\0') {
//** We're at the end and we have white space
*pch = '\0'; // Trim off the white space
}
else {
pch += cch; // Advance to next non-white space
}
}
}
//** Success
return TRUE;
error_copying:
ErrSet(perr,pszDFPERR_COPYING_OVERFLOW,"%s",pszSrc);
return FALSE;
} /* substituteVariables */
/*** BOOLfromPSZ - Get boolean from string value
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
BOOL BOOLfromPSZ(char *psz, PERROR perr)
{
if (!strcmp(psz,"0") ||
!_stricmp(psz,pszVALUE_NO) ||
!_stricmp(psz,pszVALUE_OFF) ||
!_stricmp(psz,pszVALUE_FALSE)) {
return FALSE;
}
else if (!strcmp(psz,"1") ||
!_stricmp(psz,pszVALUE_YES) ||
!_stricmp(psz,pszVALUE_ON) ||
!_stricmp(psz,pszVALUE_TRUE)) {
return TRUE;
}
else {
ErrSet(perr,pszDFPERR_INVALID_BOOL,"%s",psz);
return -1;
}
} /* BOOLfromPSZ() */
/*** ChecksumWidthFromPSZ - Get Checksum Width from a string
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
int ChecksumWidthFromPSZ(char *psz, PERROR perr)
{
int level;
int levelLo;
int levelHi;
level = atoi(psz);
levelLo = atoi(pszCW_LOWEST);
levelHi = atoi(pszCW_HIGHEST);
//** Check range
if ((levelLo <= level) && (level <= levelHi)) {
return level;
}
//** Level not in valid range
ErrSet(perr,pszDFPERR_INVALID_CSUM_WIDTH,"%s%s%s",
pszCW_LOWEST,pszCW_HIGHEST,psz);
return -1;
} /* ChecksumWidthFromPSZ() */
/*** CompTypeFromPSZ - Get Compression Type from a string
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
int CompTypeFromPSZ(char *psz, PERROR perr)
{
if (!_stricmp(psz,pszCT_MSZIP)) {
return tcompTYPE_MSZIP;
}
else if (!_stricmp(psz,pszCT_QUANTUM)) {
#ifdef BIT16
ErrSet(perr,pszDFPERR_NO_16BIT_QUANTUM);
return -1;
#else // !BIT16
return tcompTYPE_QUANTUM;
#endif // !BIT16
}
else {
ErrSet(perr,pszDFPERR_INVALID_COMP_TYPE,"%s",psz);
return -1;
}
} /* CompTypeFromPSZ() */
/*** CompLevelFromPSZ - Get Compression Level from a string
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
int CompLevelFromPSZ(char *psz, PERROR perr)
{
int level;
int levelLo;
int levelHi;
level = atoi(psz);
levelLo = atoi(pszCL_LOWEST);
levelHi = atoi(pszCL_HIGHEST);
//** Check range
if ((levelLo <= level) && (level <= levelHi)) {
return level;
}
//** Level not in valid range
ErrSet(perr,pszDFPERR_INVALID_COMP_LEVEL,"%s%s%s",
pszCL_LOWEST,pszCL_HIGHEST,psz);
return -1;
} /* CompLevelFromPSZ() */
/*** roundUpToPowerOfTwo - Round up a number to a power of two
*
* Entry:
* x - Number to round up
*
* Exit:
* Returns x rounded up to a power of two:
* x result
* ----- ------
* 0 0 (???)
* 1 1 (2^0)
* 2 2 (2^1)
* 3 4 (2^2)
* 4 4 (2^2)
* ... ....
* 127 128 (2^7)
* 128 128 (2^7)
* 129 256 (2^8)
* ... ...
*/
long roundUpToPowerOfTwo(long x)
{
int ibit;
long xSave=x;
long mask;
//** Check if already a power of 2; We use the trick that clears the
// lowest order 1 bit. If the result is zero, then we know we
// already have a power of 2, since only one 1 bit was set.
if (0 == (x&(x-1))) {
return x;
}
//** Get the index (1-based) of the most significant 1 bit
for (ibit=0; x; x>>=1, ibit++)
;
//** Round up and return result
Assert(ibit >= 2); // First test ensures this
mask = (1 << ibit) - 1;
return (xSave + mask) & ~mask; // Round up to a power of 2
} /* roundUpToPowerOfTwo() */
/*** CompMemoryFromPSZ - Get Compression Memory from a string
*
* NOTE: See dfparse.h for entry/exit conditions.
*/
int CompMemoryFromPSZ(char *psz, PERROR perr)
{
long memory;
long memoryLo; // Lowest 2^n exponent allowed
long memoryHi; // Highest 2^n exponent allowed
long cbLo; // Lowest byte count allowed
long cbHi; // Highest byte count allowed
int cbits;
memory = atoi(psz);
memoryLo = atoi(pszCM_LOWEST);
memoryHi = atoi(pszCM_HIGHEST);
cbLo = 1L << memoryLo;
cbHi = 1L << memoryHi;
//** Check 2^n exponent range
if ((memoryLo <= memory) && (memory <= memoryHi)) {
return (int)memory;
}
if (memory < cbLo) {
//** Assume attempted to specify exponent that was too high
ErrSet(perr,pszDFPERR_INVALID_COMP_MEM,"%s%s%s",
pszCM_LOWEST,pszCM_HIGHEST,psz);
return -1;
}
//** Check byte count range
memory = roundUpToPowerOfTwo(memory); // Make it a power of two
if ((cbLo <= memory) && (memory <= cbHi)) {
//** Take log base 2
for (cbits=0; memory>>=1; cbits++)
;
Assert((memoryLo<=cbits) && (cbits<=memoryHi));
return cbits;
}
//** Out of range
ErrSet(perr,pszDFPERR_INVALID_COMP_MEM,"%d%d%s",
cbLo,cbHi,psz);
return -1;
} /* CompMemoryFromPSZ() */
/*** fnvcvBool - validate boolean value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvBool)
{
BOOL f;
f = BOOLfromPSZ(pszValue,perr);
if (f == -1) {
return FALSE;
}
if (f == FALSE) {
strcpy(pszNewValue,"0");
}
else {
Assert(f == TRUE);
strcpy(pszNewValue,"1");
}
return TRUE;
}
/*** fnvcvCabName - Validate CabinetName template
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvCabName)
{
//BUGBUG 12-Aug-1993 bens Validate CabinetName value
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** fnvcvChecksumWidth - validate a ChecksumWidth value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvChecksumWidth)
{
if (-1 == ChecksumWidthFromPSZ(pszValue,perr)) {
return FALSE;
}
strcpy(pszNewValue,pszValue);
return TRUE;
} /* fnvcvChecksumWidth() */
/*** fnvcvClusterSize - validate a ClusterSize value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*
* We interpret special strings here that correspond to known disk
* sizes.
*/
FNVCVALIDATE(fnvcvClusterSize)
{
int i;
i = IMDSfromPSZ(pszValue); // See if special value
if (i != -1) { // Got a special value
strcpy(pszNewValue,amds[i].pszClusterSize);
return TRUE;
}
else { // Validate long value
return fnvcvLong(hvlist,pszName,pszValue,pszNewValue,perr);
}
}
/*** fnvcvCompType - validate a CompressionType value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvCompType)
{
if (-1 == CompTypeFromPSZ(pszValue,perr)) {
return FALSE;
}
strcpy(pszNewValue,pszValue);
return TRUE;
} /* fnvcvCompType() */
/*** fnvcvCompLevel - validate a CompressionLevel value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvCompLevel)
{
if (-1 == CompLevelFromPSZ(pszValue,perr)) {
return FALSE;
}
strcpy(pszNewValue,pszValue);
return TRUE;
} /* fnvcvCompLevel() */
/*** fnvcvCompMemory - validate a CompressionMemory value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvCompMemory)
{
if (-1 == CompMemoryFromPSZ(pszValue,perr)) {
return FALSE;
}
strcpy(pszNewValue,pszValue);
return TRUE;
} /* fnvcvCompMemory() */
/*** fnvcvDateFmt - Validate InfDateFormat value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvDateFmt)
{
if (!_stricmp(pszValue,pszIDF_MMDDYY) ||
!_stricmp(pszValue,pszIDF_YYYYMMDD) ) {
//** Valid date format
strcpy(pszNewValue,pszValue);
return TRUE;
}
//** Unsupported date format
return FALSE;
} /* fnvcvDateFmt() */
/*** fnvcvDirDest - Validate DestinationDir value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvDirDest)
{
//BUGBUG 12-Aug-1993 bens Validate DestinationDir value
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** fnvcvDirSrc - Validate SourceDir value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvDirSrc)
{
//BUGBUG 12-Aug-1993 bens Validate SourceDir value
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** fnvcvFile - Validate a file name value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvFile)
{
//BUGBUG 08-Feb-1994 bens Validate file name
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** fnvcvFileChar - Validate a file name character
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvFileChar)
{
//BUGBUG 08-Feb-1994 bens Validate file name character
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** fnvcvLong - validate long value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvLong)
{
char *psz;
for (psz=pszValue; *psz && isdigit(*psz); psz++) {
; //** Make sure entire value is digits
}
if (*psz != '\0') {
ErrSet(perr,pszDFPERR_NOT_A_NUMBER,"%s%s",pszName,pszValue);
return FALSE;
}
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** fnvcvMaxDiskFileCount - validate MaxDiskFileCount value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*
* We interpret special strings here that correspond to known disk
* sizes.
*/
FNVCVALIDATE(fnvcvMaxDiskFileCount)
{
int i;
i = IMDSfromPSZ(pszValue); // See if special value
if (i != -1) { // Got a special value
strcpy(pszNewValue,amds[i].pszFilesInRoot);
return TRUE;
}
else { // Validate long value
return fnvcvLong(hvlist,pszName,pszValue,pszNewValue,perr);
}
}
/*** fnvcvMaxDiskSize - validate a MaxDiskSize value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvMaxDiskSize)
{
int i;
i = IMDSfromPSZ(pszValue); // See if special value
if (i != -1) { // Got a special value
strcpy(pszNewValue,amds[i].pszDiskSize);
return TRUE;
}
else { // Validate long value
return fnvcvLong(hvlist,pszName,pszValue,pszNewValue,perr);
}
}
/*** fnvcvSectionOrder - validate InfSectionOrder value
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvSectionOrder)
{
int bits;
int bitSection;
char ch;
char *psz;
//** Check length
if (strlen(pszValue) > 3) {
ErrSet(perr,pszDFPERR_BAD_SECTION_ORDER,"%s",pszValue);
return FALSE;
}
//** Make sure character appears at most once
bits = 0; // Set 1 bit for each character
for (psz=pszValue; *psz; psz++) {
ch = toupper(*psz);
switch (ch) {
case pszISO_DISK: bitSection = 1; break;
case pszISO_CABINET: bitSection = 2; break;
case pszISO_FILE: bitSection = 4; break;
default:
ErrSet(perr,pszDFPERR_BAD_SECTION_ORDER2,"%c%s",*psz,pszValue);
return FALSE;
}
//** Make sure character is not repeated
if (bits & bitSection) {
ErrSet(perr,pszDFPERR_BAD_SECTION_ORDER3,"%c%s",*psz,pszValue);
return FALSE;
}
bits |= bitSection; // Record this section
}
//** Value is OK
strcpy(pszNewValue,pszValue);
return TRUE;
} /* fnvcvSectionOrder() */
/*** fnvcvWildFile - validate filename with possibly single "*" char
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvWildFile)
{
//BUGBUG 12-Aug-1993 bens Validate Wild Filename
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** fnvcvWildPath - validate path with possibly single "*" char
*
* NOTE: See variable.h:FNVCVALIDATE for entry/exit conditions.
*/
FNVCVALIDATE(fnvcvWildPath)
{
//BUGBUG 12-Aug-1993 bens Validate Wild Path
strcpy(pszNewValue,pszValue);
return TRUE;
}
/*** IMDSfromPSZ - Look for special disk designator in amds[]
*
* Entry:
* pszValue - Value to compare against amds[].pszDiskSize values
*
* Exit-Success:
* Returns index in amds[] of entry that matches pszValue;
*
* Exit-Failure:
* Returns -1, pszValue not in amds[]
*/
int IMDSfromPSZ(char *pszValue)
{
int i;
for (i=0;
(i<nmds) && // More special values to check
_stricmp(pszValue,amds[i].pszSpecial) && // String not special
(atol(pszValue) != atol(amds[i].pszDiskSize)); // Value not special
i++) {
; // Check for special value
}
if (i<nmds) { // Got a special value
return i;
}
else {
return -1;
}
} /* IMDSfromPSZ() */