You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3834 lines
88 KiB
3834 lines
88 KiB
/*++
|
|
|
|
Copyright (c) 1996-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ppdkwd.c
|
|
|
|
Abstract:
|
|
|
|
Functions for interpreting the semantics elements of a PPD file
|
|
|
|
Environment:
|
|
|
|
PostScript driver, PPD parser
|
|
|
|
Revision History:
|
|
|
|
09/30/96 -davidx-
|
|
Cleaner handling of ManualFeed and AutoSelect feature.
|
|
|
|
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"
|
|
#include <math.h>
|
|
|
|
//
|
|
// Data structure for representing entries in a keyword table
|
|
//
|
|
|
|
typedef PPDERROR (*KWDPROC)(PPARSERDATA);
|
|
|
|
typedef struct _KWDENTRY {
|
|
|
|
PCSTR pstrKeyword; // keyword name
|
|
KWDPROC pfnProc; // keyword handler function
|
|
DWORD dwFlags; // misc. flag bits
|
|
|
|
} KWDENTRY, *PKWDENTRY;
|
|
|
|
//
|
|
// Constants for KWDENTRY.flags field. The low order byte is used to indicate value types.
|
|
//
|
|
|
|
#define REQ_OPTION 0x0100
|
|
#define ALLOW_MULTI 0x0200
|
|
#define ALLOW_HEX 0x0400
|
|
#define DEFAULT_PROC 0x0800
|
|
|
|
#define INVOCA_VALUE (VALUETYPE_QUOTED | VALUETYPE_SYMBOL)
|
|
#define QUOTED_VALUE (VALUETYPE_QUOTED | ALLOW_HEX)
|
|
#define QUOTED_NOHEX VALUETYPE_QUOTED
|
|
#define STRING_VALUE VALUETYPE_STRING
|
|
|
|
#define GENERIC_ENTRY(featureId) ((featureId) << 16)
|
|
|
|
//
|
|
// Give a warning when there are duplicate entries for the same option
|
|
//
|
|
|
|
#define WARN_DUPLICATE() \
|
|
TERSE(("%ws: Duplicate entries of '*%s %s' on line %d\n", \
|
|
pParserData->pFile->ptstrFileName, \
|
|
pParserData->achKeyword, \
|
|
pParserData->achOption, \
|
|
pParserData->pFile->iLineNumber))
|
|
|
|
//
|
|
// Default keyword prefix string
|
|
//
|
|
|
|
#define HAS_DEFAULT_PREFIX(pstr) \
|
|
(strncmp((pstr), gstrDefault, strlen(gstrDefault)) == EQUAL_STRING)
|
|
|
|
//
|
|
// Determine whether a string starts with JCL prefix
|
|
//
|
|
|
|
#define HAS_JCL_PREFIX(pstr) (strncmp((pstr), "JCL", 3) == EQUAL_STRING)
|
|
|
|
//
|
|
// Forward declaration of local functions
|
|
//
|
|
|
|
PPDERROR IVerifyValueType(PPARSERDATA, DWORD);
|
|
PPDERROR IGenericOptionProc(PPARSERDATA, PFEATUREOBJ);
|
|
PPDERROR IGenericDefaultProc(PPARSERDATA, PFEATUREOBJ);
|
|
PPDERROR IGenericQueryProc(PPARSERDATA, PFEATUREOBJ);
|
|
PFEATUREOBJ PCreateFeatureItem(PPARSERDATA, DWORD);
|
|
PKWDENTRY PSearchKeywordTable(PPARSERDATA, PSTR, PINT);
|
|
|
|
|
|
|
|
PPDERROR
|
|
IInterpretEntry(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Interpret an entry parsed from a printer description file
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR pstrKeyword, pstrRealKeyword;
|
|
PPDERROR iStatus;
|
|
PKWDENTRY pKwdEntry;
|
|
INT iIndex;
|
|
BOOL bQuery, bDefault;
|
|
|
|
//
|
|
// Get a pointer to the keyword string and look for
|
|
// *? and *Default prefix in front of the keyword.
|
|
//
|
|
|
|
pstrRealKeyword = pstrKeyword = pParserData->achKeyword;
|
|
bQuery = bDefault = FALSE;
|
|
|
|
// NOTE: We don't have any use for query entries so don't parse them here.
|
|
// This helps us somewhat to preserve the feature index from NT4.
|
|
|
|
if (FALSE && *pstrKeyword == QUERY_CHAR)
|
|
{
|
|
bQuery = TRUE;
|
|
pstrRealKeyword++;
|
|
}
|
|
else if (HAS_DEFAULT_PREFIX(pstrKeyword))
|
|
{
|
|
bDefault = TRUE;
|
|
pstrRealKeyword += strlen(gstrDefault);
|
|
}
|
|
|
|
//
|
|
// Set up a convenient pointer to the entry value
|
|
//
|
|
|
|
pParserData->pstrValue = (PSTR) pParserData->Value.pbuf;
|
|
|
|
//
|
|
// If we're within an OpenUI/CloseUI pair and the keyword
|
|
// in the current entry matches what's in OpenUI, then we
|
|
// will handle the current entry using a generic procedure.
|
|
//
|
|
|
|
if (pParserData->pOpenFeature &&
|
|
pParserData->pOpenFeature->dwFeatureID == GID_UNKNOWN &&
|
|
strcmp(pstrRealKeyword, pParserData->pOpenFeature->pstrName) == EQUAL_STRING)
|
|
{
|
|
pKwdEntry = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Find out if the keyword has built-in support
|
|
//
|
|
|
|
pKwdEntry = PSearchKeywordTable(pParserData, pstrRealKeyword, &iIndex);
|
|
|
|
//
|
|
// For *Default keywords, if we failed to find the keyword without
|
|
// the prefix, try it again with the prefix.
|
|
//
|
|
|
|
if (bDefault &&
|
|
(pKwdEntry == NULL || pKwdEntry->pfnProc != NULL) &&
|
|
(pKwdEntry = PSearchKeywordTable(pParserData, pstrKeyword, &iIndex)))
|
|
{
|
|
bDefault = FALSE;
|
|
}
|
|
|
|
//
|
|
// Ignore unsupported keywords
|
|
//
|
|
|
|
if ((pKwdEntry == NULL) ||
|
|
((bQuery || bDefault) && (pKwdEntry->pfnProc != NULL)))
|
|
{
|
|
VERBOSE(("Keyword not supported: *%s\n", pstrKeyword));
|
|
return PPDERR_NONE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine if the entry should be handle by the generic procedure
|
|
//
|
|
|
|
if (pKwdEntry == NULL || pKwdEntry->pfnProc == NULL)
|
|
{
|
|
PFEATUREOBJ pFeature;
|
|
DWORD dwValueType;
|
|
PPDERROR (*pfnGenericProc)(PPARSERDATA, PFEATUREOBJ);
|
|
|
|
//
|
|
// Make sure the value type matches what's expected
|
|
//
|
|
|
|
if (bQuery)
|
|
{
|
|
pfnGenericProc = IGenericQueryProc;
|
|
dwValueType = INVOCA_VALUE;
|
|
}
|
|
else if (bDefault)
|
|
{
|
|
pfnGenericProc = IGenericDefaultProc;
|
|
dwValueType = STRING_VALUE;
|
|
}
|
|
else
|
|
{
|
|
pfnGenericProc = IGenericOptionProc;
|
|
dwValueType = INVOCA_VALUE | REQ_OPTION;
|
|
}
|
|
|
|
if ((iStatus = IVerifyValueType(pParserData, dwValueType)) != PPDERR_NONE)
|
|
return iStatus;
|
|
|
|
//
|
|
// Call the appropriate generic procedure
|
|
//
|
|
|
|
pFeature = (pKwdEntry == NULL) ?
|
|
pParserData->pOpenFeature :
|
|
PCreateFeatureItem(pParserData, HIWORD(pKwdEntry->dwFlags));
|
|
|
|
return pfnGenericProc(pParserData, pFeature);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Screen out duplicate keyword entries
|
|
//
|
|
|
|
if (! (pKwdEntry->dwFlags & (ALLOW_MULTI|REQ_OPTION)))
|
|
{
|
|
if (pParserData->pubKeywordCounts[iIndex])
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
pParserData->pubKeywordCounts[iIndex]++;
|
|
}
|
|
|
|
//
|
|
// Make sure the value type matches what's expected
|
|
// Take care of embedded hexdecimal strings if necessary
|
|
//
|
|
|
|
if ((iStatus = IVerifyValueType(pParserData, pKwdEntry->dwFlags)) != PPDERR_NONE)
|
|
return iStatus;
|
|
|
|
//
|
|
// Invoke the specific procedure to handle built-in keywords
|
|
//
|
|
|
|
return (pKwdEntry->pfnProc)(pParserData);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IVerifyValueType(
|
|
PPARSERDATA pParserData,
|
|
DWORD dwExpectedType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify the value type of the current entry matches what's expected
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
dwExpectedType - Expected value type
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwValueType;
|
|
|
|
//
|
|
// Check for following syntax error conditions:
|
|
// 1. The entry requires an option keyword but no option keyword is present
|
|
// 2. The entry doesn't require an option keyword but an option keyword is present
|
|
//
|
|
|
|
if ((dwExpectedType & REQ_OPTION) &&
|
|
IS_BUFFER_EMPTY(&pParserData->Option))
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Missing option keyword");
|
|
}
|
|
|
|
if (! (dwExpectedType & REQ_OPTION) &&
|
|
! IS_BUFFER_EMPTY(&pParserData->Option))
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Extra option keyword");
|
|
}
|
|
|
|
//
|
|
// Tolerate the following syntax error conditions:
|
|
// 1. The entry requires a quoted value but a string value is provided.
|
|
// 2. The entry requires a string value but a quoted value is provided.
|
|
//
|
|
|
|
switch (dwValueType = pParserData->dwValueType)
|
|
{
|
|
case VALUETYPE_STRING:
|
|
|
|
if (dwExpectedType & VALUETYPE_QUOTED)
|
|
{
|
|
TERSE(("%ws: Expect QuotedValue instead of StringValue on line %d\n",
|
|
pParserData->pFile->ptstrFileName,
|
|
pParserData->pFile->iLineNumber));
|
|
|
|
dwValueType = VALUETYPE_QUOTED;
|
|
}
|
|
break;
|
|
|
|
case VALUETYPE_QUOTED:
|
|
|
|
if (dwExpectedType & VALUETYPE_STRING)
|
|
{
|
|
TERSE(("%ws: Expect StringValue instead of QuotedValue on line %d\n",
|
|
pParserData->pFile->ptstrFileName,
|
|
pParserData->pFile->iLineNumber));
|
|
|
|
if (IS_BUFFER_EMPTY(&pParserData->Value))
|
|
return ISyntaxError(pParserData->pFile, "Empty string value");
|
|
|
|
dwValueType = VALUETYPE_STRING;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Return syntax error if the provided value type doesn't match what's expected
|
|
//
|
|
|
|
if ((dwExpectedType & dwValueType) == 0)
|
|
return ISyntaxError(pParserData->pFile, "Value type mismatch");
|
|
|
|
//
|
|
// If the value field is a quoted string and one of the following conditions
|
|
// is true, then we need to process any embedded hexdecimal strings within
|
|
// the quoted string:
|
|
// 1. The entry expects a QuotedValue.
|
|
// 2. The entry expects an InvocationValue and appears inside JCLOpenUI/JCLCloseUI
|
|
//
|
|
|
|
if (dwValueType == VALUETYPE_QUOTED)
|
|
{
|
|
if ((dwExpectedType & ALLOW_HEX) ||
|
|
((dwExpectedType & VALUETYPE_MASK) == INVOCA_VALUE && pParserData->bJclFeature))
|
|
{
|
|
if (! BConvertHexString(&pParserData->Value))
|
|
return ISyntaxError(pParserData->pFile, "Invalid embedded hexdecimal string");
|
|
}
|
|
else if (! BIs7BitAscii(pParserData->pstrValue))
|
|
{
|
|
//
|
|
// Only allow 7-bit ASCII characters inside invocation string
|
|
//
|
|
|
|
return ISyntaxError(pParserData->pFile, "Non-printable ASCII character");
|
|
}
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BGetIntegerFromString(
|
|
PSTR *ppstr,
|
|
LONG *plValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse an unsigned decimal integer value from a character string
|
|
|
|
Arguments:
|
|
|
|
ppstr - Points to a string pointer. On entry, it contains a pointer
|
|
to the beginning of the number string. On exit, it points to
|
|
the first non-space character after the number string.
|
|
plValue - Points to a variable for storing parsed number
|
|
|
|
Return Value:
|
|
|
|
TRUE if a number is successfully parsed, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG lValue;
|
|
PSTR pstr = *ppstr;
|
|
BOOL bNegative = FALSE;
|
|
|
|
//
|
|
// Skip any leading space characters and
|
|
// look for the sign character (if any)
|
|
//
|
|
|
|
while (IS_SPACE(*pstr))
|
|
pstr++;
|
|
|
|
if (*pstr == '-')
|
|
{
|
|
bNegative = TRUE;
|
|
pstr++;
|
|
}
|
|
|
|
if (! IS_DIGIT(*pstr))
|
|
{
|
|
TERSE(("Invalid integer number: %s\n", pstr));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// NOTE: Overflow conditions are ignored.
|
|
//
|
|
|
|
lValue = 0;
|
|
|
|
while (IS_DIGIT(*pstr))
|
|
lValue = lValue * 10 + (*pstr++ - '0');
|
|
|
|
//
|
|
// Skip any trailing space characters
|
|
//
|
|
|
|
while (IS_SPACE(*pstr))
|
|
pstr++;
|
|
|
|
*ppstr = pstr;
|
|
*plValue = bNegative ? -lValue : lValue;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BGetFloatFromString(
|
|
PSTR *ppstr,
|
|
PLONG plValue,
|
|
INT iType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse an unsigned floating-point number from a character string
|
|
|
|
Arguments:
|
|
|
|
ppstr - Points to a string pointer. On entry, it contains a pointer
|
|
to the beginning of the number string. On exit, it points to
|
|
the first non-space character after the number string.
|
|
plValue - Points to a variable for storing the parsed number
|
|
iType - How to convert the floating-point number before returning
|
|
FLTYPE_FIX - convert it to 24.8 format fixed-point number
|
|
FLTYPE_INT - convert it to integer
|
|
FLTYPE_POINT - convert it from point to micron
|
|
FLTYPE_POINT_ROUNDUP - round it up and convert it from point to micron
|
|
FLTYPE_POINT_ROUNDDOWN - round it down and convert it from point to micron
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
double value, scale;
|
|
PSTR pstr = *ppstr;
|
|
BOOL bNegative = FALSE, bFraction = FALSE;
|
|
|
|
//
|
|
// Skip any leading space characters and
|
|
// look for the sign character (if any)
|
|
//
|
|
|
|
while (IS_SPACE(*pstr))
|
|
pstr++;
|
|
|
|
if (*pstr == '-')
|
|
{
|
|
bNegative = TRUE;
|
|
pstr++;
|
|
}
|
|
|
|
if (!IS_DIGIT(*pstr) && *pstr != '.')
|
|
{
|
|
TERSE(("Invalid floating-point number\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Integer portion
|
|
//
|
|
|
|
value = 0.0;
|
|
|
|
while (IS_DIGIT(*pstr))
|
|
value = value * 10.0 + (*pstr++ - '0');
|
|
|
|
//
|
|
// Fractional portion
|
|
//
|
|
|
|
if (*pstr == '.')
|
|
{
|
|
bFraction = TRUE;
|
|
pstr++;
|
|
|
|
if (! IS_DIGIT(*pstr))
|
|
{
|
|
TERSE(("Invalid floating-point number\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
scale = 0.1;
|
|
|
|
while (IS_DIGIT(*pstr))
|
|
{
|
|
value += scale * (*pstr++ - '0');
|
|
scale *= 0.1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Skip any trailing space characters
|
|
//
|
|
|
|
while (IS_SPACE(*pstr))
|
|
pstr++;
|
|
|
|
//
|
|
// Perform requested round up or round down only if
|
|
// fractional portion is present.
|
|
//
|
|
|
|
if (bFraction)
|
|
{
|
|
if (iType == FLTYPE_POINT_ROUNDUP)
|
|
value = ceil(value);
|
|
else if (iType == FLTYPE_POINT_ROUNDDOWN)
|
|
value = (LONG)value;
|
|
}
|
|
|
|
//
|
|
// Convert the return value to the specified format
|
|
//
|
|
|
|
switch (iType)
|
|
{
|
|
case FLTYPE_POINT:
|
|
case FLTYPE_POINT_ROUNDUP:
|
|
case FLTYPE_POINT_ROUNDDOWN:
|
|
|
|
value = (value * 25400.0) / 72.0;
|
|
break;
|
|
|
|
case FLTYPE_FIX:
|
|
|
|
value *= FIX_24_8_SCALE;
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(iType == FLTYPE_INT);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Guard against overflow conditions
|
|
//
|
|
|
|
if (value >= MAX_LONG)
|
|
{
|
|
TERSE(("Floating-point number overflow\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
*ppstr = pstr;
|
|
*plValue = (LONG) (value + 0.5);
|
|
|
|
if (bNegative)
|
|
{
|
|
TERSE(("Negative number treated as 0\n"));
|
|
*plValue = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BSearchStrTable(
|
|
PCSTRTABLE pTable,
|
|
PSTR pstrKeyword,
|
|
DWORD *pdwValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search for a keyword from a string table
|
|
|
|
Arguments:
|
|
|
|
pTable - Specifies the string table to be search
|
|
pstrKeyword - Specifies the keyword we're interested in
|
|
pdwValue - Points to a variable for storing value corresponding to the given keyword
|
|
|
|
Return Value:
|
|
|
|
TRUE if the given keyword is found in the table, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(pstrKeyword != NULL);
|
|
|
|
while (pTable->pstrKeyword != NULL &&
|
|
strcmp(pTable->pstrKeyword, pstrKeyword) != EQUAL_STRING)
|
|
{
|
|
pTable++;
|
|
}
|
|
|
|
*pdwValue = pTable->dwValue;
|
|
return (pTable->pstrKeyword != NULL);
|
|
}
|
|
|
|
|
|
|
|
PSTR
|
|
PstrParseString(
|
|
PPARSERDATA pParserData,
|
|
PBUFOBJ pBufObj
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Duplicate a character string from a buffer object
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pBufObj - Specifies the buffer object containing the character string to be duplicated
|
|
|
|
Return Value:
|
|
|
|
Pointer to a copy of the specified string
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR pstr;
|
|
|
|
ASSERT(! IS_BUFFER_EMPTY(pBufObj));
|
|
|
|
if (pstr = ALLOC_PARSER_MEM(pParserData, pBufObj->dwSize + 1))
|
|
CopyMemory(pstr, pBufObj->pbuf, pBufObj->dwSize + 1);
|
|
else
|
|
ERR(("Memory allocation failed: %d\n", GetLastError()));
|
|
|
|
return pstr;
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IParseInvocation(
|
|
PPARSERDATA pParserData,
|
|
PINVOCOBJ pInvocation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse the content of value buffer to an invocation string
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pInvocation - Specifies a buffer for storing the parsed invocation string
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(pInvocation->pvData == NULL);
|
|
|
|
//
|
|
// Determine if the invocation is a quoted string or a symbol reference.
|
|
// In case of symbol reference, we save the name of the symbol in pvData
|
|
// field (including the leading ^ character).
|
|
//
|
|
|
|
if (pParserData->dwValueType == VALUETYPE_SYMBOL)
|
|
{
|
|
PSTR pstrSymbolName;
|
|
|
|
if (! (pstrSymbolName = PstrParseString(pParserData, &pParserData->Value)))
|
|
return PPDERR_MEMORY;
|
|
|
|
pInvocation->pvData = pstrSymbolName;
|
|
MARK_SYMBOL_INVOC(pInvocation);
|
|
}
|
|
else
|
|
{
|
|
PVOID pvData;
|
|
|
|
if (! (pvData = ALLOC_PARSER_MEM(pParserData, pParserData->Value.dwSize + 1)))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
pInvocation->pvData = pvData;
|
|
pInvocation->dwLength = pParserData->Value.dwSize;
|
|
ASSERT(! IS_SYMBOL_INVOC(pInvocation));
|
|
|
|
CopyMemory(pvData, pParserData->Value.pbuf, pInvocation->dwLength + 1);
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IParseInteger(
|
|
PPARSERDATA pParserData,
|
|
PDWORD pdwValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Intrepret the value field of an entry as unsigned integer
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pdwValue - Points to a variable for storing parsed integer value
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR pstr = pParserData->pstrValue;
|
|
LONG lValue;
|
|
|
|
if (BGetIntegerFromString(&pstr, &lValue))
|
|
{
|
|
if (lValue >= 0)
|
|
{
|
|
*pdwValue = lValue;
|
|
return PPDERR_NONE;
|
|
|
|
} else
|
|
TERSE(("Negative integer value not allowed: %s.\n", pParserData->pstrValue));
|
|
}
|
|
|
|
*pdwValue = 0;
|
|
return PPDERR_SYNTAX;
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IParseBoolean(
|
|
PPARSERDATA pParserData,
|
|
DWORD *pdwValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Interpret the value of an entry as a boolen, i.e. True or False
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pdwValue - Points to a variable for storing the parsed boolean value
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
static const STRTABLE BooleanStrs[] =
|
|
{
|
|
gstrTrueKwd, TRUE,
|
|
gstrFalseKwd, FALSE,
|
|
NULL, FALSE
|
|
};
|
|
|
|
if (! BSearchStrTable(BooleanStrs, pParserData->pstrValue, pdwValue))
|
|
return ISyntaxError(pParserData->pFile, "Invalid boolean constant");
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BFindNextWord(
|
|
PSTR *ppstr,
|
|
PSTR pstrWord
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the next word in a character string. Words are separated by spaces.
|
|
|
|
Arguments:
|
|
|
|
ppstr - Points to a string pointer. On entry, it contains a pointer
|
|
to the beginning of the word string. On exit, it points to
|
|
the first non-space character after the word string.
|
|
pstrWord - Points to a buffer for storing characters from the next word
|
|
The size of this buffer must be at least MAX_WORD_LEN characters
|
|
|
|
Return Value:
|
|
|
|
TRUE if next word is found, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR pstr = *ppstr;
|
|
|
|
pstrWord[0] = NUL;
|
|
|
|
//
|
|
// Skip any leading spaces
|
|
//
|
|
|
|
while (IS_SPACE(*pstr))
|
|
pstr++;
|
|
|
|
if (*pstr != NUL)
|
|
{
|
|
PSTR pstrStart;
|
|
INT iWordLen;
|
|
|
|
//
|
|
// Go to the end of the word
|
|
//
|
|
|
|
pstrStart = pstr;
|
|
|
|
while (*pstr && !IS_SPACE(*pstr))
|
|
pstr++;
|
|
|
|
//
|
|
// Copy the word into the specified buffer
|
|
//
|
|
|
|
if ((iWordLen = (INT)(pstr - pstrStart)) < MAX_WORD_LEN)
|
|
{
|
|
CopyMemory(pstrWord, pstrStart, iWordLen);
|
|
pstrWord[iWordLen] = NUL;
|
|
}
|
|
|
|
//
|
|
// Skip to the next non-space character
|
|
//
|
|
|
|
while (IS_SPACE(*pstr))
|
|
pstr++;
|
|
}
|
|
|
|
*ppstr = pstr;
|
|
return (*pstrWord != NUL);
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IParseVersionNumber(
|
|
PPARSERDATA pParserData,
|
|
PDWORD pdwVersion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse a version number. The format of a version number is Version[.Revision]
|
|
where both Version and Revision are integers.
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pdwVersion - Points to a variable for storing the parsed version number
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR pstr;
|
|
LONG lVersion, lRevision = 0;
|
|
|
|
//
|
|
// Parse the major version number followed by minor revision number
|
|
//
|
|
|
|
pstr = pParserData->pstrValue;
|
|
|
|
if (! BGetIntegerFromString(&pstr, &lVersion))
|
|
return ISyntaxError(pParserData->pFile, "Invalid version number");
|
|
|
|
if (*pstr == '.')
|
|
{
|
|
pstr++;
|
|
|
|
if (! BGetIntegerFromString(&pstr, &lRevision))
|
|
return ISyntaxError(pParserData->pFile, "Invalid revision number");
|
|
}
|
|
|
|
//
|
|
// High-order word contains version number and
|
|
// low-order word contains revision number
|
|
//
|
|
|
|
if (lVersion < 0 || lVersion > MAX_WORD ||
|
|
lRevision < 0 || lRevision > MAX_WORD)
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Version number out-of-range");
|
|
}
|
|
|
|
*pdwVersion = (lVersion << 16) | lRevision;
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IParseXlation(
|
|
PPARSERDATA pParserData,
|
|
PINVOCOBJ pXlation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse the information in the translation string field to an INVOCOBJ
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pXlation - Returns information about parsed translation string
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUFOBJ pBufObj = &pParserData->Xlation;
|
|
|
|
//
|
|
// Allocate memory to hold the translation string (plus a null terminator)
|
|
//
|
|
|
|
pXlation->pvData = ALLOC_PARSER_MEM(pParserData, pBufObj->dwSize + 1);
|
|
|
|
if (pXlation->pvData == NULL)
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
pXlation->dwLength = pBufObj->dwSize;
|
|
ASSERT(! IS_SYMBOL_INVOC(pXlation));
|
|
CopyMemory(pXlation->pvData, pBufObj->pbuf, pBufObj->dwSize);
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
|
|
|
|
PCSTR
|
|
PstrStripKeywordChar(
|
|
PCSTR pstrKeyword
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Strip off the keyword prefix character from the input string
|
|
|
|
Arguments:
|
|
|
|
pstrKeyword - Points to a string prefixed by the keyword character
|
|
|
|
Return Value:
|
|
|
|
Pointer to the keyword string after the keyword character is stripped
|
|
NULL if the keyword string is empty
|
|
|
|
--*/
|
|
|
|
{
|
|
if (IS_KEYWORD_CHAR(*pstrKeyword))
|
|
pstrKeyword++;
|
|
|
|
return *pstrKeyword ? pstrKeyword : NULL;
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
PvCreateListItem(
|
|
PPARSERDATA pParserData,
|
|
PLISTOBJ *ppList,
|
|
DWORD dwItemSize,
|
|
PSTR pstrListTag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new item in the specified linked-list
|
|
Make sure no existing item has the same name
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
ppList - Specifies the linked-list
|
|
dwItemSize - Linked-list item size
|
|
pListTag - Specifies the name of the linked-list (for debugging purpose)
|
|
|
|
Return Value:
|
|
|
|
Pointer to newly created linked-list item
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PLISTOBJ pItem;
|
|
PSTR pstrItemName;
|
|
|
|
//
|
|
// Check if the item appeared in the list already
|
|
// Create a new item data structure if not
|
|
//
|
|
|
|
pItem = PvFindListItem(*ppList, pParserData->Option.pbuf, NULL);
|
|
|
|
if (pItem != NULL)
|
|
{
|
|
if (pstrListTag)
|
|
TERSE(("%s %s redefined\n", pstrListTag, pItem->pstrName));
|
|
}
|
|
else
|
|
{
|
|
if (! (pItem = ALLOC_PARSER_MEM(pParserData, dwItemSize)) ||
|
|
! (pstrItemName = PstrParseString(pParserData, &pParserData->Option)))
|
|
{
|
|
ERR(("Memory allocation failed: %d\n", GetLastError()));
|
|
return NULL;
|
|
}
|
|
|
|
pItem->pstrName = pstrItemName;
|
|
pItem->pNext = NULL;
|
|
|
|
//
|
|
// Put the newly created item at the end of the linked-list
|
|
//
|
|
|
|
while (*ppList != NULL)
|
|
ppList = (PLISTOBJ *) &((*ppList)->pNext);
|
|
|
|
*ppList = pItem;
|
|
}
|
|
|
|
return pItem;
|
|
}
|
|
|
|
|
|
|
|
PFEATUREOBJ
|
|
PCreateFeatureItem(
|
|
PPARSERDATA pParserData,
|
|
DWORD dwFeatureID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new printer feature structure or find an existing one
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
dwFeatureID - Printer feature identifier
|
|
|
|
Return Value:
|
|
|
|
Pointer to a newly created or an existing printer feature structure
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
static struct {
|
|
|
|
PCSTR pstrKeyword;
|
|
DWORD dwFeatureID;
|
|
DWORD dwOptionSize;
|
|
|
|
} FeatureInfo[] = {
|
|
|
|
gstrPageSizeKwd, GID_PAGESIZE, sizeof(PAPEROBJ),
|
|
"PageRegion", GID_PAGEREGION, sizeof(OPTIONOBJ),
|
|
"Duplex", GID_DUPLEX, sizeof(OPTIONOBJ),
|
|
gstrInputSlotKwd, GID_INPUTSLOT, sizeof(TRAYOBJ),
|
|
"Resolution", GID_RESOLUTION, sizeof(RESOBJ),
|
|
"JCLResolution", GID_RESOLUTION, sizeof(RESOBJ),
|
|
"OutputBin", GID_OUTPUTBIN, sizeof(BINOBJ),
|
|
"MediaType", GID_MEDIATYPE, sizeof(OPTIONOBJ),
|
|
"Collate", GID_COLLATE, sizeof(OPTIONOBJ),
|
|
"InstalledMemory",GID_MEMOPTION, sizeof(MEMOBJ),
|
|
"LeadingEdge", GID_LEADINGEDGE, sizeof(OPTIONOBJ),
|
|
"UseHWMargins", GID_USEHWMARGINS, sizeof(OPTIONOBJ),
|
|
NULL, GID_UNKNOWN, sizeof(OPTIONOBJ)
|
|
};
|
|
|
|
PFEATUREOBJ pFeature;
|
|
PCSTR pstrKeyword;
|
|
BUFOBJ SavedBuffer;
|
|
INT iIndex = 0;
|
|
|
|
if (dwFeatureID == GID_UNKNOWN)
|
|
{
|
|
//
|
|
// Given a feature name, first find out if it refers to
|
|
// one of the predefined features
|
|
//
|
|
|
|
pstrKeyword = PstrStripKeywordChar(pParserData->achOption);
|
|
ASSERT(pstrKeyword != NULL);
|
|
|
|
while (FeatureInfo[iIndex].pstrKeyword &&
|
|
strcmp(FeatureInfo[iIndex].pstrKeyword, pstrKeyword) != EQUAL_STRING)
|
|
{
|
|
iIndex++;
|
|
}
|
|
|
|
if (FeatureInfo[iIndex].pstrKeyword)
|
|
pParserData->aubOpenUIFeature[FeatureInfo[iIndex].dwFeatureID] = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We're given a predefined feature identifier.
|
|
// Map to its corresponding feature name.
|
|
//
|
|
|
|
while (FeatureInfo[iIndex].pstrKeyword &&
|
|
dwFeatureID != FeatureInfo[iIndex].dwFeatureID)
|
|
{
|
|
iIndex++;
|
|
}
|
|
|
|
pstrKeyword = FeatureInfo[iIndex].pstrKeyword;
|
|
ASSERT(pstrKeyword != NULL);
|
|
}
|
|
|
|
//
|
|
// If we're dealing with a predefined feature, the first search the current
|
|
// list of printer features based on the predefined feature identifier.
|
|
//
|
|
|
|
pFeature = NULL;
|
|
|
|
if (FeatureInfo[iIndex].dwFeatureID != GID_UNKNOWN)
|
|
{
|
|
for (pFeature = pParserData->pFeatures;
|
|
pFeature && pFeature->dwFeatureID != FeatureInfo[iIndex].dwFeatureID;
|
|
pFeature = pFeature->pNext)
|
|
{
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a new printer feature item or find an existing printer feature item
|
|
// based on the feature name.
|
|
//
|
|
|
|
if (pFeature == NULL)
|
|
{
|
|
SavedBuffer = pParserData->Option;
|
|
pParserData->Option.pbuf = (PBYTE) pstrKeyword;
|
|
pParserData->Option.dwSize = strlen(pstrKeyword);
|
|
|
|
pFeature = PvCreateListItem(pParserData,
|
|
(PLISTOBJ *) &pParserData->pFeatures,
|
|
sizeof(FEATUREOBJ),
|
|
NULL);
|
|
|
|
pParserData->Option = SavedBuffer;
|
|
}
|
|
|
|
if (pFeature)
|
|
{
|
|
//
|
|
// Parse the translation string for the feature name
|
|
//
|
|
|
|
if (dwFeatureID == GID_UNKNOWN &&
|
|
! IS_BUFFER_EMPTY(&pParserData->Xlation) &&
|
|
! pFeature->Translation.pvData &&
|
|
IParseXlation(pParserData, &pFeature->Translation) != PPDERR_NONE)
|
|
{
|
|
ERR(("Failed to parse feature name translation string\n"));
|
|
return NULL;
|
|
}
|
|
|
|
if (pFeature->dwOptionSize == 0)
|
|
{
|
|
//
|
|
// Store information about newly created feature item
|
|
//
|
|
|
|
pFeature->dwOptionSize = FeatureInfo[iIndex].dwOptionSize;
|
|
pFeature->dwFeatureID = FeatureInfo[iIndex].dwFeatureID;
|
|
|
|
//
|
|
// All predefined features are doc-sticky except for InstalledMemory/VMOption
|
|
//
|
|
|
|
if (pFeature->dwFeatureID == GID_MEMOPTION ||
|
|
pFeature->dwFeatureID == GID_UNKNOWN && pParserData->bInstallableGroup)
|
|
{
|
|
pFeature->bInstallable = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERR(("Couldn't create printer feature item for: %s\n", pstrKeyword));
|
|
}
|
|
|
|
return pFeature;
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
PvCreateXlatedItem(
|
|
PPARSERDATA pParserData,
|
|
PVOID ppList,
|
|
DWORD dwItemSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a feature option item and parse the associated translation string
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
ppList - Points to the list of feature option items
|
|
dwItemSize - Size of a feature option item
|
|
|
|
Return Value:
|
|
|
|
Pointer to newly created feature option item
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
POPTIONOBJ pOption;
|
|
|
|
if (! (pOption = PvCreateListItem(pParserData, ppList, dwItemSize, NULL)) ||
|
|
(! IS_BUFFER_EMPTY(&pParserData->Xlation) &&
|
|
! pOption->Translation.pvData &&
|
|
IParseXlation(pParserData, &pOption->Translation) != PPDERR_NONE))
|
|
{
|
|
ERR(("Couldn't process entry: *%s %s\n",
|
|
pParserData->achKeyword,
|
|
pParserData->achOption));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return pOption;
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
PvCreateOptionItem(
|
|
PPARSERDATA pParserData,
|
|
DWORD dwFeatureID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a feature option item for a predefined printer feature
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
dwFeatureID - Specifies a predefined feature identifier
|
|
|
|
Return Value:
|
|
|
|
Pointer to an existing feature option item or a newly created one if none exists
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PFEATUREOBJ pFeature;
|
|
|
|
ASSERT(dwFeatureID != GID_UNKNOWN);
|
|
|
|
if (! (pFeature = PCreateFeatureItem(pParserData, dwFeatureID)))
|
|
return NULL;
|
|
|
|
return PvCreateXlatedItem(pParserData, &pFeature->pOptions, pFeature->dwOptionSize);
|
|
}
|
|
|
|
|
|
|
|
INT
|
|
ICountFeatureList(
|
|
PFEATUREOBJ pFeature,
|
|
BOOL bInstallable
|
|
)
|
|
|
|
{
|
|
INT i = 0;
|
|
|
|
//
|
|
// Count the number of features of the specified type
|
|
//
|
|
|
|
while (pFeature != NULL)
|
|
{
|
|
if (pFeature->bInstallable == bInstallable)
|
|
i++;
|
|
|
|
pFeature = pFeature->pNext;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IGenericOptionProc(
|
|
PPARSERDATA pParserData,
|
|
PFEATUREOBJ pFeature
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function for handling a generic feature option entry
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pFeature - Points to feature data structure
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
POPTIONOBJ pOption;
|
|
|
|
//
|
|
// Handle special case
|
|
//
|
|
|
|
if (pFeature == NULL)
|
|
return PPDERR_MEMORY;
|
|
|
|
//
|
|
// Create a feature option item and parse the option name and translation string
|
|
//
|
|
|
|
if (! (pOption = PvCreateXlatedItem(pParserData, &pFeature->pOptions, pFeature->dwOptionSize)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pOption->Invocation.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Parse the invocation string
|
|
//
|
|
|
|
return IParseInvocation(pParserData, &pOption->Invocation);
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IGenericDefaultProc(
|
|
PPARSERDATA pParserData,
|
|
PFEATUREOBJ pFeature
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function for handling a generic default option entry
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pFeature - Points to feature data structure
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Check if there is a memory error before this function is called
|
|
//
|
|
|
|
if (pFeature == NULL)
|
|
return PPDERR_MEMORY;
|
|
|
|
//
|
|
// Watch out for duplicate *Default entries for the same feature
|
|
//
|
|
|
|
if (pFeature->pstrDefault)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// NOTE: Hack to take in account of a bug in NT4 driver.
|
|
// This is used to build up NT4-NT5 feature index mapping.
|
|
//
|
|
|
|
if (pFeature->dwFeatureID == GID_MEMOPTION &&
|
|
pParserData->iDefInstallMemIndex < 0)
|
|
{
|
|
pParserData->iDefInstallMemIndex = ICountFeatureList(pParserData->pFeatures, TRUE);
|
|
}
|
|
|
|
//
|
|
// Remember the default option keyword
|
|
//
|
|
|
|
if (pFeature->pstrDefault = PstrParseString(pParserData, &pParserData->Value))
|
|
return PPDERR_NONE;
|
|
else
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
|
|
|
|
PPDERROR
|
|
IGenericQueryProc(
|
|
PPARSERDATA pParserData,
|
|
PFEATUREOBJ pFeature
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function for handling a generic query invocation entry
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pFeature - Points to feature data structure
|
|
|
|
Return Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Check if there is a memory error before this function is called
|
|
//
|
|
|
|
if (pFeature == NULL)
|
|
return PPDERR_MEMORY;
|
|
|
|
//
|
|
// Watch out for duplicate *Default entries for the same feature
|
|
//
|
|
|
|
if (pFeature->QueryInvoc.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Parse the query invocation string
|
|
//
|
|
|
|
return IParseInvocation(pParserData, &pFeature->QueryInvoc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Functions for handling predefined PPD keywords
|
|
//
|
|
|
|
//
|
|
// Specifies the imageable area of a media option
|
|
//
|
|
|
|
PPDERROR
|
|
IImageAreaProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PPAPEROBJ pOption;
|
|
PSTR pstr;
|
|
RECT *pRect;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_PAGESIZE)))
|
|
return PPDERR_MEMORY;
|
|
|
|
pRect = &pOption->rcImageArea;
|
|
|
|
if (pRect->top > 0 || pRect->right > 0)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Parse imageable area: left, bottom, right, top
|
|
//
|
|
|
|
pstr = pParserData->pstrValue;
|
|
|
|
if (! BGetFloatFromString(&pstr, &pRect->left, FLTYPE_POINT_ROUNDUP) ||
|
|
! BGetFloatFromString(&pstr, &pRect->bottom, FLTYPE_POINT_ROUNDUP) ||
|
|
! BGetFloatFromString(&pstr, &pRect->right, FLTYPE_POINT_ROUNDDOWN) ||
|
|
! BGetFloatFromString(&pstr, &pRect->top, FLTYPE_POINT_ROUNDDOWN) ||
|
|
pRect->left >= pRect->right || pRect->bottom >= pRect->top)
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Invalid imageable area");
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the paper dimension of a media option
|
|
//
|
|
|
|
PPDERROR
|
|
IPaperDimProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PPAPEROBJ pOption;
|
|
PSTR pstr;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_PAGESIZE)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pOption->szDimension.cx > 0 || pOption->szDimension.cy > 0)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Parse paper width and height
|
|
//
|
|
|
|
pstr = pParserData->pstrValue;
|
|
|
|
if (! BGetFloatFromString(&pstr, &pOption->szDimension.cx, FLTYPE_POINT) ||
|
|
! BGetFloatFromString(&pstr, &pOption->szDimension.cy, FLTYPE_POINT))
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Invalid paper dimension");
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Interpret PageStackOrder and OutputOrder options
|
|
//
|
|
|
|
BOOL
|
|
BParseOutputOrder(
|
|
PSTR pstr,
|
|
PBOOL pbValue
|
|
)
|
|
|
|
{
|
|
static const STRTABLE OutputOrderStrs[] =
|
|
{
|
|
"Normal", FALSE,
|
|
"Reverse", TRUE,
|
|
NULL, FALSE
|
|
};
|
|
|
|
DWORD dwValue;
|
|
BOOL bResult;
|
|
|
|
bResult = BSearchStrTable(OutputOrderStrs, pstr, &dwValue);
|
|
|
|
*pbValue = dwValue;
|
|
return bResult;
|
|
}
|
|
|
|
//
|
|
// Specifies the page stack order for an output bin
|
|
//
|
|
|
|
PPDERROR
|
|
IPageStackOrderProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PFEATUREOBJ pFeature;
|
|
PBINOBJ pOutputBin;
|
|
|
|
//
|
|
// Have we seen the OutputBin feature yet?
|
|
//
|
|
|
|
for (pFeature = pParserData->pFeatures;
|
|
pFeature && pFeature->dwFeatureID != GID_OUTPUTBIN;
|
|
pFeature = pFeature->pNext)
|
|
{
|
|
}
|
|
|
|
//
|
|
// If PageStackOrder entry appears before OutputBin feature, ignore it
|
|
//
|
|
|
|
if (pFeature == NULL)
|
|
{
|
|
BOOL bReverse;
|
|
if (!BParseOutputOrder(pParserData->pstrValue, &bReverse))
|
|
return ISyntaxError(pParserData->pFile, "Invalid PageStackOrder option");
|
|
if (bReverse)
|
|
TERSE(("%ws: Ignored *PageStackOrder: Reverse on line %d because OutputBin not yet defined\n",
|
|
pParserData->pFile->ptstrFileName,
|
|
pParserData->pFile->iLineNumber));
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Add an option for OutputBin feature
|
|
//
|
|
|
|
pOutputBin = PvCreateXlatedItem(pParserData, &pFeature->pOptions, pFeature->dwOptionSize);
|
|
|
|
if (pOutputBin == NULL)
|
|
return PPDERR_MEMORY;
|
|
|
|
return BParseOutputOrder(pParserData->pstrValue, &pOutputBin->bReversePrint) ?
|
|
PPDERR_NONE :
|
|
ISyntaxError(pParserData->pFile, "Invalid PageStackOrder option");
|
|
}
|
|
|
|
//
|
|
// Specifies the default page output order
|
|
// NOTE: This function gets called only if *DefaultOutputOrder
|
|
// entry appears outside of OpenUI/CloseUI.
|
|
//
|
|
|
|
PPDERROR
|
|
IDefOutputOrderProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
pParserData->bDefOutputOrderSet = BParseOutputOrder(pParserData->pstrValue, &pParserData->bDefReversePrint);
|
|
|
|
return pParserData->bDefOutputOrderSet ?
|
|
PPDERR_NONE :
|
|
ISyntaxError(pParserData->pFile, "Invalid DefaultOutputOrder option");
|
|
}
|
|
|
|
//
|
|
// Specifies whether an input slot requires page region specification
|
|
//
|
|
|
|
PPDERROR
|
|
IReqPageRgnProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PTRAYOBJ pOption;
|
|
DWORD dwValue;
|
|
|
|
//
|
|
// NOTE: Hack for doing NT4-NT5 feature index conversion
|
|
//
|
|
|
|
if (pParserData->iReqPageRgnIndex < 0)
|
|
{
|
|
PFEATUREOBJ pFeature = pParserData->pFeatures;
|
|
|
|
while (pFeature && pFeature->dwFeatureID != GID_INPUTSLOT)
|
|
pFeature = pFeature->pNext;
|
|
|
|
if (pFeature == NULL)
|
|
pParserData->iReqPageRgnIndex = ICountFeatureList(pParserData->pFeatures, FALSE);
|
|
}
|
|
|
|
//
|
|
// The value should be either True or False
|
|
//
|
|
|
|
if (IParseBoolean(pParserData, &dwValue) != PPDERR_NONE)
|
|
return PPDERR_SYNTAX;
|
|
|
|
dwValue = dwValue ? REQRGN_TRUE : REQRGN_FALSE;
|
|
|
|
//
|
|
// *RequiresPageRegion All: entry has special meaning
|
|
//
|
|
|
|
if (strcmp(pParserData->achOption, "All") == EQUAL_STRING)
|
|
{
|
|
if (pParserData->dwReqPageRgn == REQRGN_UNKNOWN)
|
|
pParserData->dwReqPageRgn = dwValue;
|
|
else
|
|
WARN_DUPLICATE();
|
|
}
|
|
else
|
|
{
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_INPUTSLOT)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pOption->dwReqPageRgn == REQRGN_UNKNOWN)
|
|
pOption->dwReqPageRgn = dwValue;
|
|
else
|
|
WARN_DUPLICATE();
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies Duplex feature options
|
|
//
|
|
|
|
PPDERROR
|
|
IDefaultDuplexProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IGenericDefaultProc(pParserData,
|
|
PCreateFeatureItem(pParserData, GID_DUPLEX));
|
|
}
|
|
|
|
PPDERROR
|
|
IDuplexProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
if (strcmp(pParserData->achOption, gstrNoneKwd) != EQUAL_STRING &&
|
|
strcmp(pParserData->achOption, gstrDuplexTumble) != EQUAL_STRING &&
|
|
strcmp(pParserData->achOption, gstrDuplexNoTumble) != EQUAL_STRING)
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Invalid Duplex option");
|
|
}
|
|
|
|
return IGenericOptionProc(pParserData,
|
|
PCreateFeatureItem(pParserData, GID_DUPLEX));
|
|
}
|
|
|
|
//
|
|
// Specifies ManualFeed True/False invocation strings
|
|
//
|
|
|
|
PPDERROR
|
|
IDefManualFeedProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
//
|
|
// NOTE: Hack for doing NT4-NT5 feature index conversion
|
|
//
|
|
|
|
if (pParserData->iManualFeedIndex < 0)
|
|
pParserData->iManualFeedIndex = ICountFeatureList(pParserData->pFeatures, FALSE);
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
PPDERROR
|
|
IManualFeedProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
POPTIONOBJ pOption;
|
|
INT iResult = PPDERR_NONE;
|
|
|
|
//
|
|
// NOTE: Hack for doing NT4-NT5 feature index conversion
|
|
//
|
|
|
|
if (pParserData->iManualFeedIndex < 0)
|
|
pParserData->iManualFeedIndex = ICountFeatureList(pParserData->pFeatures, FALSE);
|
|
|
|
if (strcmp(pParserData->achOption, gstrTrueKwd) == EQUAL_STRING ||
|
|
strcmp(pParserData->achOption, gstrOnKwd) == EQUAL_STRING)
|
|
{
|
|
//
|
|
// The way manual feed is handled in PPD spec is incredibly klugy.
|
|
// Hack here to treat *ManualFeed True as one of the input slot
|
|
// selections so that downstream component can handle it uniformly.
|
|
//
|
|
|
|
StringCchCopyA(pParserData->achOption, CCHOF(pParserData->achOption), gstrManualFeedKwd);
|
|
|
|
pParserData->Option.dwSize = strlen(gstrManualFeedKwd);
|
|
|
|
StringCchCopyA(pParserData->achXlation, CCHOF(pParserData->achXlation), "");
|
|
|
|
pParserData->Xlation.dwSize = 0;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_INPUTSLOT)))
|
|
{
|
|
iResult = PPDERR_MEMORY;
|
|
}
|
|
else if (pOption->Invocation.pvData)
|
|
{
|
|
TERSE(("%ws: Duplicate entries of '*ManualFeed True' on line %d\n",
|
|
pParserData->pFile->ptstrFileName,
|
|
pParserData->pFile->iLineNumber));
|
|
}
|
|
else
|
|
{
|
|
((PTRAYOBJ) pOption)->dwTrayIndex = DMBIN_MANUAL;
|
|
iResult = IParseInvocation(pParserData, &pOption->Invocation);
|
|
}
|
|
}
|
|
else if (strcmp(pParserData->achOption, gstrFalseKwd) == EQUAL_STRING ||
|
|
strcmp(pParserData->achOption, gstrNoneKwd) == EQUAL_STRING ||
|
|
strcmp(pParserData->achOption, gstrOffKwd) == EQUAL_STRING)
|
|
{
|
|
//
|
|
// Save *ManualFeed False invocation string separately.
|
|
// It's always emitted before any tray invocation string.
|
|
//
|
|
|
|
if (pParserData->ManualFeedFalse.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
}
|
|
else
|
|
{
|
|
iResult = IParseInvocation(pParserData, &pParserData->ManualFeedFalse);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iResult = ISyntaxError(pParserData->pFile, "Unrecognized ManualFeed option");
|
|
}
|
|
|
|
return iResult;
|
|
}
|
|
|
|
//
|
|
// Specifies JCLResolution invocation string
|
|
//
|
|
|
|
PPDERROR
|
|
IJCLResProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
POPTIONOBJ pOption;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_RESOLUTION)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pOption->Invocation.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
pParserData->dwSetResType = RESTYPE_JCL;
|
|
return IParseInvocation(pParserData, &pOption->Invocation);
|
|
}
|
|
|
|
//
|
|
// Specifies the default JCLResolution option
|
|
//
|
|
|
|
PPDERROR
|
|
IDefaultJCLResProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IGenericDefaultProc(pParserData,
|
|
PCreateFeatureItem(pParserData, GID_RESOLUTION));
|
|
}
|
|
|
|
//
|
|
// Specifies SetResolution invocation string
|
|
//
|
|
|
|
PPDERROR
|
|
ISetResProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
POPTIONOBJ pOption;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_RESOLUTION)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pOption->Invocation.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
pParserData->dwSetResType = RESTYPE_EXITSERVER;
|
|
return IParseInvocation(pParserData, &pOption->Invocation);
|
|
}
|
|
|
|
//
|
|
// Specifies default halftone screen angle
|
|
//
|
|
|
|
PPDERROR
|
|
IScreenAngleProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
if (! BGetFloatFromString(&pstr, &pParserData->fxScreenAngle, FLTYPE_FIX))
|
|
return ISyntaxError(pParserData->pFile, "Invalid screen angle");
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies default halftone screen frequency
|
|
//
|
|
|
|
PPDERROR
|
|
IScreenFreqProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
if (! BGetFloatFromString(&pstr, &pParserData->fxScreenFreq, FLTYPE_FIX) ||
|
|
pParserData->fxScreenFreq <= 0)
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Invalid screen frequency");
|
|
}
|
|
else
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies default halftone screen angle for a resolution option
|
|
//
|
|
|
|
PPDERROR
|
|
IResScreenAngleProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PRESOBJ pOption;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_RESOLUTION)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (! BGetFloatFromString(&pstr, &pOption->fxScreenAngle, FLTYPE_FIX))
|
|
return ISyntaxError(pParserData->pFile, "Invalid screen angle");
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies default halftone screen frequency for a resolution option
|
|
//
|
|
|
|
PPDERROR
|
|
IResScreenFreqProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PRESOBJ pOption;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_RESOLUTION)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (! BGetFloatFromString(&pstr, &pOption->fxScreenFreq, FLTYPE_FIX) ||
|
|
pOption->fxScreenFreq <= 0)
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Invalid screen frequency");
|
|
}
|
|
else
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies device font information
|
|
//
|
|
|
|
PPDERROR
|
|
IFontProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE FontStatusStrs[] =
|
|
{
|
|
"ROM", FONTSTATUS_ROM,
|
|
"Disk", FONTSTATUS_DISK,
|
|
NULL, FONTSTATUS_UNKNOWN
|
|
};
|
|
|
|
PFONTREC pFont;
|
|
PSTR pstr;
|
|
CHAR achWord[MAX_WORD_LEN];
|
|
DWORD cbSize;
|
|
|
|
//
|
|
// Create a new device font item
|
|
//
|
|
|
|
if (! (pFont = PvCreateXlatedItem(pParserData, &pParserData->pFonts, sizeof(FONTREC))))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pFont->pstrEncoding != NULL)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// encoding
|
|
//
|
|
|
|
pstr = pParserData->pstrValue;
|
|
|
|
if (! BFindNextWord(&pstr, achWord))
|
|
return ISyntaxError(pParserData->pFile, "Invalid *Font entry");
|
|
|
|
cbSize = strlen(achWord) + 1;
|
|
if (! (pFont->pstrEncoding = ALLOC_PARSER_MEM(pParserData, cbSize)))
|
|
return PPDERR_MEMORY;
|
|
|
|
StringCchCopyA(pFont->pstrEncoding, cbSize / sizeof(char), achWord);
|
|
|
|
//
|
|
// version
|
|
//
|
|
|
|
(VOID) BFindNextWord(&pstr, achWord);
|
|
|
|
{
|
|
PSTR pstrStart, pstrEnd;
|
|
|
|
if (pstrStart = strchr(achWord, '('))
|
|
pstrStart++;
|
|
else
|
|
pstrStart = achWord;
|
|
|
|
if (pstrEnd = strrchr(pstrStart, ')'))
|
|
*pstrEnd = NUL;
|
|
|
|
cbSize = strlen(pstrStart) + 1;
|
|
if (! (pFont->pstrVersion = ALLOC_PARSER_MEM(pParserData, cbSize)))
|
|
return PPDERR_MEMORY;
|
|
|
|
StringCchCopyA(pFont->pstrVersion, cbSize / sizeof(char), pstrStart);
|
|
}
|
|
|
|
//
|
|
// charset
|
|
//
|
|
|
|
(VOID) BFindNextWord(&pstr, achWord);
|
|
|
|
cbSize = strlen(achWord) + 1;
|
|
if (! (pFont->pstrCharset = ALLOC_PARSER_MEM(pParserData, cbSize)))
|
|
return PPDERR_MEMORY;
|
|
|
|
StringCchCopyA(pFont->pstrCharset, cbSize / sizeof(char), achWord);
|
|
|
|
//
|
|
// status
|
|
//
|
|
|
|
(VOID) BFindNextWord(&pstr, achWord);
|
|
(VOID) BSearchStrTable(FontStatusStrs, achWord, &pFont->dwStatus);
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the default device font
|
|
//
|
|
|
|
PPDERROR
|
|
IDefaultFontProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
if (strcmp(pParserData->pstrValue, "Error") == EQUAL_STRING)
|
|
pParserData->pstrDefaultFont = NULL;
|
|
else if (! (pParserData->pstrDefaultFont = PstrParseString(pParserData, &pParserData->Value)))
|
|
return PPDERR_MEMORY;
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Mark the beginning of a new printer feature section
|
|
//
|
|
|
|
PPDERROR
|
|
IOpenUIProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE UITypeStrs[] =
|
|
{
|
|
"PickOne", UITYPE_PICKONE,
|
|
"PickMany", UITYPE_PICKMANY,
|
|
"Boolean", UITYPE_BOOLEAN,
|
|
NULL, UITYPE_PICKONE
|
|
};
|
|
|
|
PCSTR pstrKeyword;
|
|
|
|
//
|
|
// Guard against nested or unbalanced OpenUI
|
|
//
|
|
|
|
if (pParserData->pOpenFeature != NULL)
|
|
{
|
|
TERSE(("Missing CloseUI for *%s\n", pParserData->pOpenFeature->pstrName));
|
|
pParserData->pOpenFeature = NULL;
|
|
}
|
|
|
|
//
|
|
// Make sure the keyword is well-formed
|
|
//
|
|
|
|
if (! (pstrKeyword = PstrStripKeywordChar(pParserData->achOption)))
|
|
return ISyntaxError(pParserData->pFile, "Empty keyword");
|
|
|
|
//
|
|
// HACK: special-case handling of "*OpenUI: *ManualFeed" entry
|
|
//
|
|
|
|
if (strcmp(pstrKeyword, gstrManualFeedKwd) == EQUAL_STRING)
|
|
return PPDERR_NONE;
|
|
|
|
if (! (pParserData->pOpenFeature = PCreateFeatureItem(pParserData, GID_UNKNOWN)))
|
|
return PPDERR_MEMORY;
|
|
|
|
//
|
|
// Determine the type of feature option list
|
|
//
|
|
|
|
if (! BSearchStrTable(UITypeStrs,
|
|
pParserData->pstrValue,
|
|
&pParserData->pOpenFeature->dwUIType))
|
|
{
|
|
ISyntaxError(pParserData->pFile, "Unrecognized UI type");
|
|
}
|
|
|
|
//
|
|
// Are we dealing with JCLOpenUI?
|
|
//
|
|
|
|
pParserData->bJclFeature = HAS_JCL_PREFIX(pstrKeyword);
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Mark the end of a new printer feature section
|
|
//
|
|
|
|
PPDERROR
|
|
ICloseUIProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PCSTR pstrKeyword;
|
|
PFEATUREOBJ pOpenFeature;
|
|
|
|
//
|
|
// Make sure the CloseUI entry is balanced with a previous OpenUI entry
|
|
//
|
|
|
|
pOpenFeature = pParserData->pOpenFeature;
|
|
pParserData->pOpenFeature = NULL;
|
|
pstrKeyword = PstrStripKeywordChar(pParserData->pstrValue);
|
|
|
|
//
|
|
// HACK: special-case handling of "*CloseUI: *ManualFeed" entry
|
|
//
|
|
|
|
if (pstrKeyword && strcmp(pstrKeyword, gstrManualFeedKwd) == EQUAL_STRING)
|
|
return PPDERR_NONE;
|
|
|
|
if (pOpenFeature == NULL ||
|
|
pstrKeyword == NULL ||
|
|
strcmp(pstrKeyword, pOpenFeature->pstrName) != EQUAL_STRING ||
|
|
pParserData->bJclFeature != HAS_JCL_PREFIX(pstrKeyword))
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Invalid CloseUI entry");
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Process OpenGroup and CloseGroup entries
|
|
//
|
|
// !!! OpenGroup, CloseGroup, OpenSubGroup, and CloseSubGroup
|
|
// keywords are not completely supported. Currently, we
|
|
// only pay specific attention to the InstallableOptions group.
|
|
//
|
|
// If the group information is needed in the future by the
|
|
// user interface, the following functions should be beefed up.
|
|
//
|
|
|
|
PPDERROR
|
|
IOpenCloseGroupProc(
|
|
PPARSERDATA pParserData,
|
|
BOOL bOpenGroup
|
|
)
|
|
|
|
{
|
|
PSTR pstrGroupName = pParserData->pstrValue;
|
|
|
|
//
|
|
// We're only interested in the InstallableOptions group
|
|
//
|
|
|
|
if (strcmp(pstrGroupName, "InstallableOptions") == EQUAL_STRING)
|
|
{
|
|
if (pParserData->bInstallableGroup == bOpenGroup)
|
|
return ISyntaxError(pParserData->pFile, "Unbalanced OpenGroup/CloseGroup");
|
|
|
|
pParserData->bInstallableGroup = bOpenGroup;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE(("Group %s ignored\n", pstrGroupName));
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Process OpenGroup entries
|
|
//
|
|
|
|
PPDERROR
|
|
IOpenGroupProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IOpenCloseGroupProc(pParserData, TRUE);
|
|
}
|
|
|
|
//
|
|
// Process CloseGroup entries
|
|
//
|
|
|
|
PPDERROR
|
|
ICloseGroupProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IOpenCloseGroupProc(pParserData, FALSE);
|
|
}
|
|
|
|
//
|
|
// Handle OpenSubGroup entries
|
|
//
|
|
|
|
PPDERROR
|
|
IOpenSubGroupProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Handle CloseSubGroup entries
|
|
//
|
|
|
|
PPDERROR
|
|
ICloseSubGroupProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Parse a UIConstraints entry
|
|
//
|
|
|
|
PPDERROR
|
|
IUIConstraintsProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PLISTOBJ pItem;
|
|
|
|
if (! (pItem = ALLOC_PARSER_MEM(pParserData, sizeof(LISTOBJ))) ||
|
|
! (pItem->pstrName = PstrParseString(pParserData, &pParserData->Value)))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
pItem->pNext = pParserData->pUIConstraints;
|
|
pParserData->pUIConstraints = pItem;
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Parse an OrderDependency entry
|
|
//
|
|
|
|
PPDERROR
|
|
IOrderDepProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PLISTOBJ pItem;
|
|
|
|
if (! (pItem = ALLOC_PARSER_MEM(pParserData, sizeof(LISTOBJ))) ||
|
|
! (pItem->pstrName = PstrParseString(pParserData, &pParserData->Value)))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
pItem->pNext = pParserData->pOrderDep;
|
|
pParserData->pOrderDep = pItem;
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Parse QueryOrderDependency entries
|
|
//
|
|
|
|
PPDERROR
|
|
IQueryOrderDepProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PLISTOBJ pItem;
|
|
|
|
if (! (pItem = ALLOC_PARSER_MEM(pParserData, sizeof(LISTOBJ))) ||
|
|
! (pItem->pstrName = PstrParseString(pParserData, &pParserData->Value)))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
pItem->pNext = pParserData->pQueryOrderDep;
|
|
pParserData->pQueryOrderDep = pItem;
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies memory configuration information
|
|
//
|
|
|
|
PPDERROR
|
|
IVMOptionProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PMEMOBJ pOption;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_MEMOPTION)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pOption->dwFreeVM)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
return IParseInteger(pParserData, &pOption->dwFreeVM);
|
|
}
|
|
|
|
//
|
|
// Specifies font cache size information
|
|
//
|
|
|
|
PPDERROR
|
|
IFCacheSizeProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PMEMOBJ pOption;
|
|
|
|
if (! (pOption = PvCreateOptionItem(pParserData, GID_MEMOPTION)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pOption->dwFontMem)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
return IParseInteger(pParserData, &pOption->dwFontMem);
|
|
}
|
|
|
|
//
|
|
// Specifies the minimum amount of free VM
|
|
//
|
|
|
|
PPDERROR
|
|
IFreeVMProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInteger(pParserData, &pParserData->dwFreeMem);
|
|
}
|
|
|
|
//
|
|
// Include another file
|
|
//
|
|
|
|
PPDERROR
|
|
IIncludeProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
#define MAX_INCLUDE_LEVEL 10
|
|
|
|
{
|
|
WCHAR awchFilename[MAX_PATH];
|
|
PFILEOBJ pPreviousFile;
|
|
PPDERROR iStatus;
|
|
|
|
if (pParserData->iIncludeLevel >= MAX_INCLUDE_LEVEL)
|
|
{
|
|
ERR(("There appears to be recursive *Include.\n"));
|
|
return PPDERR_FILE;
|
|
}
|
|
|
|
if (! MultiByteToWideChar(CP_ACP, 0, pParserData->pstrValue, -1, awchFilename, MAX_PATH))
|
|
return ISyntaxError(pParserData->pFile, "Invalid include filename");
|
|
|
|
VERBOSE(("Including file %ws ...\n", awchFilename));
|
|
|
|
pPreviousFile = pParserData->pFile;
|
|
pParserData->iIncludeLevel++;
|
|
|
|
iStatus = IParseFile(pParserData, awchFilename);
|
|
|
|
pParserData->iIncludeLevel--;
|
|
pParserData->pFile = pPreviousFile;
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
//
|
|
// Specifies the printer description file format version number
|
|
//
|
|
|
|
PPDERROR
|
|
IPPDAdobeProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseVersionNumber(pParserData, &pParserData->dwSpecVersion);
|
|
}
|
|
|
|
//
|
|
// Specifies the printer description file format version number
|
|
//
|
|
|
|
PPDERROR
|
|
IFormatVersionProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
if (pParserData->dwSpecVersion != 0)
|
|
return PPDERR_NONE;
|
|
|
|
return IParseVersionNumber(pParserData, &pParserData->dwSpecVersion);
|
|
}
|
|
|
|
//
|
|
// Specifies the PPD file version number
|
|
//
|
|
|
|
PPDERROR
|
|
IFileVersionProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseVersionNumber(pParserData, &pParserData->dwPpdFilever);
|
|
}
|
|
|
|
//
|
|
// Specifies the protocols supported by the device
|
|
//
|
|
|
|
PPDERROR
|
|
IProtocolsProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE ProtocolStrs[] =
|
|
{
|
|
"PJL", PROTOCOL_PJL,
|
|
"BCP", PROTOCOL_BCP,
|
|
"TBCP", PROTOCOL_TBCP,
|
|
"SIC", PROTOCOL_SIC,
|
|
NULL, 0
|
|
};
|
|
|
|
CHAR achWord[MAX_WORD_LEN];
|
|
DWORD dwProtocol;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
while (BFindNextWord(&pstr, achWord))
|
|
{
|
|
if (BSearchStrTable(ProtocolStrs, achWord, &dwProtocol))
|
|
pParserData->dwProtocols |= dwProtocol;
|
|
else
|
|
TERSE(("Unknown protocol: %s\n", achWord));
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies whether the device supports color output
|
|
//
|
|
|
|
PPDERROR
|
|
IColorDeviceProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseBoolean(pParserData, &pParserData->dwColorDevice);
|
|
}
|
|
|
|
//
|
|
// Specifies whether the device fonts already have the Euro
|
|
//
|
|
|
|
PPDERROR
|
|
IHasEuroProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PPDERROR rc;
|
|
|
|
if (rc = IParseBoolean(pParserData, &pParserData->bHasEuro) != PPDERR_NONE)
|
|
return rc;
|
|
|
|
pParserData->bEuroInformationSet = TRUE;
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies whether the device fonts already have the Euro
|
|
//
|
|
|
|
PPDERROR
|
|
ITrueGrayProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseBoolean(pParserData, &pParserData->bTrueGray);
|
|
}
|
|
|
|
//
|
|
// Specifies the language extensions supported by the device
|
|
//
|
|
|
|
PPDERROR
|
|
IExtensionsProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE ExtensionStrs[] =
|
|
{
|
|
"DPS", LANGEXT_DPS,
|
|
"CMYK", LANGEXT_CMYK,
|
|
"Composite", LANGEXT_COMPOSITE,
|
|
"FileSystem", LANGEXT_FILESYSTEM,
|
|
NULL, 0
|
|
};
|
|
|
|
CHAR achWord[MAX_WORD_LEN];
|
|
INT dwExtension;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
while (BFindNextWord(&pstr, achWord))
|
|
{
|
|
if (BSearchStrTable(ExtensionStrs, achWord, &dwExtension))
|
|
pParserData->dwExtensions |= dwExtension;
|
|
else
|
|
TERSE(("Unknown extension: %s\n", achWord));
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies whether the device has a file system on disk
|
|
//
|
|
|
|
PPDERROR
|
|
IFileSystemProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
DWORD dwFileSystem;
|
|
PPDERROR iStatus;
|
|
|
|
if ((iStatus = IParseBoolean(pParserData, &dwFileSystem)) == PPDERR_NONE)
|
|
{
|
|
if (dwFileSystem)
|
|
pParserData->dwExtensions |= LANGEXT_FILESYSTEM;
|
|
else
|
|
pParserData->dwExtensions &= ~LANGEXT_FILESYSTEM;
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
//
|
|
// Specifies the device name
|
|
//
|
|
|
|
PPDERROR
|
|
INickNameProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
//
|
|
// Use NickName only if ShortNickName entry is not present
|
|
//
|
|
|
|
if (pParserData->NickName.pvData == NULL)
|
|
return IParseInvocation(pParserData, &pParserData->NickName);
|
|
else
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the short device name
|
|
//
|
|
|
|
PPDERROR
|
|
IShortNameProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
pParserData->NickName.dwLength = 0;
|
|
pParserData->NickName.pvData = NULL;
|
|
|
|
return IParseInvocation(pParserData, &pParserData->NickName);
|
|
}
|
|
|
|
//
|
|
// Specifies the PostScript language level
|
|
//
|
|
|
|
PPDERROR
|
|
ILangLevelProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInteger(pParserData, &pParserData->dwLangLevel);
|
|
}
|
|
|
|
//
|
|
// Specifies PPD language encoding options
|
|
//
|
|
|
|
PPDERROR
|
|
ILangEncProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
//
|
|
// NOTE: Only the following two language encodings are supported because
|
|
// the rest of them are not used (according to our discussions with Adobe).
|
|
// In any case, we don't have any direct way of translating ANSI strings
|
|
// in other encodings to Unicode.
|
|
//
|
|
// A possible future PPD extension is to allow Unicode encoding directly
|
|
// in translation strings.
|
|
//
|
|
|
|
static const STRTABLE LangEncStrs[] =
|
|
{
|
|
"ISOLatin1", LANGENC_ISOLATIN1,
|
|
"WindowsANSI", LANGENC_ISOLATIN1, // WindowsANSI means CharSet=0, which is now page 1252->ISO Latin1
|
|
"None", LANGENC_NONE,
|
|
"Unicode", LANGENC_UNICODE,
|
|
"JIS83-RKSJ", LANGENC_JIS83_RKSJ,
|
|
NULL, LANGENC_NONE
|
|
};
|
|
|
|
if (! BSearchStrTable(LangEncStrs, pParserData->pstrValue, &pParserData->dwLangEncoding))
|
|
return ISyntaxError(pParserData->pFile, "Unsupported LanguageEncoding keyword");
|
|
else
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Identifies the natural language used in the PPD file
|
|
//
|
|
|
|
PPDERROR
|
|
ILangVersProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE LangVersionStrs[] = {
|
|
|
|
"English", LANGENC_ISOLATIN1,
|
|
"Danish", LANGENC_ISOLATIN1,
|
|
"Dutch", LANGENC_ISOLATIN1,
|
|
"Finnish", LANGENC_ISOLATIN1,
|
|
"French", LANGENC_ISOLATIN1,
|
|
"German", LANGENC_ISOLATIN1,
|
|
"Italian", LANGENC_ISOLATIN1,
|
|
"Norwegian", LANGENC_ISOLATIN1,
|
|
"Portuguese", LANGENC_ISOLATIN1,
|
|
"Spanish", LANGENC_ISOLATIN1,
|
|
"Swedish", LANGENC_ISOLATIN1,
|
|
"Japanese", LANGENC_JIS83_RKSJ,
|
|
"Chinese", LANGENC_NONE,
|
|
"Russian", LANGENC_NONE,
|
|
|
|
NULL, LANGENC_NONE
|
|
};
|
|
|
|
if (pParserData->dwLangEncoding == LANGENC_NONE &&
|
|
! BSearchStrTable(LangVersionStrs, pParserData->pstrValue, &pParserData->dwLangEncoding))
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Unsupported LanguageVersion keyword");
|
|
}
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the available TrueType rasterizer options
|
|
//
|
|
|
|
PPDERROR
|
|
ITTRasterizerProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE RasterizerStrs[] =
|
|
{
|
|
"None", TTRAS_NONE,
|
|
"Accept68K", TTRAS_ACCEPT68K,
|
|
"Type42", TTRAS_TYPE42,
|
|
"TrueImage", TTRAS_TRUEIMAGE,
|
|
NULL, TTRAS_NONE
|
|
};
|
|
|
|
if (! BSearchStrTable(RasterizerStrs, pParserData->pstrValue, &pParserData->dwTTRasterizer))
|
|
return ISyntaxError(pParserData->pFile, "Unknown TTRasterizer option");
|
|
else
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the exitserver invocation string
|
|
//
|
|
|
|
PPDERROR
|
|
IExitServerProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInvocation(pParserData, &pParserData->ExitServer);
|
|
}
|
|
|
|
//
|
|
// Specifies the password string
|
|
//
|
|
|
|
PPDERROR
|
|
IPasswordProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInvocation(pParserData, &pParserData->Password);
|
|
}
|
|
|
|
//
|
|
// Specifies the PatchFile invocation string
|
|
//
|
|
|
|
PPDERROR
|
|
IPatchFileProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInvocation(pParserData, &pParserData->PatchFile);
|
|
}
|
|
|
|
//
|
|
// Specifies JobPatchFile invocation strings
|
|
//
|
|
|
|
PPDERROR
|
|
IJobPatchFileProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PJOBPATCHFILEOBJ pItem;
|
|
PSTR pTmp;
|
|
|
|
//
|
|
// Create a new job patch file item
|
|
//
|
|
|
|
if (! (pItem = PvCreateListItem(pParserData,
|
|
(PLISTOBJ *) &pParserData->pJobPatchFiles,
|
|
sizeof(JOBPATCHFILEOBJ),
|
|
"JobPatchFile")))
|
|
{
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Parse the job patch file invocation string
|
|
//
|
|
|
|
if (pItem->Invocation.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// warn if number of patch file is invalid
|
|
//
|
|
|
|
pTmp = pItem->pstrName;
|
|
|
|
if (!BGetIntegerFromString(&pTmp, &pItem->lPatchNo))
|
|
{
|
|
TERSE(("Warning: invalid JobPatchFile number '%s' on line %d\n",
|
|
pParserData->achOption, pParserData->pFile->iLineNumber));
|
|
pItem->lPatchNo = 0;
|
|
}
|
|
|
|
return IParseInvocation(pParserData, &pItem->Invocation);
|
|
}
|
|
|
|
//
|
|
// Specifies PostScript interpreter version and revision number
|
|
//
|
|
|
|
PPDERROR
|
|
IPSVersionProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PSTR pstr = pParserData->Value.pbuf;
|
|
DWORD dwVersion;
|
|
PPDERROR status;
|
|
|
|
//
|
|
// Save the first PSVersion string
|
|
//
|
|
|
|
if ((pParserData->PSVersion.pvData == NULL) &&
|
|
((status = IParseInvocation(pParserData, &pParserData->PSVersion)) != PPDERR_NONE))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Skip non-digit characters
|
|
//
|
|
|
|
while (*pstr && !IS_DIGIT(*pstr))
|
|
pstr++;
|
|
|
|
//
|
|
// Extract the PS interpreter version number
|
|
//
|
|
|
|
dwVersion = 0;
|
|
|
|
while (*pstr && IS_DIGIT(*pstr))
|
|
dwVersion = dwVersion * 10 + (*pstr++ - '0');
|
|
|
|
if (dwVersion > 0)
|
|
{
|
|
//
|
|
// Remember the lowest PSVersion number
|
|
//
|
|
|
|
if (pParserData->dwPSVersion == 0 || pParserData->dwPSVersion > dwVersion)
|
|
pParserData->dwPSVersion = dwVersion;
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
else
|
|
return ISyntaxError(pParserData->pFile, "Invalid PSVersion entry");
|
|
}
|
|
|
|
//
|
|
// Specifies the Product string
|
|
//
|
|
|
|
PPDERROR
|
|
IProductProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
//
|
|
// only store the first *Product entry, though there may be multiple
|
|
//
|
|
|
|
if (pParserData->Product.dwLength != 0)
|
|
return PPDERR_NONE;
|
|
|
|
return IParseInvocation(pParserData, &pParserData->Product);
|
|
}
|
|
|
|
//
|
|
// Specifies the default job timeout value
|
|
//
|
|
|
|
PPDERROR
|
|
IJobTimeoutProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInteger(pParserData, &pParserData->dwJobTimeout);
|
|
}
|
|
|
|
//
|
|
// Specifies the default wait timeout value
|
|
//
|
|
|
|
PPDERROR
|
|
IWaitTimeoutProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInteger(pParserData, &pParserData->dwWaitTimeout);
|
|
}
|
|
|
|
//
|
|
// Specifies whether error handler should be enabled by default
|
|
//
|
|
|
|
PPDERROR
|
|
IPrintPSErrProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
DWORD dwValue;
|
|
|
|
if (IParseBoolean(pParserData, &dwValue) != PPDERR_NONE)
|
|
return PPDERR_SYNTAX;
|
|
|
|
if (dwValue)
|
|
pParserData->dwPpdFlags |= PPDFLAG_PRINTPSERROR;
|
|
else
|
|
pParserData->dwPpdFlags &= ~PPDFLAG_PRINTPSERROR;
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies PJL commands to start a job
|
|
//
|
|
|
|
PPDERROR
|
|
IJCLBeginProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
pParserData->dwPpdFlags |= PPDFLAG_HAS_JCLBEGIN;
|
|
return IParseInvocation(pParserData, &pParserData->JclBegin);
|
|
}
|
|
|
|
//
|
|
// Specifies PJL commands to switch into PostScript language
|
|
//
|
|
|
|
PPDERROR
|
|
IJCLToPSProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
pParserData->dwPpdFlags |= PPDFLAG_HAS_JCLENTERPS;
|
|
return IParseInvocation(pParserData, &pParserData->JclEnterPS);
|
|
}
|
|
|
|
//
|
|
// Specifies PJL commands to end a job
|
|
//
|
|
|
|
PPDERROR
|
|
IJCLEndProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
pParserData->dwPpdFlags |= PPDFLAG_HAS_JCLEND;
|
|
return IParseInvocation(pParserData, &pParserData->JclEnd);
|
|
}
|
|
|
|
//
|
|
// Specifies the default landscape orientation mode
|
|
//
|
|
|
|
PPDERROR
|
|
ILSOrientProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE LsoStrs[] =
|
|
{
|
|
"Any", LSO_ANY,
|
|
"Plus90", LSO_PLUS90,
|
|
"Minus90", LSO_MINUS90,
|
|
NULL, LSO_ANY
|
|
};
|
|
|
|
if (! BSearchStrTable(LsoStrs, pParserData->pstrValue, &pParserData->dwLSOrientation))
|
|
return ISyntaxError(pParserData->pFile, "Unrecognized landscape orientation");
|
|
else
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
|
|
|
|
PPAPEROBJ
|
|
PCreateCustomSizeOption(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a CustomPageSize option for PageSize feature (if necessary)
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
|
|
Return Value:
|
|
|
|
Pointer to newly created CustomPageSize option item or
|
|
the existing CustomPageSize option item if it already exists.
|
|
|
|
NULL if there is an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPAPEROBJ pCustomSize;
|
|
BUFOBJ SavedBuffer;
|
|
|
|
//
|
|
// Create an item corresponding to *PageSize feature if needed
|
|
//
|
|
|
|
SavedBuffer = pParserData->Option;
|
|
pParserData->Option.pbuf = (PBYTE) gstrCustomSizeKwd;
|
|
pParserData->Option.dwSize = strlen(gstrCustomSizeKwd);
|
|
|
|
pCustomSize = PvCreateOptionItem(pParserData, GID_PAGESIZE);
|
|
|
|
pParserData->Option = SavedBuffer;
|
|
|
|
return pCustomSize;;
|
|
}
|
|
|
|
//
|
|
// Specifies custom paper size invocation string
|
|
//
|
|
|
|
PPDERROR
|
|
ICustomSizeProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PPAPEROBJ pCustomSize;
|
|
|
|
if (strcmp(pParserData->achOption, gstrTrueKwd) != EQUAL_STRING)
|
|
{
|
|
ISyntaxError(pParserData->pFile, "Invalid *CustomPageSize option");
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
if (! (pCustomSize = PCreateCustomSizeOption(pParserData)))
|
|
return PPDERR_MEMORY;
|
|
|
|
if (pCustomSize->Option.Invocation.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
return IParseInvocation(pParserData, &pCustomSize->Option.Invocation);
|
|
}
|
|
|
|
//
|
|
// Specifies custom paper size parameters
|
|
//
|
|
|
|
PPDERROR
|
|
IParamCustomProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
static const STRTABLE CustomParamStrs[] =
|
|
{
|
|
"Width", CUSTOMPARAM_WIDTH,
|
|
"Height", CUSTOMPARAM_HEIGHT,
|
|
"WidthOffset", CUSTOMPARAM_WIDTHOFFSET,
|
|
"HeightOffset", CUSTOMPARAM_HEIGHTOFFSET,
|
|
"Orientation", CUSTOMPARAM_ORIENTATION,
|
|
NULL, 0
|
|
};
|
|
|
|
CHAR achWord[MAX_WORD_LEN];
|
|
LONG lMinVal, lMaxVal;
|
|
INT iType;
|
|
DWORD dwParam;
|
|
LONG lOrder;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
//
|
|
// The format for a ParamCustomPageSize entry:
|
|
// ParameterName Order Type MinVal MaxVal
|
|
//
|
|
|
|
if (! BSearchStrTable(CustomParamStrs, pParserData->achOption, &dwParam) ||
|
|
! BGetIntegerFromString(&pstr, &lOrder) ||
|
|
! BFindNextWord(&pstr, achWord) ||
|
|
lOrder <= 0 || lOrder > CUSTOMPARAM_MAX)
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Bad *ParamCustomPageSize entry");
|
|
}
|
|
|
|
//
|
|
// Expected type is "int" for Orientation parameter and "points" for other parameters
|
|
//
|
|
|
|
iType = (dwParam == CUSTOMPARAM_ORIENTATION) ?
|
|
((strcmp(achWord, "int") == EQUAL_STRING) ? FLTYPE_INT : FLTYPE_ERROR) :
|
|
((strcmp(achWord, "points") == EQUAL_STRING) ? FLTYPE_POINT : FLTYPE_ERROR);
|
|
|
|
if (iType == FLTYPE_ERROR ||
|
|
! BGetFloatFromString(&pstr, &lMinVal, iType) ||
|
|
! BGetFloatFromString(&pstr, &lMaxVal, iType) ||
|
|
lMinVal > lMaxVal)
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Bad *ParamCustomPageSize entry");
|
|
}
|
|
|
|
pParserData->CustomSizeParams[dwParam].dwOrder = lOrder;
|
|
pParserData->CustomSizeParams[dwParam].lMinVal = lMinVal;
|
|
pParserData->CustomSizeParams[dwParam].lMaxVal = lMaxVal;
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the maximum height of custom paper size
|
|
//
|
|
|
|
PPDERROR
|
|
IMaxWidthProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PPAPEROBJ pCustomSize;
|
|
LONG lValue;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
if (! BGetFloatFromString(&pstr, &lValue, FLTYPE_POINT) || lValue <= 0)
|
|
return ISyntaxError(pParserData->pFile, "Invalid media width");
|
|
|
|
if (! (pCustomSize = PCreateCustomSizeOption(pParserData)))
|
|
return PPDERR_MEMORY;
|
|
|
|
pCustomSize->szDimension.cx = lValue;
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the maximum height of custom paper size
|
|
//
|
|
|
|
PPDERROR
|
|
IMaxHeightProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PPAPEROBJ pCustomSize;
|
|
LONG lValue;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
if (! BGetFloatFromString(&pstr, &lValue, FLTYPE_POINT) || lValue <= 0)
|
|
return ISyntaxError(pParserData->pFile, "Invalid media height");
|
|
|
|
if (! (pCustomSize = PCreateCustomSizeOption(pParserData)))
|
|
return PPDERR_MEMORY;
|
|
|
|
pCustomSize->szDimension.cy = lValue;
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Specifies the hardware margins on cut-sheet devices
|
|
//
|
|
|
|
PPDERROR
|
|
IHWMarginsProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PPAPEROBJ pCustomSize;
|
|
RECT rc;
|
|
PSTR pstr = pParserData->pstrValue;
|
|
|
|
//
|
|
// Parse hardware margins: left, bottom, right, top
|
|
//
|
|
|
|
if (! BGetFloatFromString(&pstr, &rc.left, FLTYPE_POINT) ||
|
|
! BGetFloatFromString(&pstr, &rc.bottom, FLTYPE_POINT) ||
|
|
! BGetFloatFromString(&pstr, &rc.right, FLTYPE_POINT) ||
|
|
! BGetFloatFromString(&pstr, &rc.top, FLTYPE_POINT))
|
|
{
|
|
return ISyntaxError(pParserData->pFile, "Invalid HWMargins");
|
|
}
|
|
|
|
if (! (pCustomSize = PCreateCustomSizeOption(pParserData)))
|
|
return PPDERR_MEMORY;
|
|
|
|
pCustomSize->rcImageArea = rc;
|
|
|
|
//
|
|
// The presence of HWMargins entry indicates the device supports cut-sheet
|
|
//
|
|
|
|
pParserData->dwCustomSizeFlags |= CUSTOMSIZE_CUTSHEET;
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Function to process *CenterRegistered entry
|
|
//
|
|
|
|
PPDERROR
|
|
ICenterRegProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
DWORD dwValue;
|
|
|
|
if (IParseBoolean(pParserData, &dwValue) != PPDERR_NONE)
|
|
return PPDERR_SYNTAX;
|
|
|
|
if (dwValue)
|
|
pParserData->dwCustomSizeFlags |= CUSTOMSIZE_CENTERREG;
|
|
else
|
|
pParserData->dwCustomSizeFlags &= ~CUSTOMSIZE_CENTERREG;
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Function to process *ADORequiresEExec entry
|
|
//
|
|
|
|
PPDERROR
|
|
IReqEExecProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
DWORD dwValue;
|
|
|
|
if (IParseBoolean(pParserData, &dwValue) != PPDERR_NONE)
|
|
return PPDERR_SYNTAX;
|
|
|
|
if (dwValue)
|
|
pParserData->dwPpdFlags |= PPDFLAG_REQEEXEC;
|
|
else
|
|
pParserData->dwPpdFlags &= ~PPDFLAG_REQEEXEC;
|
|
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Function to process *ADOTTFontSub entry
|
|
//
|
|
|
|
PPDERROR
|
|
ITTFontSubProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PTTFONTSUB pTTFontSub;
|
|
|
|
//
|
|
// Create a new font substitution item
|
|
//
|
|
|
|
if (! (pTTFontSub = PvCreateXlatedItem(
|
|
pParserData,
|
|
&pParserData->pTTFontSubs,
|
|
sizeof(TTFONTSUB))))
|
|
{
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Parse the PS family name
|
|
//
|
|
|
|
if (pTTFontSub->PSName.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
if (*pParserData->pstrValue == NUL)
|
|
return ISyntaxError(pParserData->pFile, "Missing TrueType font family name");
|
|
|
|
return IParseInvocation(pParserData, &pTTFontSub->PSName);
|
|
}
|
|
|
|
//
|
|
// Function to process *Throughput entry
|
|
//
|
|
|
|
PPDERROR
|
|
IThroughputProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return IParseInteger(pParserData, &pParserData->dwThroughput);
|
|
}
|
|
|
|
//
|
|
// Function to ignore the current entry
|
|
//
|
|
|
|
PPDERROR
|
|
INullProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
//
|
|
// Define a named symbol
|
|
//
|
|
|
|
PPDERROR
|
|
ISymbolValueProc(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
{
|
|
PSYMBOLOBJ pSymbol;
|
|
|
|
if (pParserData->dwValueType == VALUETYPE_SYMBOL)
|
|
return ISyntaxError(pParserData->pFile, "Symbol value cannot be another symbol");
|
|
|
|
//
|
|
// Create a new symbol item
|
|
//
|
|
|
|
if (! (pSymbol = PvCreateListItem(pParserData,
|
|
(PLISTOBJ *) &pParserData->pSymbols,
|
|
sizeof(SYMBOLOBJ),
|
|
"Symbol")))
|
|
{
|
|
return PPDERR_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Parse the symbol value
|
|
//
|
|
|
|
if (pSymbol->Invocation.pvData)
|
|
{
|
|
WARN_DUPLICATE();
|
|
return PPDERR_NONE;
|
|
}
|
|
|
|
return IParseInvocation(pParserData, &pSymbol->Invocation);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Built-in keyword table
|
|
//
|
|
|
|
const CHAR gstrDefault[] = "Default";
|
|
const CHAR gstrPageSizeKwd[] = "PageSize";
|
|
const CHAR gstrInputSlotKwd[] = "InputSlot";
|
|
const CHAR gstrManualFeedKwd[] = "ManualFeed";
|
|
const CHAR gstrCustomSizeKwd[] = "CustomPageSize";
|
|
const CHAR gstrLetterSizeKwd[] = "Letter";
|
|
const CHAR gstrA4SizeKwd[] = "A4";
|
|
const CHAR gstrLongKwd[] = "Long";
|
|
const CHAR gstrShortKwd[] = "Short";
|
|
const CHAR gstrTrueKwd[] = "True";
|
|
const CHAR gstrFalseKwd[] = "False";
|
|
const CHAR gstrOnKwd[] = "On";
|
|
const CHAR gstrOffKwd[] = "Off";
|
|
const CHAR gstrNoneKwd[] = "None";
|
|
const CHAR gstrVMOptionKwd[] = "VMOption";
|
|
const CHAR gstrInstallMemKwd[] = "InstalledMemory";
|
|
const CHAR gstrDuplexTumble[] = "DuplexTumble";
|
|
const CHAR gstrDuplexNoTumble[] = "DuplexNoTumble";
|
|
|
|
const KWDENTRY gPpdBuiltInKeywordTable[] =
|
|
{
|
|
{ gstrPageSizeKwd, NULL, GENERIC_ENTRY(GID_PAGESIZE) },
|
|
{ "PageRegion", NULL, GENERIC_ENTRY(GID_PAGEREGION) },
|
|
{ gstrInputSlotKwd, NULL, GENERIC_ENTRY(GID_INPUTSLOT) },
|
|
{ "MediaType", NULL, GENERIC_ENTRY(GID_MEDIATYPE) },
|
|
{ "OutputBin", NULL, GENERIC_ENTRY(GID_OUTPUTBIN) },
|
|
{ "Collate", NULL, GENERIC_ENTRY(GID_COLLATE) },
|
|
{ "Resolution", NULL, GENERIC_ENTRY(GID_RESOLUTION) },
|
|
{ "InstalledMemory", NULL, GENERIC_ENTRY(GID_MEMOPTION) },
|
|
{ "LeadingEdge", NULL, GENERIC_ENTRY(GID_LEADINGEDGE) },
|
|
{ "UseHWMargins", NULL, GENERIC_ENTRY(GID_USEHWMARGINS) },
|
|
|
|
{ "Duplex", IDuplexProc, INVOCA_VALUE | REQ_OPTION },
|
|
{ "DefaultDuplex", IDefaultDuplexProc, STRING_VALUE },
|
|
{ "PaperDimension", IPaperDimProc, QUOTED_NOHEX | REQ_OPTION },
|
|
{ "ImageableArea", IImageAreaProc, QUOTED_NOHEX | REQ_OPTION },
|
|
{ "RequiresPageRegion", IReqPageRgnProc, STRING_VALUE | REQ_OPTION },
|
|
{ gstrManualFeedKwd, IManualFeedProc, INVOCA_VALUE | REQ_OPTION },
|
|
{ "DefaultManualFeed", IDefManualFeedProc, STRING_VALUE },
|
|
{ "PageStackOrder", IPageStackOrderProc, STRING_VALUE | REQ_OPTION },
|
|
{ "DefaultOutputOrder", IDefOutputOrderProc, STRING_VALUE },
|
|
{ "JCLResolution", IJCLResProc, INVOCA_VALUE | REQ_OPTION | ALLOW_HEX },
|
|
{ "DefaultJCLResolution", IDefaultJCLResProc, STRING_VALUE },
|
|
{ "SetResolution", ISetResProc, INVOCA_VALUE | REQ_OPTION },
|
|
{ "ScreenAngle", IScreenAngleProc, QUOTED_VALUE },
|
|
{ "ScreenFreq", IScreenFreqProc, QUOTED_VALUE },
|
|
{ "ResScreenAngle", IResScreenAngleProc, QUOTED_NOHEX | REQ_OPTION },
|
|
{ "ResScreenFreq", IResScreenFreqProc, QUOTED_NOHEX | REQ_OPTION },
|
|
{ "Font", IFontProc, STRING_VALUE | REQ_OPTION },
|
|
{ "DefaultFont", IDefaultFontProc, STRING_VALUE },
|
|
{ "OpenUI", IOpenUIProc, STRING_VALUE | REQ_OPTION },
|
|
{ "CloseUI", ICloseUIProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "JCLOpenUI", IOpenUIProc, STRING_VALUE | REQ_OPTION },
|
|
{ "JCLCloseUI", ICloseUIProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "OrderDependency", IOrderDepProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "UIConstraints", IUIConstraintsProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "QueryOrderDependency", IQueryOrderDepProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "NonUIOrderDependency", IOrderDepProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "NonUIConstraints", IUIConstraintsProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "VMOption", IVMOptionProc, QUOTED_NOHEX | REQ_OPTION },
|
|
{ "FCacheSize", IFCacheSizeProc, STRING_VALUE | REQ_OPTION },
|
|
{ "FreeVM", IFreeVMProc, QUOTED_VALUE },
|
|
{ "OpenGroup", IOpenGroupProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "CloseGroup", ICloseGroupProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "OpenSubGroup", IOpenSubGroupProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "CloseSubGroup", ICloseSubGroupProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "Include", IIncludeProc, QUOTED_VALUE | ALLOW_MULTI },
|
|
{ "PPD-Adobe", IPPDAdobeProc, QUOTED_VALUE },
|
|
{ "FormatVersion", IFormatVersionProc, QUOTED_VALUE },
|
|
{ "FileVersion", IFileVersionProc, QUOTED_VALUE },
|
|
{ "ColorDevice", IColorDeviceProc, STRING_VALUE },
|
|
{ "Protocols", IProtocolsProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "Extensions", IExtensionsProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "FileSystem", IFileSystemProc, STRING_VALUE },
|
|
{ "NickName", INickNameProc, QUOTED_VALUE },
|
|
{ "ShortNickName", IShortNameProc, QUOTED_VALUE },
|
|
{ "LanguageLevel", ILangLevelProc, QUOTED_NOHEX },
|
|
{ "LanguageEncoding", ILangEncProc, STRING_VALUE },
|
|
{ "LanguageVersion", ILangVersProc, STRING_VALUE },
|
|
{ "TTRasterizer", ITTRasterizerProc, STRING_VALUE },
|
|
{ "ExitServer", IExitServerProc, INVOCA_VALUE },
|
|
{ "Password", IPasswordProc, INVOCA_VALUE },
|
|
{ "PatchFile", IPatchFileProc, INVOCA_VALUE },
|
|
{ "JobPatchFile", IJobPatchFileProc, INVOCA_VALUE | REQ_OPTION },
|
|
{ "PSVersion", IPSVersionProc, QUOTED_NOHEX | ALLOW_MULTI },
|
|
{ "ModelName", INullProc, QUOTED_VALUE },
|
|
{ "Product", IProductProc, QUOTED_NOHEX | ALLOW_MULTI },
|
|
{ "SuggestedJobTimeout", IJobTimeoutProc, QUOTED_VALUE },
|
|
{ "SuggestedWaitTimeout", IWaitTimeoutProc, QUOTED_VALUE },
|
|
{ "PrintPSErrors", IPrintPSErrProc, STRING_VALUE },
|
|
{ "JCLBegin", IJCLBeginProc, QUOTED_VALUE },
|
|
{ "JCLToPSInterpreter", IJCLToPSProc, QUOTED_VALUE },
|
|
{ "JCLEnd", IJCLEndProc, QUOTED_VALUE },
|
|
{ "LandscapeOrientation", ILSOrientProc, STRING_VALUE },
|
|
{ gstrCustomSizeKwd, ICustomSizeProc, INVOCA_VALUE | REQ_OPTION },
|
|
{ "ParamCustomPageSize", IParamCustomProc, STRING_VALUE | REQ_OPTION },
|
|
{ "MaxMediaWidth", IMaxWidthProc, QUOTED_VALUE },
|
|
{ "MaxMediaHeight", IMaxHeightProc, QUOTED_VALUE },
|
|
{ "HWMargins", IHWMarginsProc, STRING_VALUE },
|
|
{ "CenterRegistered", ICenterRegProc, STRING_VALUE },
|
|
{ "ADORequiresEExec", IReqEExecProc, STRING_VALUE },
|
|
{ "ADOTTFontSub", ITTFontSubProc, QUOTED_VALUE | REQ_OPTION },
|
|
{ "ADTrueGray", ITrueGrayProc, STRING_VALUE },
|
|
{ "ADHasEuro", IHasEuroProc, STRING_VALUE },
|
|
{ "Throughput", IThroughputProc, QUOTED_NOHEX },
|
|
{ "SymbolValue", ISymbolValueProc, INVOCA_VALUE | REQ_OPTION },
|
|
{ "Status", INullProc, QUOTED_VALUE | ALLOW_MULTI },
|
|
{ "PrinterError", INullProc, QUOTED_VALUE | ALLOW_MULTI },
|
|
{ "SymbolLength", INullProc, STRING_VALUE | REQ_OPTION },
|
|
{ "SymbolEnd", INullProc, STRING_VALUE | ALLOW_MULTI },
|
|
{ "End", INullProc, VALUETYPE_NONE | ALLOW_MULTI },
|
|
};
|
|
|
|
#define NUM_BUILTIN_KEYWORDS (sizeof(gPpdBuiltInKeywordTable) / sizeof(KWDENTRY))
|
|
|
|
|
|
|
|
DWORD
|
|
DwHashKeyword(
|
|
PSTR pstrKeyword
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compute the hash value for the specified keyword string
|
|
|
|
Arguments:
|
|
|
|
pstrKeyword - Pointer to the keyword string to be hashed
|
|
|
|
Return Value:
|
|
|
|
Hash value computed using the specified keyword string
|
|
|
|
--*/
|
|
|
|
{
|
|
PBYTE pub = (PBYTE) pstrKeyword;
|
|
DWORD dwHashValue = 0;
|
|
|
|
while (*pub)
|
|
dwHashValue = (dwHashValue << 1) ^ *pub++;
|
|
|
|
return dwHashValue;
|
|
}
|
|
|
|
|
|
|
|
PKWDENTRY
|
|
PSearchKeywordTable(
|
|
PPARSERDATA pParserData,
|
|
PSTR pstrKeyword,
|
|
INT *piIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if a keyword appears in the built-in keyword table
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
pstrKeyword - Specifies the keyword to be searched
|
|
piIndex - Returns the index of the entry in the built-in keyword
|
|
table corresponding to the specified keyword.
|
|
|
|
Return Value:
|
|
|
|
Pointer to the entry in the built-in table corresponding to the
|
|
specified keyword. NULL if the specified keyword is not supported.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwHashValue;
|
|
INT iIndex;
|
|
|
|
ASSERT(pstrKeyword != NULL);
|
|
dwHashValue = DwHashKeyword(pstrKeyword);
|
|
|
|
for (iIndex = 0; iIndex < NUM_BUILTIN_KEYWORDS; iIndex++)
|
|
{
|
|
if (pParserData->pdwKeywordHashs[iIndex] == dwHashValue &&
|
|
strcmp(gPpdBuiltInKeywordTable[iIndex].pstrKeyword, pstrKeyword) == EQUAL_STRING)
|
|
{
|
|
*piIndex = iIndex;
|
|
return (PKWDENTRY) &gPpdBuiltInKeywordTable[iIndex];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BInitKeywordLookup(
|
|
PPARSERDATA pParserData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build up data structures to speed up keyword lookup
|
|
|
|
Arguments:
|
|
|
|
pParserData - Points to parser data structure
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD iIndex, iCount;
|
|
|
|
//
|
|
// Allocate memory to hold extra data structures
|
|
//
|
|
|
|
iCount = NUM_BUILTIN_KEYWORDS;
|
|
pParserData->pdwKeywordHashs = ALLOC_PARSER_MEM(pParserData, iCount * sizeof(DWORD));
|
|
pParserData->pubKeywordCounts = ALLOC_PARSER_MEM(pParserData, iCount * sizeof(BYTE));
|
|
|
|
if (!pParserData->pdwKeywordHashs || !pParserData->pubKeywordCounts)
|
|
{
|
|
ERR(("Memory allocation failed: %d\n", GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Precompute the hash values for built-in keywords
|
|
//
|
|
|
|
for (iIndex = 0; iIndex < iCount; iIndex++)
|
|
{
|
|
pParserData->pdwKeywordHashs[iIndex] =
|
|
DwHashKeyword((PSTR) gPpdBuiltInKeywordTable[iIndex].pstrKeyword);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|