/*++ Copyright (c) 1996-1997 Microsoft Corporation Module Name: ppdentry.c Abstract: Functions for parsing syntactical elements of a PPD file Environment: PostScript driver, PPD parser Revision History: 08/20/96 -davidx- Common coding style for NT 5.0 drivers. 03/26/96 -davidx- Created it. --*/ #include "lib.h" #include "ppd.h" #include "ppdparse.h" // // Forward declaration of local functions // PPDERROR IParseKeyword(PPARSERDATA); PPDERROR IParseValue(PPARSERDATA); PPDERROR IParseField(PFILEOBJ, PBUFOBJ, BYTE); PPDERROR IParseEntry( PPARSERDATA pParserData ) /*++ Routine Description: Parse one entry from a PPD file Arguments: pParserData - Points to parser data structure Return Value: Status code --*/ { PPDERROR iStatus; INT iChar; PFILEOBJ pFile = pParserData->pFile; // // Clear values from previous entry // CLEAR_BUFFER(&pParserData->Keyword); CLEAR_BUFFER(&pParserData->Option); CLEAR_BUFFER(&pParserData->Xlation); CLEAR_BUFFER(&pParserData->Value); pParserData->dwValueType = VALUETYPE_NONE; // // Parse the keyword field and skip over trailing white spaces // if ((iStatus = IParseKeyword(pParserData)) != PPDERR_NONE) return iStatus; // // Look at the first non-space character after the keyword field // VSkipSpace(pFile); if ((iChar = IGetNextChar(pFile)) == EOF_CHAR) return PPDERR_EOF; if (IS_NEWLINE(iChar)) return PPDERR_NONE; if (iChar != SEPARATOR_CHAR) { // // Parse the option field and skip over trailing white spaces // ASSERT(iChar != EOF_CHAR); VUngetChar(pFile); if ((iStatus = IParseField(pFile, &pParserData->Option, KEYWORD_MASK)) != PPDERR_NONE) return iStatus; VSkipSpace(pFile); // // Look at the first non-space character after the option field // if ((iChar = IGetNextChar(pFile)) == XLATION_CHAR) { // // Parse the translation string field // if ((iStatus = IParseField(pFile, &pParserData->Xlation, XLATION_MASK)) != PPDERR_NONE) return iStatus; iChar = IGetNextChar(pFile); } if (iChar != SEPARATOR_CHAR) return ISyntaxError(pFile, "Missing ':'"); } // // Parse the value field and interpret the entry if it's valid // if ((iStatus = IParseValue(pParserData)) == PPDERR_NONE) { // // Take care of any embedded hexdecimals in the translation string // if (! IS_BUFFER_EMPTY(&pParserData->Xlation) && ! BConvertHexString(&pParserData->Xlation)) { return ISyntaxError(pFile, "Invalid hexdecimals in the translation string"); } // // Interpret the current entry // iStatus = IInterpretEntry(pParserData); } return iStatus; } PPDERROR IParseKeyword( PPARSERDATA pParserData ) /*++ Routine Description: Parse the keyword field of a PPD file entry Arguments: pParserData - Points to parser data structure Return Value: Status code --*/ { PFILEOBJ pFile = pParserData->pFile; INT iChar; while (TRUE) { // // Get the first character of a line // if ((iChar = IGetNextChar(pFile)) == EOF_CHAR) return PPDERR_EOF; // // Ignore empty lines // if (IS_NEWLINE(iChar)) continue; if (IS_SPACE(iChar)) { VSkipSpace(pFile); if ((iChar = IGetNextChar(pFile)) == EOF_CHAR) return PPDERR_EOF; if (IS_NEWLINE(iChar)) continue; return ISyntaxError(pFile, "Missing '*'"); } // // If the line is not empty, the first character must be the keyword character // if (! IS_KEYWORD_CHAR(iChar)) return ISyntaxError(pFile, "Missing '*'"); // // If the second character is not %, then the line is a normal entry. // Otherwise, the line is a comment. // if ((iChar = IGetNextChar(pFile)) == EOF_CHAR) return PPDERR_EOF; if (!IS_NEWLINE(iChar) && iChar != COMMENT_CHAR) { VUngetChar(pFile); break; } VSkipLine(pFile); } return IParseField(pFile, &pParserData->Keyword, KEYWORD_MASK); } PPDERROR IParseValue( PPARSERDATA pParserData ) /*++ Routine Description: Parse the value field of a PPD file entry Arguments: pParserData - Points to parser data structure Return Value: Status code --*/ { PPDERROR iStatus; INT iChar; PBUFOBJ pBufObj = &pParserData->Value; PFILEOBJ pFile = pParserData->pFile; // // The value is either a StringValue, a SymbolValue, or a QuotedValue // depending on the first non-space character // VSkipSpace(pFile); if ((iChar = IGetNextChar(pFile)) == EOF_CHAR) return PPDERR_EOF; if (iChar == QUOTE_CHAR) { // // The value is a quoted string // pParserData->dwValueType = VALUETYPE_QUOTED; if ((iStatus = IParseField(pFile, pBufObj, QUOTED_MASK)) != PPDERR_NONE) return iStatus; // // Read the closing quote character // if ((iChar = IGetNextChar(pFile)) != QUOTE_CHAR) return ISyntaxError(pFile, "Unbalanced '\"'"); } else if (iChar == SYMBOL_CHAR) { // // The value is a symbol reference // pParserData->dwValueType = VALUETYPE_SYMBOL; ADD_CHAR_TO_BUFFER(pBufObj, iChar); if ((iStatus = IParseField(pFile, pBufObj, KEYWORD_MASK)) != PPDERR_NONE) return iStatus; } else { PBYTE pubEnd; // // The value is a string // pParserData->dwValueType = VALUETYPE_STRING; VUngetChar(pFile); if ((iStatus = IParseField(pFile, pBufObj, STRING_MASK)) != PPDERR_NONE) return iStatus; // // Ignore any trailing spaces // ASSERT(pBufObj->dwSize > 0); pubEnd = pBufObj->pbuf + (pBufObj->dwSize - 1); while (IS_SPACE(*pubEnd)) *pubEnd-- = NUL; pBufObj->dwSize= (DWORD)(pubEnd - pBufObj->pbuf) + 1; ASSERT(pBufObj->dwSize > 0); } // // Ignore extra characters after the entry value // VSkipSpace(pFile); iChar = IGetNextChar(pFile); if (! IS_NEWLINE(iChar)) { if (iChar != XLATION_CHAR) { TERSE(("%ws: Extra chars at the end of line %d\n", pFile->ptstrFileName, pFile->iLineNumber)); } VSkipLine(pFile); } return PPDERR_NONE; } PPDERROR IParseField( PFILEOBJ pFile, PBUFOBJ pBufObj, BYTE ubMask ) /*++ Routine Description: Parse one field of a PPD file entry Arguments: pFile - Specifies the input file object pBufObj - Specifies the buffer for storing the field value ubMask - Mask to limit the set of allowable characters Return Value: Status code --*/ { PPDERROR iStatus; INT iChar; while ((iChar = IGetNextChar(pFile)) != EOF_CHAR) { if (! IS_MASKED_CHAR(iChar, ubMask)) { // // Encountered a not-allowed character // if (IS_BUFFER_EMPTY(pBufObj) && !(ubMask & QUOTED_MASK)) return ISyntaxError(pFile, "Empty field"); // // Always put a null byte at the end // pBufObj->pbuf[pBufObj->dwSize] = 0; VUngetChar(pFile); return PPDERR_NONE; } else { // // If we're parsing a quoted string and we encountered a line // starting with the keyword character, then we'll assume // the closing quote is missing. Just give a warning and continue. // if ((ubMask & QUOTED_MASK) && IS_KEYWORD_CHAR(iChar) && !IS_BUFFER_EMPTY(pBufObj) && IS_NEWLINE(pBufObj->pbuf[pBufObj->dwSize - 1])) { (VOID) ISyntaxError(pFile, "Expecting '\"'"); } // // Grow the buffer if it's full. If we're not allowed to // grow it, then return a syntax error. // if (IS_BUFFER_FULL(pBufObj)) { if (ubMask & (STRING_MASK|QUOTED_MASK)) { if ((iStatus = IGrowValueBuffer(pBufObj)) != PPDERR_NONE) return iStatus; } else { return ISyntaxError(pFile, "Field too long"); } } ADD_CHAR_TO_BUFFER(pBufObj, iChar); } } return PPDERR_EOF; } BOOL BConvertHexString( PBUFOBJ pBufObj ) /*++ Routine Description: Convert embedded hexdecimal strings into binary data Arguments: pBufObj - Specifies the buffer object to be converted Return Value: TRUE if everything is ok FALSE if the embedded hexdecimal string is ill-formed --*/ #define HexDigitValue(c) \ (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : ((c) - 'a' + 10)) { PBYTE pubSrc, pubDest; DWORD dwSize; DWORD dwHexMode = 0; pubSrc = pubDest = pBufObj->pbuf; dwSize = pBufObj->dwSize; while (dwSize--) { if (dwHexMode) { // // We're currently inside a hex string: // switch to normal mode if '>' is encountered // otherwise, only valid hex digits, newline, and spaces are allowed // if (IS_HEX_DIGIT(*pubSrc)) { // // If we're currently on odd hex digit, save the hex digit value // in the upper nibble of the destination byte. // If we're on even hex digit, save the hex digit value in the // lower nibble of the destination byte. If the destination byte // is zero and NUL is not allowed, then return with error. // if (dwHexMode & 1) *pubDest = HexDigitValue(*pubSrc) << 4; else *pubDest++ |= HexDigitValue(*pubSrc); dwHexMode++; } else if (*pubSrc == '>') { if ((dwHexMode & 1) == 0) { TERSE(("Odd number of hexdecimal digits\n")); return FALSE; } dwHexMode = 0; } else if (!IS_SPACE(*pubSrc) && !IS_NEWLINE(*pubSrc)) { TERSE(("Invalid hexdecimal digit\n")); return FALSE; } } else { // // We're not currently inside a hex string: // switch to hex mode if '<' is encountered // otherwise, simply copy the source byte to the destination // if (*pubSrc == '<') dwHexMode = 1; else *pubDest++ = *pubSrc; } pubSrc++; } if (dwHexMode) { TERSE(("Missing '>' in hexdecimal string\n")); return FALSE; } // // Modified the buffer size if it's changed // *pubDest = 0; pBufObj->dwSize = (DWORD)(pubDest - pBufObj->pbuf); return TRUE; } PPDERROR ISyntaxErrorMessage( PFILEOBJ pFile, PSTR pstrMsg ) /*++ Routine Description: Display syntax error message Arguments: pFile - Specifies the input file object pstrMsg - Indicate the reason for the syntax error Return Value: PPDERR_SYNTAX --*/ { // // Display an error message // TERSE(("%ws: %s on line %d\n", pFile->ptstrFileName, pstrMsg, pFile->iLineNumber)); // // Skip any remaining characters on the current line // VSkipLine(pFile); return PPDERR_SYNTAX; }