Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2279 lines
51 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
ppd.c
Abstract:
PostScript driver PPD parser external interface module.
After a PPD file is parsed, a binary version is generated and
saved in a BPD file. From then on, we can load the binary
version from the BPD file directly without going through the
parser every time.
[Notes:]
We assume NULL is 0 and pointers are the same size as DWORD.
If that assumption is not true, then the packing and unpacking
functions must be changed.
Revision History:
07/17/95 -davidx-
Created it.
mm/dd/yy -author-
description
--*/
#include "pslib.h"
#include "regdata.h"
#include "ppdfile.h"
#include "ppdparse.h"
#include "ppdchar.h"
#include "ppdkwd.h"
// Local type definitions
typedef VOID (*UNPACKPROC)(HPPD, PLISTOBJ);
typedef DWORD (*PACKPROC)(PBYTE, DWORD, PLISTOBJ);
// Local function declarations
VOID OffsetToPointer(HPPD, PVOID);
DWORD PackOrderDep(PBYTE, DWORD, HPPD);
DWORD PackUiConstraints(PBYTE, DWORD, HPPD);
HPPD PackPpdData(PPPDOBJ);
VOID UnpackPpdData(HPPD);
VOID
GenerateBpdFilename(
PWSTR pwstrBpdName,
PWSTR pwstrPpdName
)
/*++
Routine Description:
Generate BPD filename corresponding to specified PPD filename
Arguments:
pwstrBpdName Pointer to a buffer for storing BPD filename
pwstrPpdName Pointer to PPD filename
[Note:]
The BPD filename buffer should always be big enough because
we're simply replacing .PPD with .BPD.
Return Value:
NONE
--*/
#define PPD_FILENAME_EXTENSION L"PPD"
#define BPD_FILENAME_EXTENSION L"BPD"
{
PWSTR pwstrExtension = NULL;
// Copy PPD filename to BPD filename buffer
// Find the filename extension in the process
while (*pwstrPpdName != L'\0') {
if ((*pwstrBpdName++ = *pwstrPpdName++) == L'.')
pwstrExtension = pwstrBpdName;
}
// If PPD filename has no extension, then
// append an empty extension instead
if (pwstrExtension == NULL) {
*pwstrBpdName++ = L'.';
pwstrExtension = pwstrBpdName;
}
#if DBG
*pwstrBpdName = L'\0';
if (_wcsicmp(pwstrExtension, PPD_FILENAME_EXTENSION) != EQUAL_STRING) {
DBGPRINT("Unknown PPD filename extension: %ws\n", pwstrExtension);
}
#endif
wcscpy(pwstrExtension, BPD_FILENAME_EXTENSION);
}
HPPD
GetBinaryPpdData(
PWSTR pwstrPpdName
)
/*++
Routine Description:
Read binary PPD data from a BPD file
Arguments:
pwstrPpdName Pointer to PPD filename
Return Value:
Handle to binary PPD data if successful.
NULL if an error occured.
--*/
{
WCHAR bpdFilename[MAX_PATH];
HANDLE hmodule;
PBYTE pdata;
DWORD size;
HPPD hppd;
// Figure out the BPD filename corresponding to specified
// PPD filename. And attempt to map BPD file into memory.
GenerateBpdFilename(bpdFilename, pwstrPpdName);
#ifndef KERNEL_MODE
// Check if the BPD file is older than the PPD file
// If it is, then we'll throw away the BPD file.
{
FILETIME bpdTime, ppdTime;
HANDLE hPpdFile, hBpdFile;
BOOL bOutOfDate = TRUE;
if ((hPpdFile = CreateFile(pwstrPpdName, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
DBGMSG(DBG_LEVEL_ERROR, "Cannot open PPD file\n");
return NULL;
}
if ((hBpdFile = CreateFile(bpdFilename, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
DBGMSG(DBG_LEVEL_ERROR, "Cannot open BPD file\n");
CloseHandle(hPpdFile);
return NULL;
}
if (GetFileTime(hPpdFile, NULL, NULL, &ppdTime) &&
GetFileTime(hBpdFile, NULL, NULL, &bpdTime))
{
bOutOfDate = CompareFileTime(&bpdTime, &ppdTime) < 0;
}
CloseHandle(hPpdFile);
CloseHandle(hBpdFile);
if (bOutOfDate) {
DBGMSG(DBG_LEVEL_WARNING, "BPD file is out-of-date.\n");
return NULL;
}
}
#endif
if (! MAPFILE(bpdFilename, &hmodule, &pdata, &size)) {
DBGERRMSG("MAPFILE");
return NULL;
}
// Allocate memory to hold binary PPD data
if ((hppd = MEMALLOC(size)) != NULL) {
// If the binary PPD data is valid,
// copy it into the memory buffer
if (size <= sizeof(PPDOBJ) || size != ((HPPD) pdata)->dwDataSize) {
DBGMSG(DBG_LEVEL_ERROR, "Corrupted binary PPD data\n");
MEMFREE(hppd);
hppd = NULL;
} else {
memcpy(hppd, pdata, size);
}
} else {
DBGERRMSG("MEMALLOC");
}
// Unmap the BPD file from memory
FREEMODULE(hmodule);
return hppd;
}
#ifndef KERNEL_MODE
VOID
SaveBinaryPpdData(
PWSTR pwstrPpdName,
HPPD hppd
)
/*++
Routine Description:
Write binary PPD data to a BPD file
Arguments:
pwstrPpdName Pointer to PPD filename
Return Value:
NONE
--*/
{
WCHAR bpdFilename[MAX_PATH];
HANDLE hfile;
DWORD count;
// Figure out the BPD filename corresponding to specified
// PPD filename. And attempt to create the BPD file.
GenerateBpdFilename(bpdFilename, pwstrPpdName);
hfile = CreateFileW(
bpdFilename,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hfile != INVALID_HANDLE_VALUE) {
// Write binary data out to the BPD file
if (! WriteFile(hfile, hppd, hppd->dwDataSize, &count, NULL) ||
count != hppd->dwDataSize)
{
DBGERRMSG("WriteFile");
}
CloseHandle(hfile);
} else {
DBGERRMSG("CreateFileW");
}
}
#endif //!KERNEL_MODE
HPPD
PpdCreate(
PWSTR pwstrFilename
)
/*++
Routine Description:
Load PPD data from the BPD file or parse the PPD file
Arguments:
pwstrFilename PPD filename
Return Value:
Handle to PPD data object
--*/
{
HPPD hppd;
PPPDOBJ ppdobj;
#ifndef STANDALONE
// Try to load PPD data from the BPD file
hppd = GetBinaryPpdData(pwstrFilename);
if (hppd != NULL) {
// Verify version number
if (hppd->wParserVersion != PPD_PARSER_VERSION) {
DBGMSG(DBG_LEVEL_WARNING,
"BPD file out-of-date: version number mismatch.\n");
} else {
// Convert offsets to address pointers
UnpackPpdData(hppd);
if (_wcsicmp(StripDirPrefixW(pwstrFilename),
StripDirPrefixW(hppd->pwstrFilename)) != EQUAL_STRING)
{
// Verify filename
DBGMSG(DBG_LEVEL_WARNING,
"BPD file out-of-date: filename mismatch.\n");
} else {
// The binary PPD data is valid, use it.
return hppd;
}
}
MEMFREE(hppd);
}
DBGMSG(DBG_LEVEL_WARNING, "No binary PPD data exists\n");
#endif
// Couldn't get PPD data from the BPD file.
// We have to parse the PPD file.
if ((ppdobj = PPDOBJ_Create(pwstrFilename)) == NULL) {
DBGERRMSG("PPDOBJ_Create");
return NULL;
}
// Pack the PPDOBJ into a contiguous block of memory
// Pointers are represented as offsets so that data
// can be saved to a BPD file.
if ((hppd = PackPpdData(ppdobj)) == NULL) {
DBGERRMSG("PackPpdData");
return NULL;
}
#if !defined(KERNEL_MODE) && !defined(STANDALONE)
// Save the binary PPD data to a BPD file
// if called from user mode UI DLL
SaveBinaryPpdData(pwstrFilename, hppd);
#endif
// Convert offsets back to pointers. This should never fail because
// we just converted pointers to offsets ourselves.
UnpackPpdData(hppd);
return hppd;
}
VOID
PpdDelete(
HPPD hppd
)
/*++
Routine Description:
Free memory allocated to hold PPD data
Arguments:
hppd Handle to PPD data
Return Value:
NONE
--*/
{
ASSERT(hppd != NULL);
MEMFREE(hppd);
}
///////////////////////////////////////////////////////////////////////////////
// Local functions called by PackPpdData() to pack various objects
///////////////////////////////////////////////////////////////////////////////
DWORD
PackPstr(
PBYTE pbuf,
DWORD offset,
PSTR *ppstr
)
{
if (*ppstr == NULL) {
return 0;
} else {
// Determine the number of bytes we need (DWORD aligned)
DWORD size = strlen(*ppstr) + 1;
size = RoundUpMultiple(size, MemAlignmentSize);
if (pbuf != NULL) {
// If the destination buffer pointer is not NULL,
// copy the character string to destination buffer
strcpy((PSTR) (pbuf + offset), *ppstr);
// Convert address pointer to offset
*ppstr = (PSTR) offset;
}
return size;
}
}
DWORD
PackPwstr(
PBYTE pbuf,
DWORD offset,
PWSTR *ppwstr
)
{
if (*ppwstr == NULL) {
return 0;
} else {
// Determine the number of bytes we need (DWORD aligned)
DWORD size = (wcslen(*ppwstr) + 1) * sizeof(WCHAR);
size = RoundUpMultiple(size, MemAlignmentSize);
if (pbuf != NULL && *ppwstr != NULL) {
// If the destination buffer pointer is not NULL,
// copy the UNICODE string to destination buffer
wcscpy((PWSTR) (pbuf + offset), *ppwstr);
// Convert address pointer to offset
*ppwstr = (PWSTR) offset;
}
return size;
}
}
DWORD
PackListObj(
PBYTE pbuf,
DWORD offset,
PLISTOBJ *pplistobj,
DWORD objsize,
PACKPROC proc
)
{
DWORD curOffset = offset;
PLISTOBJ pitem;
while ((pitem = *pplistobj) != NULL) {
// Pack the current item itself
if (pbuf != NULL) {
memcpy(pbuf + curOffset, pitem, objsize);
pitem = (PLISTOBJ) (pbuf + curOffset);
*pplistobj = (PLISTOBJ) curOffset;
}
curOffset += RoundUpMultiple(objsize, MemAlignmentSize);
// Pack the objects embedded within the current item
ASSERT(pitem->pName != NULL);
curOffset += PackPstr(pbuf, curOffset, & pitem->pName);
curOffset += proc(pbuf, curOffset, pitem);
// Move on to the next item
pplistobj = (PLISTOBJ *) &pitem->pNext;
}
return curOffset - offset;
}
DWORD
PackDevFont(
PBYTE pbuf,
DWORD offset,
PDEVFONT pdevfont
)
{
return PackPstr(pbuf, offset, &pdevfont->pXlation);
}
DWORD
PackMediaOption(
PBYTE pbuf,
DWORD offset,
PMEDIAOPTION pmediaoption
)
{
DWORD curOffset = offset;
PSRECT *pImageableArea;
#if DBG
if (CHECK_DBG_LEVEL(DBG_LEVEL_ERROR) && pbuf == NULL) {
if (pmediaoption->pPageSizeCode == NULL) {
DBGPRINT(
"No PageSize invocation for mediaOption: %s\n",
pmediaoption->pName);
}
if (pmediaoption->pPageRgnCode == NULL) {
DBGPRINT(
"No PageRegion invocation for mediaOption: %s\n",
pmediaoption->pName);
}
}
#endif
// Validate paper dimension and imageable area.
// Only need to do this once on the first pass.
if (pbuf == NULL) {
if (pmediaoption->dimension.width == 0 ||
pmediaoption->dimension.height == 0)
{
DBGMSG1(DBG_LEVEL_WARNING,
"Invalid PaperDimension for mediaOption: %s\n",
pmediaoption->pName);
pmediaoption->dimension.width =
pmediaoption->dimension.height = 0;
}
pImageableArea = & pmediaoption->imageableArea;
if (pImageableArea->right > pmediaoption->dimension.width)
pImageableArea->right = pmediaoption->dimension.width;
if (pImageableArea->top > pmediaoption->dimension.height)
pImageableArea->top = pmediaoption->dimension.height;
if (pImageableArea->left >= pImageableArea->right ||
pImageableArea->bottom >= pImageableArea->top)
{
DBGMSG1(DBG_LEVEL_WARNING,
"Invalid ImageableArea for mediaOption: %s\n",
pmediaoption->pName);
pImageableArea->left = pImageableArea->bottom = 0;
pImageableArea->top = pmediaoption->dimension.height;
}
}
curOffset += PackPstr(pbuf, curOffset, &pmediaoption->pXlation);
curOffset += PackPstr(pbuf, curOffset, &pmediaoption->pPageSizeCode);
curOffset += PackPstr(pbuf, curOffset, &pmediaoption->pPageRgnCode);
return curOffset - offset;
}
DWORD
PackResOption(
PBYTE pbuf,
DWORD offset,
PRESOPTION presoption
)
{
DWORD curOffset = offset;
#if DBG
if (CHECK_DBG_LEVEL(DBG_LEVEL_WARNING) && pbuf == NULL) {
if (presoption->pInvocation == NULL &&
presoption->pJclCode == NULL &&
presoption->pSetResCode == NULL)
{
DBGPRINT(
"No invocation strings for resolutionOption: %s\n",
presoption->pName);
}
}
#endif
curOffset += PackPstr(pbuf, curOffset, &presoption->pXlation);
curOffset += PackPstr(pbuf, curOffset, &presoption->pInvocation);
curOffset += PackPstr(pbuf, curOffset, &presoption->pJclCode);
curOffset += PackPstr(pbuf, curOffset, &presoption->pSetResCode);
return curOffset - offset;
}
DWORD
PackUiOption(
PBYTE pbuf,
DWORD offset,
PUIOPTION puioption
)
{
DWORD curOffset = offset;
curOffset += PackPstr(pbuf, curOffset, &puioption->pXlation);
curOffset += PackPstr(pbuf, curOffset, &puioption->pInvocation);
return curOffset - offset;
}
DWORD
PackUiGroupDefault(
PUIGROUP pUiGroup
)
{
PSTR pDefault = (PSTR) pUiGroup->dwDefault;
if (pUiGroup->uigrpIndex == UIGRP_RESOLUTION) {
LONG res = 0;
// Resolution group is an odd ball. dwDefault field contains
// the device resolution in dpi instead of the option index.
// If there is no default, then use the first resoltion option
if (pDefault == NULL && pUiGroup->pUiOptions != NULL)
pDefault = pUiGroup->pUiOptions->pName;
// Convert the string to an integer
if (pDefault != NULL)
res = atol(pDefault);
// If the resolution is unreasonable, then default to 300dpi
pUiGroup->dwDefault = (res > 0) ? res : DEFAULT_RESOLUTION;
} else {
WORD index;
if (pDefault != NULL &&
LISTOBJ_FindItemIndex(
(PLISTOBJ) pUiGroup->pUiOptions, pDefault, &index) != NULL)
{
pUiGroup->dwDefault = index;
} else {
// If no default is specified or the specified default
// is not valid, then use the very first UI option.
pUiGroup->dwDefault = 0;
}
}
return 0;
}
DWORD
PackUiGroup(
PBYTE pbuf,
DWORD offset,
PUIGROUP puigroup
)
{
DWORD curOffset = offset;
PACKPROC proc;
#if DBG
if (CHECK_DBG_LEVEL(DBG_LEVEL_TERSE) && pbuf == NULL) {
WORD index;
index = GetUiGroupIndex(puigroup->pName);
ASSERT(index == puigroup->uigrpIndex);
if ((PSTR) puigroup->dwDefault == NULL) {
DBGPRINT("No default for UI group *%s\n", puigroup->pName);
}
if (puigroup->pUiOptions == NULL) {
DBGPRINT("No options for UI group *%s\n", puigroup->pName);
}
}
#endif
// Pack embedded string pointers
curOffset += PackPstr(pbuf, curOffset, &puigroup->pXlation);
// Convert default option string to default option index
if (pbuf != NULL) {
PackUiGroupDefault(puigroup);
}
// Pack UI options
switch (puigroup->uigrpIndex) {
case UIGRP_PAGESIZE:
proc = (PACKPROC) PackMediaOption;
break;
case UIGRP_RESOLUTION:
proc = (PACKPROC) PackResOption;
break;
default:
proc = (PACKPROC) PackUiOption;
break;
}
curOffset += PackListObj(
pbuf, curOffset,
(PLISTOBJ *) &puigroup->pUiOptions,
puigroup->dwObjectSize, proc);
return curOffset - offset;
}
DWORD
PackOrderDep(
PBYTE pbuf,
DWORD offset,
HPPD hppd
)
{
DWORD count, size;
// Count total number of order dependencies
count = LISTOBJ_Count((PLISTOBJ) hppd->pOrderDep);
// Calculate how many bytes of memory we need
size = offsetof(PACKEDORDERDEPLIST, dependencies) +
count * sizeof(PACKEDORDERDEP);
size = RoundUpMultiple(size, MemAlignmentSize);
if (pbuf != NULL) {
PACKEDORDERDEPLIST *pOrderDepList;
PACKEDORDERDEP *pPackedOrderDep;
PORDERDEP pOrderDep;
pOrderDepList = (PACKEDORDERDEPLIST *) (pbuf + offset);
pOrderDepList->itemCount = (WORD) count;
pOrderDepList->itemSize = sizeof(PACKEDORDERDEP);
pOrderDep = (PORDERDEP) hppd->pOrderDep;
pPackedOrderDep = pOrderDepList->dependencies;
hppd->pOrderDep = (PACKEDORDERDEPLIST *) offset;
while (pOrderDep != NULL) {
PUIGROUP pUiGroup;
// For each order dependency, convert keyword
// and option strings to feature and option
// indices respectively
ASSERT(pOrderDep->pKeyword != NULL);
pUiGroup = (PUIGROUP) LISTOBJ_FindItemIndex(
(PLISTOBJ) hppd->pUiGroups,
pOrderDep->pKeyword,
& pPackedOrderDep->featureIndex);
if (pUiGroup != NULL) {
if (pOrderDep->pOption == NULL)
pPackedOrderDep->optionIndex = OPTION_INDEX_ANY;
else {
LISTOBJ_FindItemIndex(
(PLISTOBJ) pUiGroup->pUiOptions,
pOrderDep->pOption,
& pPackedOrderDep->optionIndex);
}
}
// If either the feature index or the option index is
// valid, then disregard the entire entry.
if (pPackedOrderDep->featureIndex == OPTION_INDEX_NONE ||
pPackedOrderDep->optionIndex == OPTION_INDEX_NONE)
{
// !!! Since we combine *PageSize, *PageRegion,
// *ImageableArea into a single group, entries
// involving *PageRegion and *ImageableArea will
// end up here and thus ignored.
DBGMSG(DBG_LEVEL_TERSE, "Invalid *OrderDependency entry.\n");
pPackedOrderDep->featureIndex = OPTION_INDEX_NONE;
}
pPackedOrderDep->order = pOrderDep->order;
pPackedOrderDep->section = pOrderDep->wSection;
pOrderDep = pOrderDep->pNext;
pPackedOrderDep++;
}
}
return size;
}
DWORD
PackUiConstraints(
PBYTE pbuf,
DWORD offset,
HPPD hppd
)
{
DWORD count, size;
// Count total number of ui constraints
count = LISTOBJ_Count((PLISTOBJ) hppd->pUiConstraints);
// Calculate how many bytes of memory we need
size = offsetof(PACKEDUICONSTRAINTLIST, constraints) +
count * sizeof(PACKEDUICONSTRAINTLIST);
size = RoundUpMultiple(size, MemAlignmentSize);
if (pbuf != NULL) {
PACKEDUICONSTRAINTLIST *pUiConstraintList;
PACKEDUICONSTRAINT *pPackedUiConstraint;
PUICONSTRAINT pUiConstraint;
pUiConstraintList = (PACKEDUICONSTRAINTLIST *) (pbuf + offset);
pUiConstraintList->itemCount = (WORD) count;
pUiConstraintList->itemSize = sizeof(PACKEDUICONSTRAINT);
pUiConstraint = (PUICONSTRAINT) hppd->pUiConstraints;
pPackedUiConstraint = pUiConstraintList->constraints;
hppd->pUiConstraints = (PACKEDUICONSTRAINTLIST *) offset;
while (pUiConstraint != NULL) {
PUIGROUP pUiGroup;
// For each order dependency, convert keyword
// and option strings to feature and option
// indices respectively
ASSERT(pUiConstraint->pKeyword1 != NULL &&
pUiConstraint->pKeyword2 != NULL);
pUiGroup = (PUIGROUP) LISTOBJ_FindItemIndex(
(PLISTOBJ) hppd->pUiGroups,
pUiConstraint->pKeyword1,
& pPackedUiConstraint->featureIndex1);
if (pUiGroup != NULL) {
if (pUiConstraint->pOption1 == NULL)
pPackedUiConstraint->optionIndex1 = OPTION_INDEX_ANY;
else {
LISTOBJ_FindItemIndex(
(PLISTOBJ) pUiGroup->pUiOptions,
pUiConstraint->pOption1,
& pPackedUiConstraint->optionIndex1);
}
}
pUiGroup = (PUIGROUP) LISTOBJ_FindItemIndex(
(PLISTOBJ) hppd->pUiGroups,
pUiConstraint->pKeyword2,
& pPackedUiConstraint->featureIndex2);
if (pUiGroup != NULL) {
if (pUiConstraint->pOption2 == NULL)
pPackedUiConstraint->optionIndex2 = OPTION_INDEX_ANY;
else {
LISTOBJ_FindItemIndex(
(PLISTOBJ) pUiGroup->pUiOptions,
pUiConstraint->pOption2,
& pPackedUiConstraint->optionIndex2);
}
}
// If either the feature index or the option index is
// valid, then disregard the entire entry.
if (pPackedUiConstraint->featureIndex1 == OPTION_INDEX_NONE ||
pPackedUiConstraint->optionIndex1 == OPTION_INDEX_NONE ||
pPackedUiConstraint->featureIndex2 == OPTION_INDEX_NONE ||
pPackedUiConstraint->optionIndex2 == OPTION_INDEX_NONE)
{
// !!! Since we combine *PageSize, *PageRegion,
// *ImageableArea into a single group, entries
// involving *PageRegion and *ImageableArea will
// end up here and thus ignored.
DBGMSG(DBG_LEVEL_VERBOSE, "Invalid *UiConstraints entry.\n");
pPackedUiConstraint->featureIndex1 = OPTION_INDEX_NONE;
}
pUiConstraint = pUiConstraint->pNext;
pPackedUiConstraint++;
}
}
return size;
}
DWORD
DoPackPpd(
PBYTE pbuf,
PPPDOBJ ppdobj
)
/*++
Routine Description:
Pack a PPDOBJ into a contiguous block of memory.
If hppd is NULL, the function verifies the PPDOBJ and
calculates amount of space required.
Arguments:
pbuf Pointer to destination buffer for storing packed PPD data
ppdobj Pointer to PPDOBJ
Return Value:
Number of bytes in the packed PPD data
--*/
{
DWORD dwSize;
HPPD hppd;
// Start with the fixed size header
dwSize = offsetof(PPDOBJ, dwPrivate);
dwSize = RoundUpMultiple(dwSize, MemAlignmentSize);
if (pbuf != NULL) {
memcpy(pbuf, ppdobj, dwSize);
hppd = (HPPD) pbuf;
} else {
hppd = (HPPD) ppdobj;
}
// Pack string objects
dwSize += PackPwstr(pbuf, dwSize, &hppd->pwstrFilename);
dwSize += PackPstr(pbuf, dwSize, &hppd->pNickName);
dwSize += PackPstr(pbuf, dwSize, &hppd->pPassword);
dwSize += PackPstr(pbuf, dwSize, &hppd->pExitServer);
dwSize += PackPstr(pbuf, dwSize, &hppd->pCustomSizeCode);
dwSize += PackPstr(pbuf, dwSize, &hppd->pJclBegin);
dwSize += PackPstr(pbuf, dwSize, &hppd->pJclToPs);
dwSize += PackPstr(pbuf, dwSize, &hppd->pJclEnd);
// Pack order dependencies and UI constraints
// !!! This must be done before packing UI groups.
dwSize += PackOrderDep(pbuf, dwSize, hppd);
dwSize += PackUiConstraints(pbuf, dwSize, hppd);
// Pack list objects
dwSize += PackListObj(
pbuf, dwSize, (PLISTOBJ *) &hppd->pUiGroups,
sizeof(UIGROUP), (PACKPROC) PackUiGroup);
dwSize += PackListObj(
pbuf, dwSize, (PLISTOBJ *) &hppd->pFontList,
sizeof(DEVFONT), (PACKPROC) PackDevFont);
return dwSize;
}
VOID
PpdSortUiGroups(
PPPDOBJ ppdobj
)
/*++
Routine Description:
Sort printer features into two groups: document-sticky
features followed by printer-sticky ones.
Arguments:
ppdobj - Pointer to PPDOBJ object
Return Value:
NONE
--*/
{
PUIGROUP pUiGroup, pNextGroup;
PUIGROUP pPrinterStickyGroup;
PUIGROUP pDocumentStickyGroup;
WORD featureIndex;
// Break the list of printer features into two separate lists:
// a document-sticky list and a printer-sticky list.
ASSERT(ppdobj->cDocumentStickyFeatures == 0 &&
ppdobj->cPrinterStickyFeatures == 0);
pPrinterStickyGroup = pDocumentStickyGroup = NULL;
pUiGroup = ppdobj->pUiGroups;
while (pUiGroup != NULL) {
pNextGroup = pUiGroup->pNext;
if (pUiGroup->bInstallable) {
// Printer-sticky (i.e. installable) feature
ppdobj->cPrinterStickyFeatures++;
pUiGroup->pNext = pPrinterStickyGroup;
pPrinterStickyGroup = pUiGroup;
} else {
// Document-sticky feature
ppdobj->cDocumentStickyFeatures++;
pUiGroup->pNext = pDocumentStickyGroup;
pDocumentStickyGroup = pUiGroup;
}
pUiGroup = pNextGroup;
}
ASSERT(ppdobj->cPrinterStickyFeatures <= MAX_PRINTER_OPTIONS &&
ppdobj->cDocumentStickyFeatures <= MAX_PRINTER_OPTIONS);
// Concatenate printer-sticky features and document-sticky features
// into a single sorted list: document-sticky features followed by
// printer-sticky features.
pUiGroup = NULL;
featureIndex = ppdobj->cPrinterStickyFeatures;
while (pPrinterStickyGroup != NULL) {
#ifdef STANDALONE
if (CHECK_DBG_LEVEL(DBG_LEVEL_VERBOSE)) {
PUIOPTION puioption = pPrinterStickyGroup->pUiOptions;
DBGPRINT("InstallableOptions: %s\n", pPrinterStickyGroup->pName);
while (puioption != NULL) {
DBGPRINT(" %s: %s\n", puioption->pName,
(puioption->pInvocation == NULL) ?
"NULL" : puioption->pInvocation);
puioption = puioption->pNext;
}
}
#endif
pNextGroup = pPrinterStickyGroup->pNext;
pPrinterStickyGroup->pNext = pUiGroup;
pUiGroup = pPrinterStickyGroup;
pUiGroup->featureIndex = --featureIndex;
pPrinterStickyGroup = pNextGroup;
}
featureIndex = ppdobj->cDocumentStickyFeatures;
while (pDocumentStickyGroup != NULL) {
pNextGroup = pDocumentStickyGroup->pNext;
pDocumentStickyGroup->pNext = pUiGroup;
pUiGroup = pDocumentStickyGroup;
pUiGroup->featureIndex = --featureIndex;
pDocumentStickyGroup = pNextGroup;
}
ppdobj->pUiGroups = pUiGroup;
}
HPPD
PackPpdData(
PPPDOBJ ppdobj
)
/*++
Routine Description:
Pack a PPDOBJ into a contiguous block of memory so that
it can be saved to a BPD file. Perform verification
on the PPDOBJ in the process.
Arguments:
ppdobj Pointer to a newly parsed PPD object
Return Value:
Handle to PPD data. All pointers are converted to offsets
relative to the beginning of the object. Original ppdobj
is deleted when this function returns.
--*/
{
DWORD dwSize;
HPPD hppd;
// Make sure our assumptions about NULL and pointers are valid
ASSERT(NULL == 0);
ASSERT(sizeof(DWORD) == sizeof(PVOID));
#if DBG
if (CHECK_DBG_LEVEL(DBG_LEVEL_WARNING)) {
if (ppdobj->bInstallable) {
DBGPRINT("Missing *CloseGroup InstallableOptions\n");
}
if (ppdobj->pOpenUi) {
DBGPRINT("Missing *CloseUI\n");
}
if (ppdobj->pFontList == NULL) {
DBGPRINT("No device fonts\n");
}
if (ppdobj->pPageSizes == NULL) {
DBGPRINT("No page size information\n");
}
if (ppdobj->wResType == RESTYPE_JCL &&
(ppdobj->wProtocols & PROTOCOL_PJL) == 0)
{
DBGPRINT("Must support PJL protocol to use *JCLResolution\n");
}
}
#endif
// Special handling of *RequiresPageRegion All: ...
if (ppdobj->pInputSlots &&
((ppdobj->pInputSlots->uigrpFlags & UIGF_REQRGNALL_TRUE) ||
(ppdobj->pInputSlots->uigrpFlags & UIGF_REQRGNALL_FALSE)))
{
BOOL reqPageRgn;
PINPUTSLOT pInputSlot;
reqPageRgn = (ppdobj->pInputSlots->uigrpFlags & UIGF_REQRGNALL_TRUE);
pInputSlot = (PINPUTSLOT) ppdobj->pInputSlots->pUiOptions;
while (pInputSlot != NULL) {
pInputSlot->bReqPageRgn = reqPageRgn;
pInputSlot = pInputSlot->pNext;
}
}
// Sort printer features into two groups: document-sticky
// features followed by printer-sticky ones.
PpdSortUiGroups(ppdobj);
// Check virtual memory configuration
if (ppdobj->dwFreeVm < MINFREEVM) {
DBGMSG1(DBG_LEVEL_WARNING,
"Not enough FreeVM: %d\n",
ppdobj->dwFreeVm);
ppdobj->dwFreeVm = MINFREEVM;
}
// Check custom page size parameters. If they are invalid,
// then disable custom page size feature altogether.
if (ppdobj->bCustomPageSize &&
ppdobj->maxMediaWidth == 0 &&
ppdobj->maxMediaHeight == 0)
{
DBGMSG(DBG_LEVEL_ERROR, "Invalid custom page size parameters.\n");
ppdobj->bCustomPageSize = FALSE;
}
// First pass: figure out how much memory we need to hold
// PPD data in a contiguous block.
dwSize = DoPackPpd(NULL, ppdobj);
DBGMSG1(DBG_LEVEL_VERBOSE, "Packed PPD size: %d bytes\n", dwSize);
// Allocate memory to hold PPD data
if ((hppd = MEMALLOC(dwSize)) == NULL) {
DBGERRMSG("MEMALLOC");
} else {
WORD index;
// Second pass: perform the actual packing
memset(hppd, 0, dwSize);
if (DoPackPpd((PBYTE) hppd, ppdobj) != dwSize) {
ASSERT("DoPackPpd failed!\n");
}
hppd->dwDataSize = dwSize;
hppd->wParserVersion = PPD_PARSER_VERSION;
for (index=0; index < MAXUIGRP; index++)
hppd->pPredefinedUiGroups[index] = NULL;
}
// Get rid of the input PPDOBJ
PPDOBJ_Delete(ppdobj);
return hppd;
}
///////////////////////////////////////////////////////////////////////////////
// Local functions called by UnpackPpdData() to unpack various objects
///////////////////////////////////////////////////////////////////////////////
VOID
UnpackListObj(
HPPD hppd,
PLISTOBJ listobj,
UNPACKPROC proc
)
{
while (listobj != NULL) {
// Restore link pointer and item name
OffsetToPointer(hppd, &listobj->pNext);
OffsetToPointer(hppd, &listobj->pName);
// Invoke callback function to restore other pointers
// in the current item
proc(hppd, listobj);
// On to the next next
listobj = listobj->pNext;
}
}
VOID
UnpackDevFont(
HPPD hppd,
PDEVFONT fontobj
)
{
OffsetToPointer(hppd, &fontobj->pXlation);
}
VOID
UnpackMediaOption(
HPPD hppd,
PMEDIAOPTION mediaoption
)
{
OffsetToPointer(hppd, &mediaoption->pXlation);
OffsetToPointer(hppd, &mediaoption->pPageSizeCode);
OffsetToPointer(hppd, &mediaoption->pPageRgnCode);
}
VOID
UnpackResOption(
HPPD hppd,
PRESOPTION resoption
)
{
OffsetToPointer(hppd, &resoption->pXlation);
OffsetToPointer(hppd, &resoption->pInvocation);
OffsetToPointer(hppd, &resoption->pJclCode);
OffsetToPointer(hppd, &resoption->pSetResCode);
}
VOID
UnpackUiOption(
HPPD hppd,
PUIOPTION puioption
)
{
OffsetToPointer(hppd, &puioption->pXlation);
OffsetToPointer(hppd, &puioption->pInvocation);
}
VOID
UnpackUiGroup(
HPPD hppd,
PUIGROUP puigroup
)
{
UNPACKPROC proc;
OffsetToPointer(hppd, &puigroup->pXlation);
OffsetToPointer(hppd, &puigroup->pUiOptions);
if (puigroup->uigrpIndex < MAXUIGRP)
hppd->pPredefinedUiGroups[puigroup->uigrpIndex] = puigroup;
switch (puigroup->uigrpIndex) {
case UIGRP_PAGESIZE:
proc = (UNPACKPROC) UnpackMediaOption;
break;
case UIGRP_RESOLUTION:
proc = (UNPACKPROC) UnpackResOption;
break;
default:
proc = (UNPACKPROC) UnpackUiOption;
break;
}
UnpackListObj(hppd, (PLISTOBJ) puigroup->pUiOptions, proc);
}
VOID
OffsetToPointer(
HPPD hppd,
PVOID pfield
)
/*++
Routine Description:
Convert offsets to address pointers
Arguments:
hppd Handle to PPD data
pfield Address of the offset/pointer field
Return Value:
NONE
[Note:]
We assume pointers and DWORDs are the same size.
--*/
{
DWORD *pdw = (DWORD *) pfield;
if (*pdw > hppd->dwDataSize) {
DBGMSG(DBG_LEVEL_ERROR, "Invalid offset!\n");
*pdw = 0;
} else if (*pdw != 0) {
*pdw += (DWORD) hppd;
}
}
VOID
UnpackPpdData(
HPPD hppd
)
/*++
Routine Description:
Unpack PPD data read from the BPD file and convert
offsets to pointers
Arguments:
hppd - Handle to PPD data
Return Value:
NONE
--*/
{
// Make sure our assumptions about NULL and pointers are valid
ASSERT(NULL == 0);
ASSERT(sizeof(DWORD) == sizeof(PVOID));
// First level pointers
OffsetToPointer(hppd, &hppd->pwstrFilename);
OffsetToPointer(hppd, &hppd->pNickName);
OffsetToPointer(hppd, &hppd->pPassword);
OffsetToPointer(hppd, &hppd->pExitServer);
OffsetToPointer(hppd, &hppd->pCustomSizeCode);
OffsetToPointer(hppd, &hppd->pJclBegin);
OffsetToPointer(hppd, &hppd->pJclToPs);
OffsetToPointer(hppd, &hppd->pJclEnd);
OffsetToPointer(hppd, &hppd->pFontList);
OffsetToPointer(hppd, &hppd->pUiGroups);
OffsetToPointer(hppd, &hppd->pOrderDep);
OffsetToPointer(hppd, &hppd->pUiConstraints);
// List objects
UnpackListObj(
hppd,
(PLISTOBJ) hppd->pFontList,
(UNPACKPROC) UnpackDevFont);
UnpackListObj(
hppd,
(PLISTOBJ) hppd->pUiGroups,
(UNPACKPROC) UnpackUiGroup);
}
///////////////////////////////////////////////////////////////////////////////
// Convenience functions for accessing PPD data
///////////////////////////////////////////////////////////////////////////////
// Return the default device resolution
LONG
PpdDefaultResolution(
HPPD hppd
)
{
return (hppd->pResOptions != NULL) ?
(LONG) hppd->pResOptions->dwDefault :
DEFAULT_RESOLUTION;
}
// Find the specified resolution option
BOOL
CompareResolution(
PRESOPTION pResOption,
DWORD dwParam
)
{
DWORD dwRes;
// Return FALSE when we find an option matching
// the specified resolution
return
! (pResOption->pName && atol(pResOption->pName) == (LONG) dwParam);
}
PRESOPTION
PpdFindResolution(
HPPD hppd,
LONG res
)
{
// Find the resolution option matching the specified resolution
return (hppd->pResOptions == NULL) ? NULL :
(PRESOPTION) LISTOBJ_Enum(
(PLISTOBJ) hppd->pResOptions->pUiOptions,
(LISTENUMPROC) CompareResolution, res);
}
// Find the specified media option
PMEDIAOPTION
PpdFindMediaOption(
HPPD hppd,
PSTR pFormName
)
{
WORD index;
// Find the named form from the list of media options
return (hppd->pPageSizes == NULL) ? NULL :
(PMEDIAOPTION) PpdFindUiOptionWithXlation(
(PUIOPTION) hppd->pPageSizes->pUiOptions,
pFormName, &index);
}
// Find the specified input slot
PINPUTSLOT
PpdFindInputSlot(
HPPD hppd,
PSTR pSlotName
)
{
WORD index;
return (hppd->pInputSlots == NULL) ? NULL :
(PINPUTSLOT) PpdFindUiOptionWithXlation(
(PUIOPTION) hppd->pInputSlots->pUiOptions,
pSlotName, &index);
}
// Find the specified UI options (take translation into consideration)
PUIOPTION
PpdFindUiOptionWithXlation(
PUIOPTION pUiOptions,
PSTR pName,
WORD *pIndex
)
{
WORD index = 0;
DWORD dwHash;
dwHash = HashKeyword(pName);
while (pUiOptions != NULL) {
// Stop searching if the specified name matches either
// the translation string or the name of the current
// UI option.
if ((pUiOptions->pXlation != NULL &&
strcmp(pName, pUiOptions->pXlation) == EQUAL_STRING) ||
(dwHash == pUiOptions->dwHash &&
strcmp(pName, pUiOptions->pName) == EQUAL_STRING))
{
break;
}
pUiOptions = pUiOptions->pNext;
index++;
}
*pIndex = (pUiOptions == NULL) ? OPTION_INDEX_NONE : index;
return pUiOptions;
}
// Return the invocation code to select a boolean UI option
PSTR
FindBooleanOptionCode(
PUIGROUP pUiGroup,
BOOL bOption
)
{
PUIOPTION pUiOption;
pUiOption = (pUiGroup == NULL) ? NULL :
(PUIOPTION) LISTOBJ_Find(
(PLISTOBJ) pUiGroup->pUiOptions,
bOption ? "True" : "False");
return pUiOption ? pUiOption->pInvocation : NULL;
}
// Return the invocation code to select specified manual feed option
PSTR
PpdFindManualFeedCode(
HPPD hppd,
BOOL bManual
)
{
return FindBooleanOptionCode(hppd->pManualFeed, bManual);
}
// Return the invocation code to select specified collate option
PSTR
PpdFindCollateCode(
HPPD hppd,
BOOL bCollate
)
{
return FindBooleanOptionCode(hppd->pCollate, bCollate);
}
// Determine whether device supports duplex options
BOOL
PpdSupportDuplex(
HPPD hppd
)
{
// We assume the printer supports duplex if an invocation
// string is specified for any duplex option.
return (hppd->pDuplex != NULL &&
hppd->pDuplex->pUiOptions != NULL);
}
// Return the invocation code to select specified duplex option
PSTR
PpdFindDuplexCode(
HPPD hppd,
PSTR pDuplexOption
)
{
PUIOPTION pOption;
if (hppd->pDuplex != NULL) {
pOption = (PUIOPTION)
LISTOBJ_Find((PLISTOBJ) hppd->pDuplex->pUiOptions, pDuplexOption);
} else
pOption = NULL;
return pOption ? pOption->pInvocation : NULL;
}
// Determine whether device supports specified custom page size
BOOL
PpdSupportCustomPageSize(
HPPD hppd,
PSREAL width,
PSREAL height
)
{
if (hppd->bCustomPageSize) {
PSREAL maxWidth, maxHeight;
// Find the smaller and larger of MaxMediaWidth and MaxMediaHeight.
// For a roll feed device, take width and height offsets into
// consideration.
maxWidth = hppd->maxMediaWidth;
if (maxWidth == 0)
maxWidth = MAX_LONG;
maxHeight = hppd->maxMediaHeight;
if (maxHeight == 0)
maxHeight = MAX_LONG;
if (! hppd->bCutSheet) {
ASSERT(hppd->customParam[PCP_WIDTHOFFSET].minVal <= maxWidth);
ASSERT(hppd->customParam[PCP_HEIGHTOFFSET].minVal <= maxHeight);
maxWidth -= hppd->customParam[PCP_WIDTHOFFSET].minVal;
maxHeight -= hppd->customParam[PCP_HEIGHTOFFSET].minVal;
}
// A custom page size is supported if: the larger of requested
// width and height is less than or equal to the larger of
// MaxMediaWidth and MaxMediaHeight; the smaller of requested
// width and height is less than or equal to the smaller of
// MaxMediaWidth and MaxMediaHeight.
if (min(width, height) <= min(maxWidth, maxHeight) &&
max(width, height) <= max(maxWidth, maxHeight))
{
return TRUE;
}
}
return FALSE;
}
// Get the default settings of printer-sticky features
WORD
PpdDefaultPrinterStickyFeatures(
HPPD hppd,
PBYTE pOptions
)
{
WORD cPrnFeature, cDocFeature, index;
PUIGROUP pUiGroup, pPrnFeatures;
cPrnFeature = hppd->cPrinterStickyFeatures;
cDocFeature = hppd->cDocumentStickyFeatures;
ASSERT(cPrnFeature <= MAX_PRINTER_OPTIONS);
ASSERT(cDocFeature <= MAX_PRINTER_OPTIONS);
pPrnFeatures = pUiGroup = (PUIGROUP)
LISTOBJ_FindIndexed((PLISTOBJ) hppd->pUiGroups, cDocFeature);
// Use default installable options
for (index=0; index < cPrnFeature; index++) {
ASSERT(pUiGroup != NULL && pUiGroup->bInstallable);
pOptions[index] = (BYTE) pUiGroup->dwDefault;
pUiGroup = pUiGroup->pNext;
}
// Make sure the defaults don't conflict with each other.
// If the default do conflict with each other, then we
// should have the PPD file fixed. Here we try to straighten
// up what we can.
for (index = 0; index < cPrnFeature; index ++) {
LONG lParam;
WORD feature, selection, count;
lParam = PpdFeatureConstrained(
hppd, pOptions, NULL, index, pOptions[index]);
if (lParam == 0)
continue;
DBGMSG(DBG_LEVEL_VERBOSE, "Default options conflict with each other!\n");
// The selection for the current feature is constrained.
// Try to find another selection that's not.
pUiGroup = (PUIGROUP)
LISTOBJ_FindIndexed((PLISTOBJ) pPrnFeatures, index);
count = (WORD) UIGROUP_CountOptions(pUiGroup);
for (selection=0; selection < count; selection++) {
if (PpdFeatureConstrained(
hppd, pOptions, NULL, index, selection) == 0)
{
pOptions[index] = (BYTE) selection;
break;
}
}
if (selection >= count) {
// All selections for the current feature are constrained.
// Try to change the selection of the conflicting feature.
EXTRACT_CONSTRAINT_PARAM(lParam, feature, selection);
feature -= cDocFeature;
pUiGroup = (PUIGROUP)
LISTOBJ_FindIndexed((PLISTOBJ) pPrnFeatures, feature);
count = (WORD) UIGROUP_CountOptions(pUiGroup);
for (selection=0; selection < count; selection++) {
if (! SearchUiConstraints(hppd,
(WORD) (feature + cDocFeature), selection,
(WORD) (index + cDocFeature), pOptions[index]))
{
pOptions[feature] = (BYTE) selection;
break;
}
}
}
}
return cPrnFeature;
}
// Get the default settings of document-sticky features
WORD
PpdDefaultDocumentStickyFeatures(
HPPD hppd,
PBYTE pOptions
)
{
WORD cDocFeature, index;
PUIGROUP pUiGroup;
pUiGroup = hppd->pUiGroups;
cDocFeature = hppd->cDocumentStickyFeatures;
for (index=0; index < cDocFeature; index++) {
ASSERT(pUiGroup != NULL && !pUiGroup->bInstallable);
//
// HACK: The default value for *Resolution feature is not a selection index.
// Instead, it's the default resolution measured in dpi.
//
if (pUiGroup->uigrpIndex == UIGRP_RESOLUTION)
pOptions[index] = OPTION_INDEX_ANY;
else
pOptions[index] = (BYTE) pUiGroup->dwDefault;
pUiGroup = pUiGroup->pNext;
}
return cDocFeature;
}
// Find out if there is an OrderDependency entry corresponding
// to the requested feature and option.
PPACKEDORDERDEP
PpdFindOrderDep(
HPPD hppd,
WORD feature,
WORD option
)
{
PPACKEDORDERDEP pOrderDep;
WORD cOrderDep;
if (hppd->pOrderDep == NULL)
return NULL;
cOrderDep = hppd->pOrderDep->itemCount;
pOrderDep = hppd->pOrderDep->dependencies;
while (cOrderDep--) {
if (pOrderDep->featureIndex == feature &&
(pOrderDep->optionIndex == OPTION_INDEX_ANY ||
pOrderDep->optionIndex == option))
{
return pOrderDep;
}
pOrderDep++;
}
return NULL;
}
// Determine whether an invocation string is empty
BOOL
EmptyInvocationStr(
PSTR pstr
)
{
if (pstr == NULL)
return TRUE;
while (*pstr && IsSpace(*pstr))
pstr++;
return *pstr == NUL;
}
// Find the UIGROUP and UIOPTION objects corresponding to a
// printer feature selection.
BOOL
PpdFindFeatureSelection(
HPPD hppd,
WORD feature,
WORD selection,
PUIGROUP *ppUiGroup,
PUIOPTION *ppUiOption
)
{
*ppUiOption = NULL;
*ppUiGroup = NULL;
if (feature == OPTION_INDEX_NONE || selection == OPTION_INDEX_ANY)
return FALSE;
*ppUiGroup = (PUIGROUP)
LISTOBJ_FindIndexed((PLISTOBJ) hppd->pUiGroups, feature);
if (*ppUiGroup == NULL) {
DBGMSG1(DBG_LEVEL_ERROR,
"Couldn't find printer feature: index = %d\n", feature);
} else {
*ppUiOption = (PUIOPTION)
LISTOBJ_FindIndexed((PLISTOBJ) (*ppUiGroup)->pUiOptions, selection);
if (*ppUiOption == NULL) {
DBGMSG1(DBG_LEVEL_ERROR,
"Couldn't find feature selection: index = %d\n", selection);
}
}
return (*ppUiGroup != NULL) && (*ppUiOption != NULL);
}
// Map DEVMODE duplex selection to a duplex option name
PSTR
MapDevModeDuplexOption(
WORD dmDuplex
)
{
static CHAR duplexNoneStr[] = "None";
static CHAR duplexTumbleStr[] = "DuplexTumble";
static CHAR duplexNoTumbleStr[] = "DuplexNoTumble";
if (dmDuplex == DMDUP_HORIZONTAL) {
/* Horizontal == ShortEdge == Tumble */
return duplexTumbleStr;
} else if (dmDuplex == DMDUP_VERTICAL) {
/* Vertical == LongEdge == NoTumble */
return duplexNoTumbleStr;
} else {
// No duplex.
return duplexNoneStr;
}
}
BOOL
FeatureSelectionNone(
HPPD hppd,
WORD feature,
WORD selection
)
/*++
Routine Description:
Return TRUE if the option keyword for a feature/selection
is None or False.
Arguments:
hppd - Handle to PPD object
feature - Feature index
selection - Option index
Return Value:
TRUE if the option keyword is None or False.
--*/
{
PUIGROUP pUiGroup;
PUIOPTION pUiOption;
if (PpdFindFeatureSelection(
hppd, feature, selection, &pUiGroup, &pUiOption))
{
ASSERT(pUiOption->pName != NULL);
return strcmp(pUiOption->pName, "None") == EQUAL_STRING ||
strcmp(pUiOption->pName, "False") == EQUAL_STRING;
} else {
DBGERRMSG("PpdFindFeatureSelection");
}
return FALSE;
}
BOOL
SearchUiConstraints(
HPPD hppd,
WORD feature1,
WORD selection1,
WORD feature2,
WORD selection2
)
/*++
Routine Description:
Check if feature1/selection1 constrains feature2/selection2
Arguments:
hppd - Handle to PPD object
feature1/selection1
feature2/selection2 - Selections to be checked
Return Value:
TRUE if there is a conflict, FALSE otherwise
--*/
#define MatchFeatureSelection(hppd, f1, s1, f2, s2) \
(f1 == f2 && (s1 == s2 || \
(s1 == OPTION_INDEX_ANY && !FeatureSelectionNone(hppd, f2, s2))))
{
WORD cConstraints;
PACKEDUICONSTRAINT *pConstraints;
ASSERT(hppd->pUiConstraints != NULL);
cConstraints = hppd->pUiConstraints->itemCount;
pConstraints = hppd->pUiConstraints->constraints;
for ( ; cConstraints--; pConstraints++) {
if (pConstraints->featureIndex1 == OPTION_INDEX_NONE)
continue;
if (MatchFeatureSelection(hppd,
pConstraints->featureIndex1, pConstraints->optionIndex1,
feature1, selection1) &&
MatchFeatureSelection(hppd,
pConstraints->featureIndex2, pConstraints->optionIndex2,
feature2, selection2))
{
return TRUE;
}
}
return FALSE;
}
LONG
PpdFeatureConstrained(
HPPD hppd,
PBYTE pPrnOptions,
PBYTE pDocOptions,
WORD feature,
WORD selection
)
/*++
Routine Description:
Check if a feature selection conflicts with other feature selections
Arguments:
hppd - Handle to PPD object
pPrnOptions - Pointer to an array of printer-sticky feature selections
pDocOptions - Pointer to an array of document-sticky feature selections
NULL if "feature" is a printer-sticky feature
feature/selection - Feature and option indices of what's to be checked
Return Value:
0 if the feature selection is not constrained. Otherwise, the feature
selection is contrained. The return value is interpreted as follows:
Bit 17: Set if the conflict is "hard" and cannot be ignored
i.e. document-sticky <=> document-sticky features
Bit 16: Set if the conflict is "soft"and may be ignored
i.e. printer-sticky <=> document/printer-sticky features
Bit 15-8: Conflicting feature index
Bit 8-0: Conflicting selection index
--*/
{
WORD index, cDocFeature, cPrnFeature;
cDocFeature = hppd->cDocumentStickyFeatures;
cPrnFeature = hppd->cPrinterStickyFeatures;
ASSERT(pPrnOptions != NULL);
if (pDocOptions == NULL) {
// Conflicts among printer-sticky feature selections?
ASSERT(feature < cPrnFeature);
for (index=0; index < cPrnFeature; index++) {
if (index != feature &&
SearchUiConstraints(hppd,
(WORD) (index + cDocFeature), pPrnOptions[index],
(WORD) (feature + cDocFeature), selection))
{
return MAKE_CONSTRAINT_PARAM(SOFT_CONSTRAINT,
index + cDocFeature, pPrnOptions[index]);
}
}
} else {
// Conflicts among document-sticky feature selections?
ASSERT(feature < cDocFeature);
if (selection == OPTION_INDEX_ANY)
return 0;
for (index=0; index < cDocFeature; index++) {
if (index != feature &&
pDocOptions[index] != OPTION_INDEX_ANY &&
SearchUiConstraints(hppd,
index, pDocOptions[index],
feature, selection))
{
return MAKE_CONSTRAINT_PARAM(HARD_CONSTRAINT,
index, pDocOptions[index]);
}
}
// Conflicts between document-sticky and printer-sticky features
for (index=0; index < cPrnFeature; index++) {
if (SearchUiConstraints(hppd,
(WORD) (index+cDocFeature), pPrnOptions[index],
feature, selection))
{
return MAKE_CONSTRAINT_PARAM(SOFT_CONSTRAINT,
index + cDocFeature, pPrnOptions[index]);
}
}
}
return 0;
}