X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fcups.git;a=blobdiff_plain;f=systemv%2Fcupstestppd.c;h=8e7201f407eb1b9514be4220d9b264fd36277229;hp=09f45d5ae0a5c4583c2fbac165f1f677c384060d;hb=0a6827453a0fe4a97ad37a6a1a7ffeb96a10e339;hpb=fa73b22906f71080fa5056485d8204612717adac diff --git a/systemv/cupstestppd.c b/systemv/cupstestppd.c index 09f45d5ae..8e7201f40 100644 --- a/systemv/cupstestppd.c +++ b/systemv/cupstestppd.c @@ -1,25 +1,16 @@ /* - * "$Id: cupstestppd.c 4990 2006-01-26 02:21:45Z mike $" + * "$Id: cupstestppd.c 6927 2007-09-07 16:51:00Z mike $" * * PPD test program for the Common UNIX Printing System (CUPS). * - * Copyright 1997-2006 by Easy Software Products, all rights reserved. + * Copyright 2007 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the - * property of Easy Software Products and are protected by Federal - * copyright law. Distribution and use rights are outlined in the file - * "LICENSE.txt" which should have been included with this file. If this - * file is missing or damaged please contact Easy Software Products - * at: - * - * Attn: CUPS Licensing Information - * Easy Software Products - * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636 USA - * - * Voice: (301) 373-9600 - * EMail: cups-info@cups.org - * WWW: http://www.cups.org + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". * * PostScript is a trademark of Adobe Systems, Inc. * @@ -27,9 +18,16 @@ * * Contents: * - * main() - Main entry for test program. - * show_conflicts() - Show option conflicts in a PPD file. - * usage() - Show program usage... + * main() - Main entry for test program. + * check_basics() - Check for CR LF, mixed line endings, and blank lines. + * check_constraints() - Check UIConstraints in the PPD file. + * check_defaults() - Check default option keywords in the PPD file. + * check_filters() - Check filters in the PPD file. + * check_translations() - Check translations in the PPD file. + * show_conflicts() - Show option conflicts in a PPD file. + * test_raster() - Test PostScript commands for raster printers. + * usage() - Show program usage... + * valid_utf8() - Check whether a string contains valid UTF-8 text. */ /* @@ -39,8 +37,25 @@ #include #include #include +#include #include #include +#include + + +/* + * Error warning overrides... + */ + +enum +{ + WARN_NONE = 0, + WARN_CONSTRAINTS = 1, + WARN_DEFAULTS = 2, + WARN_FILTERS = 4, + WARN_TRANSLATIONS = 8, + WARN_ALL = 15 +}; /* @@ -74,45 +89,61 @@ enum * Local functions... */ -void check_basics(const char *filename); -void show_conflicts(ppd_file_t *ppd); -void usage(void); +static void check_basics(const char *filename); +static int check_constraints(ppd_file_t *ppd, int errors, int verbose, + int warn); +static int check_defaults(ppd_file_t *ppd, int errors, int verbose, + int warn); +static int check_filters(ppd_file_t *ppd, const char *root, int errors, + int verbose, int warn); +static int check_translations(ppd_file_t *ppd, int errors, int verbose,\ + int warn); +static void show_conflicts(ppd_file_t *ppd); +static int test_raster(ppd_file_t *ppd, int verbose); +static void usage(void); +static int valid_utf8(const char *s); /* * 'main()' - Main entry for test program. */ -int /* O - Exit status */ -main(int argc, /* I - Number of command-line arguments */ - char *argv[]) /* I - Command-line arguments */ +int /* O - Exit status */ +main(int argc, /* I - Number of command-line args */ + char *argv[]) /* I - Command-line arguments */ { - int i, j, k, m, n; /* Looping vars */ - int len; /* Length of option name */ - char *opt; /* Option character */ - const char *ptr; /* Pointer into string */ - int files; /* Number of files */ - int verbose; /* Want verbose output? */ - int status; /* Exit status */ - int errors; /* Number of conformance errors */ - int ppdversion; /* PPD spec version in PPD file */ - ppd_status_t error; /* Status of ppdOpen*() */ - int line; /* Line number for error */ - int xdpi, /* X resolution */ - ydpi; /* Y resolution */ - ppd_file_t *ppd; /* PPD file record */ - ppd_attr_t *attr; /* PPD attribute */ - ppd_size_t *size; /* Size record */ - ppd_group_t *group; /* UI group */ - ppd_option_t *option; /* Standard UI option */ - ppd_group_t *group2; /* UI group */ - ppd_option_t *option2; /* Standard UI option */ - ppd_choice_t *choice; /* Standard UI option choice */ + int i, j, k, m, n; /* Looping vars */ + int len; /* Length of option name */ + char *opt; /* Option character */ + const char *ptr; /* Pointer into string */ + int files; /* Number of files */ + int verbose; /* Want verbose output? */ + int warn; /* Which errors to just warn about */ + int status; /* Exit status */ + int errors; /* Number of conformance errors */ + int ppdversion; /* PPD spec version in PPD file */ + ppd_status_t error; /* Status of ppdOpen*() */ + int line; /* Line number for error */ + struct stat statbuf; /* File information */ + char pathprog[1024], /* Complete path to program/filter */ + *root; /* Root directory */ + int xdpi, /* X resolution */ + ydpi; /* Y resolution */ + ppd_file_t *ppd; /* PPD file record */ + ppd_attr_t *attr; /* PPD attribute */ + ppd_size_t *size; /* Size record */ + ppd_group_t *group; /* UI group */ + ppd_option_t *option; /* Standard UI option */ + ppd_group_t *group2; /* UI group */ + ppd_option_t *option2; /* Standard UI option */ + ppd_choice_t *choice; /* Standard UI option choice */ static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" }; static char *sections[] = { "ANY", "DOCUMENT", "EXIT", "JCL", "PAGE", "PROLOG" }; + _cupsSetLocale(argv); + /* * Display PPD files for each file listed on the command-line... */ @@ -123,6 +154,8 @@ main(int argc, /* I - Number of command-line arguments */ ppd = NULL; files = 0; status = ERROR_NONE; + root = ""; + warn = WARN_NONE; for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1]) @@ -130,6 +163,37 @@ main(int argc, /* I - Number of command-line arguments */ for (opt = argv[i] + 1; *opt; opt ++) switch (*opt) { + case 'R' : /* Alternate root directory */ + i ++; + + if (i >= argc) + usage(); + + root = argv[i]; + break; + + case 'W' : /* Turn errors into warnings */ + i ++; + + if (i >= argc) + usage(); + + if (!strcmp(argv[i], "none")) + warn = WARN_NONE; + else if (!strcmp(argv[i], "constraints")) + warn |= WARN_CONSTRAINTS; + else if (!strcmp(argv[i], "defaults")) + warn |= WARN_DEFAULTS; + else if (!strcmp(argv[i], "filters")) + warn |= WARN_FILTERS; + else if (!strcmp(argv[i], "translations")) + warn |= WARN_TRANSLATIONS; + else if (!strcmp(argv[i], "all")) + warn = WARN_ALL; + else + usage(); + break; + case 'q' : /* Quiet mode */ if (verbose > 0) { @@ -302,32 +366,20 @@ main(int argc, /* I - Number of command-line arguments */ attr->value) ppdversion = (int)(10 * atof(attr->value) + 0.5); - if (verbose > 0) - { - /* - * Look for default keywords with no matching option... - */ - - for (i = 0; i < ppd->num_attrs; i ++) + for (j = 0; j < ppd->num_filters; j ++) + if (strstr(ppd->filters[j], "application/vnd.cups-raster")) { - attr = ppd->attrs[i]; + if (!test_raster(ppd, verbose)) + errors ++; + break; + } - if (!strcmp(attr->name, "DefaultColorSpace") || - !strcmp(attr->name, "DefaultFont") || - !strcmp(attr->name, "DefaultImageableArea") || - !strcmp(attr->name, "DefaultOutputOrder") || - !strcmp(attr->name, "DefaultPaperDimension") || - !strcmp(attr->name, "DefaultTransfer")) - continue; - - if (!strncmp(attr->name, "Default", 7) && - !ppdFindOption(ppd, attr->name + 7)) - _cupsLangPrintf(stdout, - _(" WARN %s has no corresponding " - "options!\n"), - attr->name); - } - } + /* + * Look for default keywords with no matching option... + */ + + if (!(warn & WARN_DEFAULTS)) + errors = check_defaults(ppd, errors, verbose, 0); if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL) { @@ -337,14 +389,14 @@ main(int argc, /* I - Number of command-line arguments */ _cupsLangPuts(stdout, _(" FAIL\n")); _cupsLangPuts(stdout, - _(" **FAIL** REQUIRED DefaultImageableArea\n" + _(" **FAIL** REQUIRED DefaultImageableArea\n" " REF: Page 102, section 5.15.\n")); - } + } errors ++; } else if (ppdPageSize(ppd, attr->value) == NULL && - strcmp(attr->value, "Unknown")) + strcmp(attr->value, "Unknown")) { if (verbose >= 0) { @@ -352,10 +404,10 @@ main(int argc, /* I - Number of command-line arguments */ _cupsLangPuts(stdout, _(" FAIL\n")); _cupsLangPrintf(stdout, - _(" **FAIL** BAD DefaultImageableArea %s!\n" + _(" **FAIL** BAD DefaultImageableArea %s!\n" " REF: Page 102, section 5.15.\n"), attr->value); - } + } errors ++; } @@ -373,14 +425,14 @@ main(int argc, /* I - Number of command-line arguments */ _cupsLangPuts(stdout, _(" FAIL\n")); _cupsLangPuts(stdout, - _(" **FAIL** REQUIRED DefaultPaperDimension\n" + _(" **FAIL** REQUIRED DefaultPaperDimension\n" " REF: Page 103, section 5.15.\n")); - } + } errors ++; } else if (ppdPageSize(ppd, attr->value) == NULL && - strcmp(attr->value, "Unknown")) + strcmp(attr->value, "Unknown")) { if (verbose >= 0) { @@ -388,10 +440,10 @@ main(int argc, /* I - Number of command-line arguments */ _cupsLangPuts(stdout, _(" FAIL\n")); _cupsLangPrintf(stdout, - _(" **FAIL** BAD DefaultPaperDimension %s!\n" + _(" **FAIL** BAD DefaultPaperDimension %s!\n" " REF: Page 103, section 5.15.\n"), attr->value); - } + } errors ++; } @@ -407,8 +459,8 @@ main(int argc, /* I - Number of command-line arguments */ if (option->defchoice[0]) { - if (ppdFindChoice(option, option->defchoice) == NULL && - strcmp(option->defchoice, "Unknown")) + if (ppdFindChoice(option, option->defchoice) == NULL && + strcmp(option->defchoice, "Unknown")) { if (verbose >= 0) { @@ -416,16 +468,16 @@ main(int argc, /* I - Number of command-line arguments */ _cupsLangPuts(stdout, _(" FAIL\n")); _cupsLangPrintf(stdout, - _(" **FAIL** BAD Default%s %s\n" + _(" **FAIL** BAD Default%s %s\n" " REF: Page 40, section 4.5.\n"), option->keyword, option->defchoice); - } + } errors ++; } else if (verbose > 0) _cupsLangPrintf(stdout, - _(" PASS Default%s\n"), + _(" PASS Default%s\n"), option->keyword); } else @@ -436,10 +488,10 @@ main(int argc, /* I - Number of command-line arguments */ _cupsLangPuts(stdout, _(" FAIL\n")); _cupsLangPrintf(stdout, - _(" **FAIL** REQUIRED Default%s\n" - " REF: Page 40, section 4.5.\n"), - option->keyword); - } + _(" **FAIL** REQUIRED Default%s\n" + " REF: Page 40, section 4.5.\n"), + option->keyword); + } errors ++; } @@ -543,6 +595,22 @@ main(int argc, /* I - Number of command-line arguments */ errors ++; } + else if (!strncasecmp(ppd->manufacturer, "OkiData", 7) || + !strncasecmp(ppd->manufacturer, "Oki Data", 8)) + { + if (verbose >= 0) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPuts(stdout, + _(" **FAIL** BAD Manufacturer (should be " + "\"Oki\")\n" + " REF: Page 211, table D.1.\n")); + } + + errors ++; + } else if (verbose > 0) _cupsLangPuts(stdout, _(" PASS Manufacturer\n")); } @@ -913,7 +981,8 @@ main(int argc, /* I - Number of command-line arguments */ else ydpi = xdpi; - if (xdpi <= 0 || ydpi <= 0 || strcmp(ptr, "dpi")) + if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 || + strcmp(ptr, "dpi")) { if (verbose >= 0) { @@ -980,18 +1049,197 @@ main(int argc, /* I - Number of command-line arguments */ } } + if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) && + strcmp(attr->name, "1284DeviceID")) + { + if (verbose >= 0) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, + _(" **FAIL** %s must be 1284DeviceID!\n" + " REF: Page 72, section 5.5\n"), + attr->name); + } + + errors ++; + } + + if (!(warn & WARN_CONSTRAINTS)) + errors = check_constraints(ppd, errors, verbose, 0); + + if (!(warn & WARN_FILTERS)) + errors = check_filters(ppd, root, errors, verbose, 0); + + if (!(warn & WARN_TRANSLATIONS)) + errors = check_translations(ppd, errors, verbose, 0); + + if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL && + attr->value) + { + /* + * This file contains localizations, check for conformance of the + * base translation... + */ + + if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL) + { + if (!attr->value || strcmp(attr->value, "ISOLatin1")) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" **FAIL** Bad LanguageEncoding %s - " + "must be ISOLatin1!\n"), + attr->value ? attr->value : "(null)"); + + errors ++; + } + + if (!ppd->lang_version || strcmp(ppd->lang_version, "English")) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" **FAIL** Bad LanguageVersion %s - " + "must be English!\n"), + ppd->lang_version ? ppd->lang_version : "(null)"); + + errors ++; + } + + /* + * Loop through all options and choices... + */ + + for (option = ppdFirstOption(ppd); + option; + option = ppdNextOption(ppd)) + { + /* + * Check for special characters outside A0 to BF, F7, or F8 + * that are used for languages other than English. + */ + + for (ptr = option->text; *ptr; ptr ++) + if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 && + (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8) + break; + + if (*ptr) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" **FAIL** Default translation " + "string for option %s contains 8-bit " + "characters!\n"), + option->keyword); + + errors ++; + } + + for (j = 0; j < option->num_choices; j ++) + { + /* + * Check for special characters outside A0 to BF, F7, or F8 + * that are used for languages other than English. + */ + + for (ptr = option->choices[j].text; *ptr; ptr ++) + if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 && + (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8) + break; + + if (*ptr) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" **FAIL** Default translation " + "string for option %s choice %s contains " + "8-bit characters!\n"), + option->keyword, + option->choices[j].choice); + + errors ++; + } + } + } + } + } + + /* + * Final pass/fail notification... + */ + if (errors) status = ERROR_CONFORMANCE; else if (!verbose) _cupsLangPuts(stdout, _(" PASS\n")); - + if (verbose >= 0) { check_basics(argv[i]); - if (option && - strcmp(option->keyword, "Duplex") && - strcmp(option->keyword, "JCLDuplex")) + if (warn & WARN_CONSTRAINTS) + errors = check_constraints(ppd, errors, verbose, 1); + + if (warn & WARN_DEFAULTS) + errors = check_defaults(ppd, errors, verbose, 1); + + if (warn & WARN_FILTERS) + errors = check_filters(ppd, root, errors, verbose, 1); + + if (warn & WARN_TRANSLATIONS) + errors = check_translations(ppd, errors, verbose, 1); + + /* + * Look for default keywords with no corresponding option... + */ + + for (j = 0; j < ppd->num_attrs; j ++) + { + attr = ppd->attrs[j]; + + if (!strcmp(attr->name, "DefaultColorSpace") || + !strcmp(attr->name, "DefaultColorSep") || + !strcmp(attr->name, "DefaultFont") || + !strcmp(attr->name, "DefaultHalftoneType") || + !strcmp(attr->name, "DefaultImageableArea") || + !strcmp(attr->name, "DefaultLeadingEdge") || + !strcmp(attr->name, "DefaultOutputOrder") || + !strcmp(attr->name, "DefaultPaperDimension") || + !strcmp(attr->name, "DefaultResolution") || + !strcmp(attr->name, "DefaultScreenProc") || + !strcmp(attr->name, "DefaultTransfer")) + continue; + + if (!strncmp(attr->name, "Default", 7) && + !ppdFindOption(ppd, attr->name + 7)) + _cupsLangPrintf(stdout, + _(" WARN %s has no corresponding " + "options!\n"), + attr->name); + } + + /* + * Check for old Duplex option names... + */ + + if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL) + option = ppdFindOption(ppd, "KD03Duplex"); + + if (option) { _cupsLangPrintf(stdout, _(" WARN Duplex option keyword %s " @@ -1112,11 +1360,77 @@ main(int argc, /* I - Number of command-line arguments */ } } + /* + * cupsICCProfile + */ + + for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); + attr != NULL; + attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) + { + if (attr->value) + { + if (attr->value[0] == '/') + snprintf(pathprog, sizeof(pathprog), "%s%s", root, attr->value); + else + { + if ((ptr = getenv("CUPS_DATADIR")) == NULL) + ptr = CUPS_DATADIR; + + if (*ptr == '/' || !*root) + snprintf(pathprog, sizeof(pathprog), "%s%s/profiles/%s", root, + ptr, attr->value); + else + snprintf(pathprog, sizeof(pathprog), "%s/%s/profiles/%s", root, + ptr, attr->value); + } + } + + if (!attr->value || !attr->value[0] || stat(pathprog, &statbuf)) + { + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" WARN Missing cupsICCProfile " + "file \"%s\"\n"), + !attr->value || !attr->value[0] ? "" : + attr->value); + } + } + +#ifdef __APPLE__ + /* + * APDialogExtension + */ + + for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL); + attr != NULL; + attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL)) + { + if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0) + _cupsLangPrintf(stdout, _(" WARN Missing " + "APDialogExtension file \"%s\"\n"), + attr->value ? attr->value : ""); + } + + /* + * APPrinterIconPath + */ + + for (attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL); + attr != NULL; + attr = ppdFindNextAttr(ppd, "APPrinterIconPath", NULL)) + { + if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0) + _cupsLangPrintf(stdout, _(" WARN Missing " + "APPrinterIconPath file \"%s\"\n"), + attr->value ? attr->value : ""); + } +#endif /* __APPLE__ */ + if (verbose > 0) { if (errors) - _cupsLangPrintf(stdout, _(" %d ERROR%s FOUND\n"), - errors, errors == 1 ? "" : "S"); + _cupsLangPrintf(stdout, _(" %d ERRORS FOUND\n"), errors); else _cupsLangPuts(stdout, _(" NO ERRORS FOUND\n")); } @@ -1240,6 +1554,14 @@ main(int argc, /* I - Number of command-line arguments */ } } + _cupsLangPrintf(stdout, " num_consts = %d\n", + ppd->num_consts); + for (j = 0; j < ppd->num_consts; j ++) + _cupsLangPrintf(stdout, + " consts[%d] = *%s %s *%s %s\n", + j, ppd->consts[j].option1, ppd->consts[j].choice1, + ppd->consts[j].option2, ppd->consts[j].choice2); + _cupsLangPrintf(stdout, " num_profiles = %d\n", ppd->num_profiles); for (j = 0; j < ppd->num_profiles; j ++) @@ -1289,7 +1611,7 @@ main(int argc, /* I - Number of command-line arguments */ * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines. */ -void +static void check_basics(const char *filename) /* I - PPD file to check */ { cups_file_t *fp; /* File pointer */ @@ -1370,11 +1692,592 @@ check_basics(const char *filename) /* I - PPD file to check */ } +/* + * 'check_constraints()' - Check UIConstraints in the PPD file. + */ + +static int /* O - Errors found */ +check_constraints(ppd_file_t *ppd, /* I - PPD file */ + int errors, /* I - Errors found */ + int verbose, /* I - Verbosity level */ + int warn) /* I - Warnings only? */ +{ + int j; /* Looping var */ + ppd_const_t *c; /* Current constraint */ + ppd_option_t *option; /* Standard UI option */ + ppd_option_t *option2; /* Standard UI option */ + const char *prefix; /* WARN/FAIL prefix */ + + + prefix = warn ? " WARN " : "**FAIL**"; + + for (j = ppd->num_consts, c = ppd->consts; j > 0; j --, c ++) + { + option = ppdFindOption(ppd, c->option1); + option2 = ppdFindOption(ppd, c->option2); + + if (!option || !option2) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (!option) + _cupsLangPrintf(stdout, + _(" %s Missing option %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + prefix, c->option1, + c->option1, c->choice1, c->option2, c->choice2); + + if (!option2) + _cupsLangPrintf(stdout, + _(" %s Missing option %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + prefix, c->option2, + c->option1, c->choice1, c->option2, c->choice2); + + if (!warn) + errors ++; + + continue; + } + + if (c->choice1[0] && !ppdFindChoice(option, c->choice1)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, + _(" %s Missing choice *%s %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + prefix, c->option1, c->choice1, + c->option1, c->choice1, c->option2, c->choice2); + + if (!warn) + errors ++; + } + + if (c->choice2[0] && !ppdFindChoice(option2, c->choice2)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, + _(" %s Missing choice *%s %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + prefix, c->option2, c->choice2, + c->option1, c->choice1, c->option2, c->choice2); + + if (!warn) + errors ++; + } + } + + return (errors); +} + + +/* + * 'check_defaults()' - Check default option keywords in the PPD file. + */ + +static int /* O - Errors found */ +check_defaults(ppd_file_t *ppd, /* I - PPD file */ + int errors, /* I - Errors found */ + int verbose, /* I - Verbosity level */ + int warn) /* I - Warnings only? */ +{ + int j, k; /* Looping vars */ + ppd_attr_t *attr; /* PPD attribute */ + ppd_option_t *option; /* Standard UI option */ + const char *prefix; /* WARN/FAIL prefix */ + + + prefix = warn ? " WARN " : "**FAIL**"; + + for (j = 0; j < ppd->num_attrs; j ++) + { + attr = ppd->attrs[j]; + + if (!strcmp(attr->name, "DefaultColorSpace") || + !strcmp(attr->name, "DefaultFont") || + !strcmp(attr->name, "DefaultHalftoneType") || + !strcmp(attr->name, "DefaultImageableArea") || + !strcmp(attr->name, "DefaultLeadingEdge") || + !strcmp(attr->name, "DefaultOutputOrder") || + !strcmp(attr->name, "DefaultPaperDimension") || + !strcmp(attr->name, "DefaultResolution") || + !strcmp(attr->name, "DefaultTransfer")) + continue; + + if (!strncmp(attr->name, "Default", 7)) + { + if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL && + strcmp(attr->value, "Unknown")) + { + /* + * Check that the default option value matches a choice... + */ + + for (k = 0; k < option->num_choices; k ++) + if (!strcmp(option->choices[k].choice, attr->value)) + break; + + if (k >= option->num_choices) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s %s %s does not exist!\n"), + prefix, attr->name, attr->value); + + if (!warn) + errors ++; + } + } + } + } + + return (errors); +} + + +/* + * 'check_filters()' - Check filters in the PPD file. + */ + +static int /* O - Errors found */ +check_filters(ppd_file_t *ppd, /* I - PPD file */ + const char *root, /* I - Root directory */ + int errors, /* I - Errors found */ + int verbose, /* I - Verbosity level */ + int warn) /* I - Warnings only? */ +{ + ppd_attr_t *attr; /* PPD attribute */ + const char *ptr; /* Pointer into string */ + struct stat statbuf; /* File information */ + char super[16], /* Super-type for filter */ + type[256], /* Type for filter */ + program[256], /* Program/filter name */ + pathprog[1024]; /* Complete path to program/filter */ + int cost; /* Cost of filter */ + const char *prefix; /* WARN/FAIL prefix */ + + + prefix = warn ? " WARN " : "**FAIL**"; + + for (attr = ppdFindAttr(ppd, "cupsFilter", NULL); + attr; + attr = ppdFindNextAttr(ppd, "cupsFilter", NULL)) + { + if (!attr->value || + sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost, + program) != 4) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Bad cupsFilter value \"%s\"!\n"), + prefix, attr->value ? attr->value : ""); + + if (!warn) + errors ++; + } + else + { + if (program[0] == '/') + snprintf(pathprog, sizeof(pathprog), "%s%s", root, program); + else + { + if ((ptr = getenv("CUPS_SERVERBIN")) == NULL) + ptr = CUPS_SERVERBIN; + + if (*ptr == '/' || !*root) + snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr, + program); + else + snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr, + program); + } + + if (stat(pathprog, &statbuf)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, _(" %s Missing cupsFilter " + "file \"%s\"\n"), prefix, program); + + if (!warn) + errors ++; + } + } + } + + for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL); + attr; + attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) + { + if (!attr->value || + sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost, + program) != 4) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Bad cupsPreFilter value \"%s\"!\n"), + prefix, attr->value ? attr->value : ""); + + if (!warn) + errors ++; + } + else + { + if (program[0] == '/') + snprintf(pathprog, sizeof(pathprog), "%s%s", root, program); + else + { + if ((ptr = getenv("CUPS_SERVERBIN")) == NULL) + ptr = CUPS_SERVERBIN; + + if (*ptr == '/' || !*root) + snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr, + program); + else + snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr, + program); + } + + if (stat(pathprog, &statbuf)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, _(" %s Missing cupsPreFilter " + "file \"%s\"\n"), prefix, program); + + if (!warn) + errors ++; + } + } + } + + return (errors); +} + + +/* + * 'check_translations()' - Check translations in the PPD file. + */ + +static int /* O - Errors found */ +check_translations(ppd_file_t *ppd, /* I - PPD file */ + int errors, /* I - Errors found */ + int verbose, /* I - Verbosity level */ + int warn) /* I - Warnings only? */ +{ + int j; /* Looping var */ + ppd_attr_t *attr; /* PPD attribute */ + char *languages, /* Copy of attribute value */ + *langstart, /* Start of current language */ + *langptr, /* Pointer into languages */ + keyword[PPD_MAX_NAME], /* Localization keyword (full) */ + llkeyword[PPD_MAX_NAME],/* Localization keyword (base) */ + ckeyword[PPD_MAX_NAME], /* Custom option keyword (full) */ + cllkeyword[PPD_MAX_NAME]; + /* Custom option keyword (base) */ + ppd_option_t *option; /* Standard UI option */ + ppd_coption_t *coption; /* Custom option */ + ppd_cparam_t *cparam; /* Custom parameter */ + cups_array_t *langlist; /* List of languages so far */ + char ll[3]; /* Base language */ + const char *prefix; /* WARN/FAIL prefix */ + + + prefix = warn ? " WARN " : "**FAIL**"; + + if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL && + attr->value) + { + /* + * This file contains localizations, check them... + */ + + languages = strdup(attr->value); + langlist = cupsArrayNew((cups_array_func_t)strcmp, NULL); + + for (langptr = languages; *langptr;) + { + /* + * Skip leading whitespace... + */ + + while (isspace(*langptr & 255)) + langptr ++; + + if (!*langptr) + break; + + /* + * Find the end of this language name... + */ + + for (langstart = langptr; + *langptr && !isspace(*langptr & 255); + langptr ++); + + if (*langptr) + *langptr++ = '\0'; + + j = strlen(langstart); + if (j != 2 && j != 5) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Bad language \"%s\"!\n"), + prefix, langstart); + + if (!warn) + errors ++; + + continue; + } + + if (!strcmp(langstart, "en")) + continue; + + cupsArrayAdd(langlist, langstart); + + strlcpy(ll, langstart, sizeof(ll)); + + /* + * Loop through all options and choices... + */ + + for (option = ppdFirstOption(ppd); + option; + option = ppdNextOption(ppd)) + { + if (!strcmp(option->keyword, "PageRegion")) + continue; + + snprintf(keyword, sizeof(keyword), "%s.Translation", langstart); + snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll); + + if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL && + (attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Missing \"%s\" translation " + "string for option %s!\n"), + prefix, langstart, option->keyword); + + if (!warn) + errors ++; + } + else if (!valid_utf8(attr->text)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Bad UTF-8 \"%s\" translation " + "string for option %s!\n"), + prefix, langstart, option->keyword); + + if (!warn) + errors ++; + } + + snprintf(keyword, sizeof(keyword), "%s.%s", langstart, + option->keyword); + snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll, + option->keyword); + + for (j = 0; j < option->num_choices; j ++) + { + if (!strcasecmp(option->choices[j].choice, "Custom") && + (coption = ppdFindCustomOption(ppd, + option->keyword)) != NULL) + { + snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s", + langstart, option->keyword); + + if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL && + !valid_utf8(attr->text)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Bad UTF-8 \"%s\" " + "translation string for option %s, " + "choice %s!\n"), + prefix, langstart, + ckeyword + 1 + strlen(langstart), + "True"); + + if (!warn) + errors ++; + } + + if (strcasecmp(option->keyword, "PageSize")) + { + for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); + cparam; + cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) + { + snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s", + langstart, option->keyword); + snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s", + ll, option->keyword); + + if ((attr = ppdFindAttr(ppd, ckeyword, + cparam->name)) == NULL && + (attr = ppdFindAttr(ppd, cllkeyword, + cparam->name)) == NULL) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Missing \"%s\" " + "translation string for option %s, " + "choice %s!\n"), + prefix, langstart, + ckeyword + 1 + strlen(langstart), + cparam->name); + + if (!warn) + errors ++; + } + else if (!valid_utf8(attr->text)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Bad UTF-8 \"%s\" " + "translation string for option %s, " + "choice %s!\n"), + prefix, langstart, + ckeyword + 1 + strlen(langstart), + cparam->name); + + if (!warn) + errors ++; + } + } + } + } + else if ((attr = ppdFindAttr(ppd, keyword, + option->choices[j].choice)) == NULL && + (attr = ppdFindAttr(ppd, llkeyword, + option->choices[j].choice)) == NULL) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Missing \"%s\" " + "translation string for option %s, " + "choice %s!\n"), + prefix, langstart, option->keyword, + option->choices[j].choice); + + if (!warn) + errors ++; + } + else if (!valid_utf8(attr->text)) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s Bad UTF-8 \"%s\" " + "translation string for option %s, " + "choice %s!\n"), + prefix, langstart, option->keyword, + option->choices[j].choice); + + if (!warn) + errors ++; + } + } + } + } + + /* + * Verify that we have the base language for each localized one... + */ + + for (langptr = (char *)cupsArrayFirst(langlist); + langptr; + langptr = (char *)cupsArrayNext(langlist)) + if (langptr[2]) + { + /* + * Lookup the base language... + */ + + cupsArraySave(langlist); + + strlcpy(ll, langptr, sizeof(ll)); + + if (!cupsArrayFind(langlist, ll) && strcmp(ll, "zh")) + { + if (!warn && !errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" %s No base translation \"%s\" " + "is included in file!\n"), prefix, ll); + + if (!warn) + errors ++; + } + + cupsArrayRestore(langlist); + } + + /* + * Free memory used for the languages... + */ + + cupsArrayDelete(langlist); + free(languages); + } + + return (errors); +} + + /* * 'show_conflicts()' - Show option conflicts in a PPD file. */ -void +static void show_conflicts(ppd_file_t *ppd) /* I - PPD to check */ { int i, j; /* Looping variables */ @@ -1470,22 +2373,159 @@ show_conflicts(ppd_file_t *ppd) /* I - PPD to check */ } +/* + * 'test_raster()' - Test PostScript commands for raster printers. + */ + +static int /* O - 1 on success, 0 on failure */ +test_raster(ppd_file_t *ppd, /* I - PPD file */ + int verbose) /* I - Verbosity */ +{ + cups_page_header2_t header; /* Page header */ + + + ppdMarkDefaults(ppd); + if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0)) + { + if (!verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" **FAIL** Default option code cannot be " + "interpreted: %s\n"), cupsRasterErrorString()); + + return (0); + } + + /* + * Try a test of custom page size code, if available... + */ + + if (!ppdPageSize(ppd, "Custom.612x792")) + return (1); + + ppdMarkOption(ppd, "PageSize", "Custom.612x792"); + + if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0)) + { + if (!verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" **FAIL** Default option code cannot be " + "interpreted: %s\n"), cupsRasterErrorString()); + + return (0); + } + + return (1); +} + + /* * 'usage()' - Show program usage... */ -void +static void usage(void) { _cupsLangPuts(stdout, - _("Usage: cupstestppd [-q] [-r] [-v[v]] filename1.ppd[.gz] " + _("Usage: cupstestppd [options] filename1.ppd[.gz] " "[... filenameN.ppd[.gz]]\n" - " program | cupstestppd [-q] [-r] [-v[v]] -\n")); + " program | cupstestppd [options] -\n" + "\n" + "Options:\n" + "\n" + " -R root-directory Set alternate root\n" + " -W {all,none,constraints,defaults,filters,translations}\n" + " Issue warnings instead of errors\n" + " -q Run silently\n" + " -r Use 'relaxed' open mode\n" + " -v Be slightly verbose\n" + " -vv Be very verbose\n")); exit(ERROR_USAGE); } /* - * End of "$Id: cupstestppd.c 4990 2006-01-26 02:21:45Z mike $". + * 'valid_utf8()' - Check whether a string contains valid UTF-8 text. + */ + +static int /* O - 1 if valid, 0 if not */ +valid_utf8(const char *s) /* I - String to check */ +{ + while (*s) + { + if (*s & 0x80) + { + /* + * Check for valid UTF-8 sequence... + */ + + if ((*s & 0xc0) == 0x80) + return (0); /* Illegal suffix byte */ + else if ((*s & 0xe0) == 0xc0) + { + /* + * 2-byte sequence... + */ + + s ++; + + if ((*s & 0xc0) != 0x80) + return (0); /* Missing suffix byte */ + } + else if ((*s & 0xf0) == 0xe0) + { + /* + * 3-byte sequence... + */ + + s ++; + + if ((*s & 0xc0) != 0x80) + return (0); /* Missing suffix byte */ + + s ++; + + if ((*s & 0xc0) != 0x80) + return (0); /* Missing suffix byte */ + } + else if ((*s & 0xf8) == 0xf0) + { + /* + * 4-byte sequence... + */ + + s ++; + + if ((*s & 0xc0) != 0x80) + return (0); /* Missing suffix byte */ + + s ++; + + if ((*s & 0xc0) != 0x80) + return (0); /* Missing suffix byte */ + + s ++; + + if ((*s & 0xc0) != 0x80) + return (0); /* Missing suffix byte */ + } + else + return (0); /* Bad sequence */ + } + + s ++; + } + + return (1); +} + + +/* + * End of "$Id: cupstestppd.c 6927 2007-09-07 16:51:00Z mike $". */