/*++ Copyright (c) 1998 Microsoft Corporation Module Name: pass2.cpp Abstract: Functions for additional checking of a PPD file Environment: PostScript driver, PPD parser Revision History: 09/15/98 -rorleth- Created it. --*/ #include #include #include "pass2.h" enum FeatureId { FID_PAGE_SIZE, FID_PAGE_REGION, FID_INPUT_SLOT, FID_SMOOTHING, FID_MEDIA_COLOR, FID_MEDIA_TYPE, FID_MEDIA_WEIGHT, FID_OUTPUT_MODE, FID_PAPER_DIMENSION, FID_IMAGE_AREA, FID_OUTPUT_ORDER, NO_OF_FEATURES }; typedef struct _PPD_FEATURE { FeatureId eId; LPSTR lpszName; } PPD_FEATURE, *PPPD_FEATURE; // // features whose options we have to check // static PPD_FEATURE aCheckFeat[] = { { FID_PAGE_SIZE, "PageSize" }, { FID_PAGE_REGION, "PageRegion" }, { FID_INPUT_SLOT, "InputSlot" }, { FID_SMOOTHING, "Smoothing" }, { FID_MEDIA_COLOR, "MediaColor" }, { FID_MEDIA_TYPE, "MediaType" }, { FID_MEDIA_WEIGHT, "MediaWeight" }, { FID_OUTPUT_MODE, "OutputMode" }, { FID_PAPER_DIMENSION, "PaperDimension" }, { FID_IMAGE_AREA, "ImageableArea" }, { FID_OUTPUT_ORDER, "OutputOrder" }, { NO_OF_FEATURES, NULL} }; typedef struct _PPD_OPTION { FeatureId eId; LPSTR lpszName; } PPD_OPTION, *PPPD_OPTION; // // keywords that require defined feature options // static PPD_OPTION gaCheckKeyword[] = { { FID_INPUT_SLOT, "RequiresPageRegion"}, { NO_OF_FEATURES, NULL } }; // // special option names // static PPD_OPTION gaSpecialOptions[] = { { NO_OF_FEATURES, "None" }, // NO_OF_FEATURES means valid for all features in that case { NO_OF_FEATURES, "All" }, { NO_OF_FEATURES, "Unknown" }, { FID_OUTPUT_ORDER, "Normal" }, // Normal and Reverse are predefined options for OutputOrder { FID_OUTPUT_ORDER, "Reverse" }, { NO_OF_FEATURES, NULL}, }; // // keywords that have a length limitation for the UI // typedef struct _PPD_LENGTH_CHECK { FeatureId eId; size_t iMaxLen; } PPD_LENGTH_CHECK, *PPPD_LENGTH_CHECK; static PPD_LENGTH_CHECK gaCheckLength[] = { { FID_INPUT_SLOT, 23}, { NO_OF_FEATURES, 0 } }; const char *pDefaultKeyword = "Default"; const int MaxOptionNameLen = 40; const int MaxTranslationNameLen = 128; typedef struct _OPTION_LIST { char aName[MaxOptionNameLen+1]; char aTransName[MaxTranslationNameLen+1]; _OPTION_LIST *pNext; } OPTION_LIST, *POPTION_LIST; static POPTION_LIST gaOptionList[NO_OF_FEATURES]; // stores all defined options /*++ Routine Description: checks whether a references option is defined Arguments: char **ppString : Pointer to pointer to option, is advanced by the option name length FeatureId FeatId : ID of the feature, which should have the option char *pOptionName: pointer to buffer, where the option name shall be stored for error messages Return Value: TRUE if the identified feature has that option, FALSE if not --*/ static BOOL IsOptionDefined(char **ppString, FeatureId FeatId, char *pOptionName) { char *pEndName, *pName = *ppString; while (isspace(*pName)) pName++; pEndName = strpbrk(pName, "/: \t\n\r\0"); *ppString = pEndName; // advance current pointer strncpy(pOptionName, pName, min((DWORD)(pEndName - pName), MaxOptionNameLen)); pOptionName[pEndName-pName] = 0; // // check special cases that do not have to be defined // int i=0; while (gaSpecialOptions[i].lpszName != NULL) { if ((gaSpecialOptions[i].eId == NO_OF_FEATURES) || (gaSpecialOptions[i].eId == FeatId)) { if (!strcmp(gaSpecialOptions[i].lpszName, pOptionName)) return TRUE; } i++; } POPTION_LIST pList = gaOptionList[FeatId], pNew; while (pList != NULL) { if (!strcmp(pList->aName, pOptionName)) return TRUE; // found it, it's defined pList = pList->pNext; } return FALSE; } /*++ Routine Description: checks a whole PPD-file, whether all referenced options are defined Arguments: PTSTR FileName: Name of the PPD-file to check --*/ extern "C" void CheckOptionIntegrity(PTSTR ptstrPpdFilename) { ZeroMemory(gaOptionList, sizeof(gaOptionList)); // initialise the list header _flushall(); // to avoid sync problems with the DbgPrint output // // create the file mapping // HANDLE hFile = CreateFile(ptstrPpdFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { return; } DWORD dwFileSize = GetFileSize(hFile, NULL); if (dwFileSize == 0xffffffff) { CloseHandle(hFile); return; } HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY,0, 0,NULL); if (hMap == NULL) { CloseHandle(hFile); return; } LPCVOID pView = MapViewOfFile(hMap, FILE_MAP_READ, 0,0,0); if (pView == NULL) { CloseHandle(hMap); CloseHandle(hFile); return; } // // copy the whole file into an allocated buffer just to get zero-termination // LPSTR pFile, pFileStart; pFileStart = (LPSTR) VirtualAlloc(NULL, dwFileSize+1, MEM_COMMIT, PAGE_READWRITE); if (pFileStart != NULL) { CopyMemory(pFileStart, pView, dwFileSize); *(pFileStart + dwFileSize) = 0; } UnmapViewOfFile(pView); CloseHandle(hMap); CloseHandle(hFile); if (pFileStart == NULL) { cout << "ppdcheck.exe out of memory" << endl; return; } pFile = pFileStart; // // now the whole PPD-file is a giant string // extract all the features/options // char *pCurOption = (char *) pFileStart; char OptionName[MaxOptionNameLen+1]; // // step 1 : extract all valid feature options // while ((pFile != NULL) && (pCurOption = strchr(pFile, '*')) != NULL) { pCurOption++; char *pNextLine = strpbrk(pCurOption, "\n\r"); pFile = pNextLine; if (*pCurOption == '%') // skip comments continue; // // scan whether this is one of the features to look for // int Index = 0; while (aCheckFeat[Index].eId != NO_OF_FEATURES) { if (strncmp(aCheckFeat[Index].lpszName, pCurOption, strlen(aCheckFeat[Index].lpszName))) { Index++; continue; } // // this is one of the monitored features: make entry in list // POPTION_LIST pList = gaOptionList[aCheckFeat[Index].eId], pNew; pNew = new OPTION_LIST; pNew->pNext = gaOptionList[aCheckFeat[Index].eId]; gaOptionList[aCheckFeat[Index].eId] = pNew; char *pName = pCurOption + strlen(aCheckFeat[Index].lpszName), *pEndName; while (isspace(*pName)) pName++; pEndName = strpbrk(pName, "/: \0"); DWORD dwNameLen = min((DWORD)(pEndName - pName), MaxOptionNameLen); strncpy(pNew->aName, pName, dwNameLen); pNew->aName[dwNameLen] = 0; dwNameLen = 0; if (*pEndName == '/') // there is a translation string { pName = pEndName +1; pEndName = strpbrk(pName, ":\n\r\0"); dwNameLen = min((DWORD) (pEndName - pName), MaxTranslationNameLen); strncpy(pNew->aTransName, pName, dwNameLen); } pNew->aTransName[dwNameLen] = 0; break; } } // // step 2: check whether all referenced options are featured // pFile = pFileStart; pCurOption = (char *) pFile; while ((pFile != NULL) && (pCurOption = strchr(pFile, '*')) != NULL) { pCurOption++; char *pNextLine = strpbrk(pCurOption, "\n\r"); pFile = pNextLine; // // skip comments // if (*pCurOption == '%') continue; // // check whether it starts with "Default", if yes, check that feature option // if (!strncmp(pDefaultKeyword, pCurOption, strlen(pDefaultKeyword))) { pCurOption += strlen(pDefaultKeyword); int Index = 0; while (aCheckFeat[Index].eId != NO_OF_FEATURES) { if (strncmp(aCheckFeat[Index].lpszName, pCurOption, strlen(aCheckFeat[Index].lpszName))) { Index++; continue; } // // it's one of the checked featurs // pCurOption += strlen(aCheckFeat[Index].lpszName); char *pOption = strpbrk(pCurOption, ":"); if (pOption == NULL) { cout << "Warning: default option for '" << aCheckFeat[Index].lpszName << "' is not completed !" << endl; break; } pCurOption = pOption + 1; if (!IsOptionDefined(&pCurOption, aCheckFeat[Index].eId, OptionName)) cout << "Warning: default option '" << OptionName << "' for feature '*" << aCheckFeat[Index].lpszName <<"' is not defined!" << endl; break; } } else { // // scan whether this is one of the keywords to look for // int Index = 0; while (gaCheckKeyword[Index].eId != NO_OF_FEATURES) { if (strncmp(gaCheckKeyword[Index].lpszName, pCurOption, strlen(gaCheckKeyword[Index].lpszName))) { Index++; continue; } // // this is one of the monitored features: get the option it references // pCurOption += strlen(gaCheckKeyword[Index].lpszName); if (!IsOptionDefined(&pCurOption, gaCheckKeyword[Index].eId, OptionName)) cout << "Warning: option '" << OptionName << "' for keyword '*" << gaCheckKeyword[Index].lpszName <<"' is not defined!" << endl; break; } Index++; } } // // step 3: check that all option names are different and don't have trailing or leading spaces // for (int i = 0; i < NO_OF_FEATURES;i++) { POPTION_LIST pCheck = gaOptionList[i], pCur; while (pCheck != NULL) { pCur = pCheck->pNext; while (pCur != NULL) { if (strlen(pCheck->aName) && !strcmp(pCheck->aName, pCur->aName)) cout << "Warning: option name '" << pCheck->aName << "' used twice" << endl; if (strlen(pCheck->aTransName) && !strcmp(pCheck->aTransName, pCur->aTransName)) cout << "Warning: translation name '" << pCheck->aTransName << "' used twice" << endl; pCur = pCur->pNext; } size_t TransNameLen = strlen(pCheck->aTransName); if (isspace(pCheck->aTransName[0])) cout << "Warning: translation name '" << pCheck->aTransName << "' has leading whitespace" << endl; if ((TransNameLen > 1) && isspace(pCheck->aTransName[TransNameLen-1])) cout << "Warning: translation name '" << pCheck->aTransName << "' has trailing whitespace" << endl; pCheck = pCheck->pNext; } } // // step 4: warn if the string that is used for the display is too long // i = 0; while (gaCheckLength[i].eId != NO_OF_FEATURES) { POPTION_LIST pCheck = gaOptionList[gaCheckLength[i].eId], pCur; while (pCheck != NULL) { size_t TransNameLen = strlen(pCheck->aTransName); if (TransNameLen > gaCheckLength[i].iMaxLen) cout << "Warning: translation name '" << pCheck->aTransName << "' will be truncated to "<< (unsigned int) gaCheckLength[i].iMaxLen << " characters"<< endl; else if ((TransNameLen == 0) && (strlen(pCheck->aName) > gaCheckLength[i].iMaxLen)) cout << "Warning: option name '" << pCheck->aName << "' will be truncated to "<< (unsigned int) gaCheckLength[i].iMaxLen << " characters"<< endl; pCheck = pCheck->pNext; } i++; } // // clean up // for (i = 0; i < NO_OF_FEATURES;i++) { POPTION_LIST pTmp = gaOptionList[i], pCur; while (pTmp != NULL) { pCur = pTmp->pNext; delete pTmp; pTmp = pCur; } gaOptionList[i] = NULL; } _flushall(); // to avoid sync problems with the DbgPrint output VirtualFree((LPVOID) pFileStart, 0, MEM_RELEASE); }