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