| /* |
| * PPD test program for CUPS. |
| * |
| * THIS PROGRAM IS DEPRECATED AND WILL BE REMOVED IN A FUTURE VERSION OF CUPS. |
| * |
| * Copyright © 2007-2018 by Apple Inc. |
| * Copyright © 1997-2007 by Easy Software Products, all rights reserved. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more |
| * information. |
| * |
| * PostScript is a trademark of Adobe Systems, Inc. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include <cups/cups-private.h> |
| #include <cups/dir.h> |
| #include <cups/ppd-private.h> |
| #include <cups/raster.h> |
| #include <math.h> |
| #ifdef _WIN32 |
| # define X_OK 0 |
| #endif /* _WIN32 */ |
| |
| |
| /* |
| * Error warning overrides... |
| */ |
| |
| enum |
| { |
| WARN_NONE = 0, |
| WARN_CONSTRAINTS = 1, |
| WARN_DEFAULTS = 2, |
| WARN_FILTERS = 4, |
| WARN_PROFILES = 8, |
| WARN_TRANSLATIONS = 16, |
| WARN_DUPLEX = 32, |
| WARN_SIZES = 64, |
| WARN_FILENAME = 128, |
| WARN_ALL = 255 |
| }; |
| |
| |
| /* |
| * Error codes... |
| */ |
| |
| enum |
| { |
| ERROR_NONE = 0, |
| ERROR_USAGE, |
| ERROR_FILE_OPEN, |
| ERROR_PPD_FORMAT, |
| ERROR_CONFORMANCE |
| }; |
| |
| |
| /* |
| * Line endings... |
| */ |
| |
| enum |
| { |
| EOL_NONE = 0, |
| EOL_CR, |
| EOL_LF, |
| EOL_CRLF |
| }; |
| |
| |
| /* |
| * File permissions... |
| */ |
| |
| #define MODE_WRITE 0022 /* Group/other write */ |
| #define MODE_MASK 0555 /* Owner/group/other read+exec/search */ |
| #define MODE_DATAFILE 0444 /* Owner/group/other read */ |
| #define MODE_DIRECTORY 0555 /* Owner/group/other read+search */ |
| #define MODE_PROGRAM 0555 /* Owner/group/other read+exec */ |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static void check_basics(const char *filename); |
| static int check_constraints(ppd_file_t *ppd, int errors, int verbose, |
| int warn); |
| static int check_case(ppd_file_t *ppd, int errors, int verbose); |
| static int check_defaults(ppd_file_t *ppd, int errors, int verbose, |
| int warn); |
| static int check_duplex(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_profiles(ppd_file_t *ppd, const char *root, int errors, |
| int verbose, int warn); |
| static int check_sizes(ppd_file_t *ppd, 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, const char *prefix); |
| static int test_raster(ppd_file_t *ppd, int verbose); |
| static void usage(void) _CUPS_NORETURN; |
| static int valid_path(const char *keyword, const char *path, int errors, |
| int verbose, int warn); |
| 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 args */ |
| char *argv[]) /* I - Command-line arguments */ |
| { |
| int i, j, k, m, n; /* Looping vars */ |
| size_t len; /* Length of option name */ |
| char *opt; /* Option character */ |
| const char *ptr; /* Pointer into string */ |
| cups_file_t *fp; /* PPD file */ |
| int files; /* Number of files */ |
| int verbose; /* Want verbose output? */ |
| int warn; /* Which errors to just warn about */ |
| int ignore; /* Which errors to ignore */ |
| 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 */ |
| char *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 */ |
| struct lconv *loc; /* Locale data */ |
| static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" }; |
| static char *sections[] = { "ANY", "DOCUMENT", "EXIT", |
| "JCL", "PAGE", "PROLOG" }; |
| |
| |
| _cupsSetLocale(argv); |
| loc = localeconv(); |
| |
| /* |
| * Display PPD files for each file listed on the command-line... |
| */ |
| |
| ppdSetConformance(PPD_CONFORM_STRICT); |
| |
| verbose = 0; |
| ppd = NULL; |
| files = 0; |
| status = ERROR_NONE; |
| root = ""; |
| warn = WARN_NONE; |
| ignore = WARN_NONE; |
| |
| for (i = 1; i < argc; i ++) |
| if (!strcmp(argv[i], "--help")) |
| usage(); |
| else if (argv[i][0] == '-' && argv[i][1]) |
| { |
| for (opt = argv[i] + 1; *opt; opt ++) |
| switch (*opt) |
| { |
| case 'I' : /* Ignore errors */ |
| i ++; |
| |
| if (i >= argc) |
| usage(); |
| |
| if (!strcmp(argv[i], "none")) |
| ignore = WARN_NONE; |
| else if (!strcmp(argv[i], "filename")) |
| ignore |= WARN_FILENAME; |
| else if (!strcmp(argv[i], "filters")) |
| ignore |= WARN_FILTERS; |
| else if (!strcmp(argv[i], "profiles")) |
| ignore |= WARN_PROFILES; |
| else if (!strcmp(argv[i], "all")) |
| ignore = WARN_FILTERS | WARN_PROFILES; |
| else |
| usage(); |
| break; |
| |
| 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], "duplex")) |
| warn |= WARN_DUPLEX; |
| else if (!strcmp(argv[i], "filters")) |
| warn |= WARN_FILTERS; |
| else if (!strcmp(argv[i], "profiles")) |
| warn |= WARN_PROFILES; |
| else if (!strcmp(argv[i], "sizes")) |
| warn |= WARN_SIZES; |
| 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) |
| { |
| _cupsLangPuts(stderr, |
| _("cupstestppd: The -q option is incompatible " |
| "with the -v option.")); |
| return (1); |
| } |
| |
| verbose --; |
| break; |
| |
| case 'r' : /* Relaxed mode */ |
| ppdSetConformance(PPD_CONFORM_RELAXED); |
| break; |
| |
| case 'v' : /* Verbose mode */ |
| if (verbose < 0) |
| { |
| _cupsLangPuts(stderr, |
| _("cupstestppd: The -v option is incompatible " |
| "with the -q option.")); |
| return (1); |
| } |
| |
| verbose ++; |
| break; |
| |
| default : |
| usage(); |
| break; |
| } |
| } |
| else |
| { |
| /* |
| * Open the PPD file... |
| */ |
| |
| if (files && verbose >= 0) |
| puts(""); |
| |
| files ++; |
| |
| if (argv[i][0] == '-') |
| { |
| /* |
| * Read from stdin... |
| */ |
| |
| ppd = _ppdOpen(cupsFileStdin(), _PPD_LOCALIZATION_ALL); |
| |
| if (verbose >= 0) |
| printf("%s:", (ppd && ppd->pcfilename) ? ppd->pcfilename : "(stdin)"); |
| } |
| else |
| { |
| /* |
| * Read from a file... |
| */ |
| |
| if (verbose >= 0) |
| printf("%s:", argv[i]); |
| |
| if ((fp = cupsFileOpen(argv[i], "r")) != NULL) |
| { |
| ppd = _ppdOpen(fp, _PPD_LOCALIZATION_ALL); |
| cupsFileClose(fp); |
| } |
| else |
| { |
| status = ERROR_FILE_OPEN; |
| |
| if (verbose >= 0) |
| { |
| _cupsLangPuts(stdout, _(" FAIL")); |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Unable to open PPD file - %s on " |
| "line %d."), strerror(errno), 0); |
| continue; |
| } |
| } |
| } |
| |
| if (ppd == NULL) |
| { |
| error = ppdLastError(&line); |
| |
| if (error <= PPD_ALLOC_ERROR) |
| { |
| status = ERROR_FILE_OPEN; |
| |
| if (verbose >= 0) |
| { |
| _cupsLangPuts(stdout, _(" FAIL")); |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Unable to open PPD file - %s on " |
| "line %d."), strerror(errno), 0); |
| } |
| } |
| else |
| { |
| status = ERROR_PPD_FORMAT; |
| |
| if (verbose >= 0) |
| { |
| _cupsLangPuts(stdout, _(" FAIL")); |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Unable to open PPD file - " |
| "%s on line %d."), |
| ppdErrorString(error), line); |
| |
| switch (error) |
| { |
| case PPD_MISSING_PPDADOBE4 : |
| _cupsLangPuts(stdout, |
| _(" REF: Page 42, section " |
| "5.2.")); |
| break; |
| case PPD_MISSING_VALUE : |
| _cupsLangPuts(stdout, |
| _(" REF: Page 20, section " |
| "3.4.")); |
| break; |
| case PPD_BAD_OPEN_GROUP : |
| case PPD_NESTED_OPEN_GROUP : |
| _cupsLangPuts(stdout, |
| _(" REF: Pages 45-46, section " |
| "5.2.")); |
| break; |
| case PPD_BAD_OPEN_UI : |
| case PPD_NESTED_OPEN_UI : |
| _cupsLangPuts(stdout, |
| _(" REF: Pages 42-45, section " |
| "5.2.")); |
| break; |
| case PPD_BAD_ORDER_DEPENDENCY : |
| _cupsLangPuts(stdout, |
| _(" REF: Pages 48-49, section " |
| "5.2.")); |
| break; |
| case PPD_BAD_UI_CONSTRAINTS : |
| _cupsLangPuts(stdout, |
| _(" REF: Pages 52-54, section " |
| "5.2.")); |
| break; |
| case PPD_MISSING_ASTERISK : |
| _cupsLangPuts(stdout, |
| _(" REF: Page 15, section " |
| "3.2.")); |
| break; |
| case PPD_LINE_TOO_LONG : |
| _cupsLangPuts(stdout, |
| _(" REF: Page 15, section " |
| "3.1.")); |
| break; |
| case PPD_ILLEGAL_CHARACTER : |
| _cupsLangPuts(stdout, |
| _(" REF: Page 15, section " |
| "3.1.")); |
| break; |
| case PPD_ILLEGAL_MAIN_KEYWORD : |
| _cupsLangPuts(stdout, |
| _(" REF: Pages 16-17, section " |
| "3.2.")); |
| break; |
| case PPD_ILLEGAL_OPTION_KEYWORD : |
| _cupsLangPuts(stdout, |
| _(" REF: Page 19, section " |
| "3.3.")); |
| break; |
| case PPD_ILLEGAL_TRANSLATION : |
| _cupsLangPuts(stdout, |
| _(" REF: Page 27, section " |
| "3.5.")); |
| break; |
| default : |
| break; |
| } |
| |
| check_basics(argv[i]); |
| } |
| } |
| |
| continue; |
| } |
| |
| /* |
| * Show the header and then perform basic conformance tests (limited |
| * only by what the CUPS PPD functions actually load...) |
| */ |
| |
| errors = 0; |
| ppdversion = 43; |
| |
| if (verbose > 0) |
| _cupsLangPuts(stdout, |
| _("\n DETAILED CONFORMANCE TEST RESULTS")); |
| |
| if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL && |
| attr->value) |
| ppdversion = (int)(10 * _cupsStrScand(attr->value, NULL, loc) + 0.5); |
| |
| if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL) |
| { |
| do |
| { |
| if (strstr(attr->value, "application/vnd.cups-raster")) |
| { |
| if (!test_raster(ppd, verbose)) |
| errors ++; |
| break; |
| } |
| } |
| while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL); |
| } |
| else |
| { |
| for (j = 0; j < ppd->num_filters; j ++) |
| if (strstr(ppd->filters[j], "application/vnd.cups-raster")) |
| { |
| if (!test_raster(ppd, verbose)) |
| errors ++; |
| break; |
| } |
| } |
| |
| /* |
| * 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) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED DefaultImageableArea\n" |
| " REF: Page 102, section 5.15.")); |
| } |
| |
| errors ++; |
| } |
| else if (ppdPageSize(ppd, attr->value) == NULL && |
| strcmp(attr->value, "Unknown")) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad DefaultImageableArea %s\n" |
| " REF: Page 102, section 5.15."), |
| attr->value); |
| } |
| |
| errors ++; |
| } |
| else |
| { |
| if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS DefaultImageableArea")); |
| } |
| |
| if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED DefaultPaperDimension\n" |
| " REF: Page 103, section 5.15.")); |
| } |
| |
| errors ++; |
| } |
| else if (ppdPageSize(ppd, attr->value) == NULL && |
| strcmp(attr->value, "Unknown")) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad DefaultPaperDimension %s\n" |
| " REF: Page 103, section 5.15."), |
| attr->value); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension")); |
| |
| for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++) |
| for (k = 0, option = group->options; |
| k < group->num_options; |
| k ++, option ++) |
| { |
| /* |
| * Verify that we have a default choice... |
| */ |
| |
| if (option->defchoice[0]) |
| { |
| if (ppdFindChoice(option, option->defchoice) == NULL && |
| strcmp(option->defchoice, "Unknown")) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad Default%s %s\n" |
| " REF: Page 40, section 4.5."), |
| option->keyword, option->defchoice); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPrintf(stdout, |
| _(" PASS Default%s"), |
| option->keyword); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** REQUIRED Default%s\n" |
| " REF: Page 40, section 4.5."), |
| option->keyword); |
| } |
| |
| errors ++; |
| } |
| } |
| |
| if ((attr = ppdFindAttr(ppd, "FileVersion", NULL)) != NULL) |
| { |
| for (ptr = attr->value; *ptr; ptr ++) |
| if (!isdigit(*ptr & 255) && *ptr != '.') |
| break; |
| |
| if (*ptr) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad FileVersion \"%s\"\n" |
| " REF: Page 56, section 5.3."), |
| attr->value); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS FileVersion")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED FileVersion\n" |
| " REF: Page 56, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL) |
| { |
| ptr = attr->value; |
| if (*ptr == '4' && ptr[1] == '.') |
| { |
| |
| for (ptr += 2; *ptr; ptr ++) |
| if (!isdigit(*ptr & 255)) |
| break; |
| } |
| |
| if (*ptr) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad FormatVersion \"%s\"\n" |
| " REF: Page 56, section 5.3."), |
| attr->value); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS FormatVersion")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED FormatVersion\n" |
| " REF: Page 56, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->lang_encoding != NULL) |
| { |
| if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS LanguageEncoding")); |
| } |
| else if (ppdversion > 40) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED LanguageEncoding\n" |
| " REF: Pages 56-57, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->lang_version != NULL) |
| { |
| if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS LanguageVersion")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED LanguageVersion\n" |
| " REF: Pages 57-58, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->manufacturer != NULL) |
| { |
| if (!_cups_strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) || |
| !_cups_strncasecmp(ppd->manufacturer, "Hewlett Packard", 15)) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad Manufacturer (should be " |
| "\"%s\")\n" |
| " REF: Page 211, table D.1."), |
| "HP"); |
| } |
| |
| errors ++; |
| } |
| else if (!_cups_strncasecmp(ppd->manufacturer, "OkiData", 7) || |
| !_cups_strncasecmp(ppd->manufacturer, "Oki Data", 8)) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad Manufacturer (should be " |
| "\"%s\")\n" |
| " REF: Page 211, table D.1."), |
| "Oki"); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS Manufacturer")); |
| } |
| else if (ppdversion >= 43) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED Manufacturer\n" |
| " REF: Pages 58-59, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->modelname != NULL) |
| { |
| for (ptr = ppd->modelname; *ptr; ptr ++) |
| if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr)) |
| break; |
| |
| if (*ptr) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad ModelName - \"%c\" not " |
| "allowed in string.\n" |
| " REF: Pages 59-60, section 5.3."), |
| *ptr); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS ModelName")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED ModelName\n" |
| " REF: Pages 59-60, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->nickname != NULL) |
| { |
| if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS NickName")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED NickName\n" |
| " REF: Page 60, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppdFindOption(ppd, "PageSize") != NULL) |
| { |
| if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS PageSize")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED PageSize\n" |
| " REF: Pages 99-100, section 5.14.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppdFindOption(ppd, "PageRegion") != NULL) |
| { |
| if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS PageRegion")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED PageRegion\n" |
| " REF: Page 100, section 5.14.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->pcfilename != NULL) |
| { |
| if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS PCFileName")); |
| } |
| else if (!(ignore & WARN_FILENAME)) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED PCFileName\n" |
| " REF: Pages 61-62, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->product != NULL) |
| { |
| if (ppd->product[0] != '(' || |
| ppd->product[strlen(ppd->product) - 1] != ')') |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** Bad Product - not \"(string)\".\n" |
| " REF: Page 62, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS Product")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED Product\n" |
| " REF: Page 62, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL && |
| attr->value != NULL) |
| { |
| char junkstr[255]; /* Temp string */ |
| int junkint; /* Temp integer */ |
| |
| |
| if (sscanf(attr->value, "(%254[^)\n])%d", junkstr, &junkint) != 2) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** Bad PSVersion - not \"(string) " |
| "int\".\n" |
| " REF: Pages 62-64, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS PSVersion")); |
| } |
| else |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED PSVersion\n" |
| " REF: Pages 62-64, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->shortnickname != NULL) |
| { |
| if (strlen(ppd->shortnickname) > 31) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** Bad ShortNickName - longer " |
| "than 31 chars.\n" |
| " REF: Pages 64-65, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| else if (verbose > 0) |
| _cupsLangPuts(stdout, _(" PASS ShortNickName")); |
| } |
| else if (ppdversion >= 43) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED ShortNickName\n" |
| " REF: Page 64-65, section 5.3.")); |
| } |
| |
| errors ++; |
| } |
| |
| if (ppd->patches != NULL && strchr(ppd->patches, '\"') && |
| strstr(ppd->patches, "*End")) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** Bad JobPatchFile attribute in file\n" |
| " REF: Page 24, section 3.4.")); |
| } |
| |
| errors ++; |
| } |
| |
| /* |
| * Check for page sizes without the corresponding ImageableArea or |
| * PaperDimension values... |
| */ |
| |
| if (ppd->num_sizes == 0) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPuts(stdout, |
| _(" **FAIL** REQUIRED PageSize\n" |
| " REF: Page 41, section 5.\n" |
| " REF: Page 99, section 5.14.")); |
| } |
| |
| errors ++; |
| } |
| else |
| { |
| for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++) |
| { |
| /* |
| * Don't check custom size... |
| */ |
| |
| if (!strcmp(size->name, "Custom")) |
| continue; |
| |
| /* |
| * Check for ImageableArea... |
| */ |
| |
| if (size->left == 0.0 && size->bottom == 0.0 && |
| size->right == 0.0 && size->top == 0.0) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** REQUIRED ImageableArea for " |
| "PageSize %s\n" |
| " REF: Page 41, section 5.\n" |
| " REF: Page 102, section 5.15."), |
| size->name); |
| } |
| |
| errors ++; |
| } |
| |
| /* |
| * Check for PaperDimension... |
| */ |
| |
| if (size->width <= 0.0 && size->length <= 0.0) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** REQUIRED PaperDimension " |
| "for PageSize %s\n" |
| " REF: Page 41, section 5.\n" |
| " REF: Page 103, section 5.15."), |
| size->name); |
| } |
| |
| errors ++; |
| } |
| } |
| } |
| |
| /* |
| * Check for valid Resolution, JCLResolution, or SetResolution values... |
| */ |
| |
| if ((option = ppdFindOption(ppd, "Resolution")) == NULL) |
| if ((option = ppdFindOption(ppd, "JCLResolution")) == NULL) |
| option = ppdFindOption(ppd, "SetResolution"); |
| |
| if (option != NULL) |
| { |
| for (j = option->num_choices, choice = option->choices; |
| j > 0; |
| j --, choice ++) |
| { |
| /* |
| * Verify that all resolution options are of the form NNNdpi |
| * or NNNxNNNdpi... |
| */ |
| |
| xdpi = strtol(choice->choice, (char **)&ptr, 10); |
| if (ptr > choice->choice && xdpi > 0) |
| { |
| if (*ptr == 'x') |
| ydpi = strtol(ptr + 1, (char **)&ptr, 10); |
| else |
| ydpi = xdpi; |
| } |
| else |
| ydpi = xdpi; |
| |
| if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 || |
| strcmp(ptr, "dpi")) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad option %s choice %s\n" |
| " REF: Page 84, section 5.9"), |
| option->keyword, choice->choice); |
| } |
| |
| errors ++; |
| } |
| } |
| } |
| |
| if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) && |
| strcmp(attr->name, "1284DeviceID")) |
| { |
| if (verbose >= 0) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** %s must be 1284DeviceID\n" |
| " REF: Page 72, section 5.5"), |
| attr->name); |
| } |
| |
| errors ++; |
| } |
| |
| errors = check_case(ppd, errors, verbose); |
| |
| if (!(warn & WARN_CONSTRAINTS)) |
| errors = check_constraints(ppd, errors, verbose, 0); |
| |
| if (!(warn & WARN_FILTERS) && !(ignore & WARN_FILTERS)) |
| errors = check_filters(ppd, root, errors, verbose, 0); |
| |
| if (!(warn & WARN_PROFILES) && !(ignore & WARN_PROFILES)) |
| errors = check_profiles(ppd, root, errors, verbose, 0); |
| |
| if (!(warn & WARN_SIZES)) |
| errors = check_sizes(ppd, errors, verbose, 0); |
| |
| if (!(warn & WARN_TRANSLATIONS)) |
| errors = check_translations(ppd, errors, verbose, 0); |
| |
| if (!(warn & WARN_DUPLEX)) |
| errors = check_duplex(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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad LanguageEncoding %s - " |
| "must be ISOLatin1."), |
| attr->value ? attr->value : "(null)"); |
| |
| errors ++; |
| } |
| |
| if (!ppd->lang_version || strcmp(ppd->lang_version, "English")) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Bad LanguageVersion %s - " |
| "must be English."), |
| 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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Default translation " |
| "string for option %s contains 8-bit " |
| "characters."), |
| 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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Default translation " |
| "string for option %s choice %s contains " |
| "8-bit characters."), |
| option->keyword, |
| option->choices[j].choice); |
| |
| errors ++; |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * Final pass/fail notification... |
| */ |
| |
| if (errors) |
| status = ERROR_CONFORMANCE; |
| else if (!verbose) |
| _cupsLangPuts(stdout, _(" PASS")); |
| |
| if (verbose >= 0) |
| { |
| check_basics(argv[i]); |
| |
| if (warn & WARN_DEFAULTS) |
| errors = check_defaults(ppd, errors, verbose, 1); |
| |
| if (warn & WARN_CONSTRAINTS) |
| errors = check_constraints(ppd, errors, verbose, 1); |
| |
| if ((warn & WARN_FILTERS) && !(ignore & WARN_FILTERS)) |
| errors = check_filters(ppd, root, errors, verbose, 1); |
| |
| if ((warn & WARN_PROFILES) && !(ignore & WARN_PROFILES)) |
| errors = check_profiles(ppd, root, errors, verbose, 1); |
| |
| if (warn & WARN_SIZES) |
| errors = check_sizes(ppd, errors, verbose, 1); |
| else |
| errors = check_sizes(ppd, errors, verbose, 2); |
| |
| if (warn & WARN_TRANSLATIONS) |
| errors = check_translations(ppd, errors, verbose, 1); |
| |
| if (warn & WARN_DUPLEX) |
| errors = check_duplex(ppd, errors, verbose, 1); |
| |
| /* |
| * Look for legacy duplex keywords... |
| */ |
| |
| if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL) |
| if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL) |
| option = ppdFindOption(ppd, "KD03Duplex"); |
| |
| if (option) |
| _cupsLangPrintf(stdout, |
| _(" WARN Duplex option keyword %s may not " |
| "work as expected and should be named Duplex.\n" |
| " REF: Page 122, section 5.17"), |
| option->keyword); |
| |
| /* |
| * 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."), |
| attr->name); |
| } |
| |
| if (ppdversion < 43) |
| { |
| _cupsLangPrintf(stdout, |
| _(" WARN Obsolete PPD version %.1f.\n" |
| " REF: Page 42, section 5.2."), |
| 0.1f * ppdversion); |
| } |
| |
| if (!ppd->lang_encoding && ppdversion < 41) |
| { |
| _cupsLangPuts(stdout, |
| _(" WARN LanguageEncoding required by PPD " |
| "4.3 spec.\n" |
| " REF: Pages 56-57, section 5.3.")); |
| } |
| |
| if (!ppd->manufacturer && ppdversion < 43) |
| { |
| _cupsLangPuts(stdout, |
| _(" WARN Manufacturer required by PPD " |
| "4.3 spec.\n" |
| " REF: Pages 58-59, section 5.3.")); |
| } |
| |
| /* |
| * Treat a PCFileName attribute longer than 12 characters as |
| * a warning and not a hard error... |
| */ |
| |
| if (!(ignore & WARN_FILENAME) && ppd->pcfilename) |
| { |
| if (strlen(ppd->pcfilename) > 12) |
| { |
| _cupsLangPuts(stdout, |
| _(" WARN PCFileName longer than 8.3 in " |
| "violation of PPD spec.\n" |
| " REF: Pages 61-62, section " |
| "5.3.")); |
| } |
| |
| if (!_cups_strcasecmp(ppd->pcfilename, "unused.ppd")) |
| _cupsLangPuts(stdout, |
| _(" WARN PCFileName should contain a " |
| "unique filename.\n" |
| " REF: Pages 61-62, section " |
| "5.3.")); |
| } |
| |
| if (!ppd->shortnickname && ppdversion < 43) |
| { |
| _cupsLangPuts(stdout, |
| _(" WARN ShortNickName required by PPD " |
| "4.3 spec.\n" |
| " REF: Pages 64-65, section 5.3.")); |
| } |
| |
| /* |
| * Check the Protocols line and flag PJL + BCP since TBCP is |
| * usually used with PJL... |
| */ |
| |
| if (ppd->protocols) |
| { |
| if (strstr(ppd->protocols, "PJL") && |
| strstr(ppd->protocols, "BCP") && |
| !strstr(ppd->protocols, "TBCP")) |
| { |
| _cupsLangPuts(stdout, |
| _(" WARN Protocols contains both PJL " |
| "and BCP; expected TBCP.\n" |
| " REF: Pages 78-79, section 5.7.")); |
| } |
| |
| if (strstr(ppd->protocols, "PJL") && |
| (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps)) |
| { |
| _cupsLangPuts(stdout, |
| _(" WARN Protocols contains PJL but JCL " |
| "attributes are not set.\n" |
| " REF: Pages 78-79, section 5.7.")); |
| } |
| } |
| |
| /* |
| * Check for options with a common prefix, e.g. Duplex and Duplexer, |
| * which are errors according to the spec but won't cause problems |
| * with CUPS specifically... |
| */ |
| |
| for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++) |
| for (k = 0, option = group->options; |
| k < group->num_options; |
| k ++, option ++) |
| { |
| len = strlen(option->keyword); |
| |
| for (m = 0, group2 = ppd->groups; |
| m < ppd->num_groups; |
| m ++, group2 ++) |
| for (n = 0, option2 = group2->options; |
| n < group2->num_options; |
| n ++, option2 ++) |
| if (option != option2 && |
| len < strlen(option2->keyword) && |
| !strncmp(option->keyword, option2->keyword, len)) |
| { |
| _cupsLangPrintf(stdout, |
| _(" WARN %s shares a common " |
| "prefix with %s\n" |
| " REF: Page 15, section " |
| "3.2."), |
| option->keyword, option2->keyword); |
| } |
| } |
| } |
| |
| if (verbose > 0) |
| { |
| if (errors) |
| _cupsLangPrintf(stdout, _(" %d ERRORS FOUND"), errors); |
| else |
| _cupsLangPuts(stdout, _(" NO ERRORS FOUND")); |
| } |
| |
| /* |
| * Then list the options, if "-v" was provided... |
| */ |
| |
| if (verbose > 1) |
| { |
| _cupsLangPrintf(stdout, |
| "\n" |
| " language_level = %d\n" |
| " color_device = %s\n" |
| " variable_sizes = %s\n" |
| " landscape = %d", |
| ppd->language_level, |
| ppd->color_device ? "TRUE" : "FALSE", |
| ppd->variable_sizes ? "TRUE" : "FALSE", |
| ppd->landscape); |
| |
| switch (ppd->colorspace) |
| { |
| case PPD_CS_CMYK : |
| _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK"); |
| break; |
| case PPD_CS_CMY : |
| _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY"); |
| break; |
| case PPD_CS_GRAY : |
| _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY"); |
| break; |
| case PPD_CS_RGB : |
| _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB"); |
| break; |
| default : |
| _cupsLangPuts(stdout, " colorspace = <unknown>"); |
| break; |
| } |
| |
| _cupsLangPrintf(stdout, " num_emulations = %d", |
| ppd->num_emulations); |
| for (j = 0; j < ppd->num_emulations; j ++) |
| _cupsLangPrintf(stdout, " emulations[%d] = %s", |
| j, ppd->emulations[j].name); |
| |
| _cupsLangPrintf(stdout, " lang_encoding = %s", |
| ppd->lang_encoding); |
| _cupsLangPrintf(stdout, " lang_version = %s", |
| ppd->lang_version); |
| _cupsLangPrintf(stdout, " modelname = %s", ppd->modelname); |
| _cupsLangPrintf(stdout, " ttrasterizer = %s", |
| ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer); |
| _cupsLangPrintf(stdout, " manufacturer = %s", |
| ppd->manufacturer); |
| _cupsLangPrintf(stdout, " product = %s", ppd->product); |
| _cupsLangPrintf(stdout, " nickname = %s", ppd->nickname); |
| _cupsLangPrintf(stdout, " shortnickname = %s", |
| ppd->shortnickname); |
| _cupsLangPrintf(stdout, " patches = %d bytes", |
| ppd->patches == NULL ? 0 : (int)strlen(ppd->patches)); |
| |
| _cupsLangPrintf(stdout, " num_groups = %d", ppd->num_groups); |
| for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++) |
| { |
| _cupsLangPrintf(stdout, " group[%d] = %s", |
| j, group->text); |
| |
| for (k = 0, option = group->options; k < group->num_options; k ++, option ++) |
| { |
| _cupsLangPrintf(stdout, |
| " options[%d] = %s (%s) %s %s %.0f " |
| "(%d choices)", |
| k, option->keyword, option->text, uis[option->ui], |
| sections[option->section], option->order, |
| option->num_choices); |
| |
| if (!strcmp(option->keyword, "PageSize") || |
| !strcmp(option->keyword, "PageRegion")) |
| { |
| for (m = option->num_choices, choice = option->choices; |
| m > 0; |
| m --, choice ++) |
| { |
| size = ppdPageSize(ppd, choice->choice); |
| |
| if (size == NULL) |
| _cupsLangPrintf(stdout, |
| " %s (%s) = ERROR%s", |
| choice->choice, choice->text, |
| !strcmp(option->defchoice, choice->choice) |
| ? " *" : ""); |
| else |
| _cupsLangPrintf(stdout, |
| " %s (%s) = %.2fx%.2fin " |
| "(%.1f,%.1f,%.1f,%.1f)%s", |
| choice->choice, choice->text, |
| size->width / 72.0, size->length / 72.0, |
| size->left / 72.0, size->bottom / 72.0, |
| size->right / 72.0, size->top / 72.0, |
| !strcmp(option->defchoice, choice->choice) |
| ? " *" : ""); |
| } |
| } |
| else |
| { |
| for (m = option->num_choices, choice = option->choices; |
| m > 0; |
| m --, choice ++) |
| { |
| _cupsLangPrintf(stdout, " %s (%s)%s", |
| choice->choice, choice->text, |
| !strcmp(option->defchoice, choice->choice) |
| ? " *" : ""); |
| } |
| } |
| } |
| } |
| |
| _cupsLangPrintf(stdout, " num_consts = %d", |
| ppd->num_consts); |
| for (j = 0; j < ppd->num_consts; j ++) |
| _cupsLangPrintf(stdout, |
| " consts[%d] = *%s %s *%s %s", |
| j, ppd->consts[j].option1, ppd->consts[j].choice1, |
| ppd->consts[j].option2, ppd->consts[j].choice2); |
| |
| _cupsLangPrintf(stdout, " num_profiles = %d", |
| ppd->num_profiles); |
| for (j = 0; j < ppd->num_profiles; j ++) |
| _cupsLangPrintf(stdout, |
| " profiles[%d] = %s/%s %.3f %.3f " |
| "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]", |
| j, ppd->profiles[j].resolution, |
| ppd->profiles[j].media_type, |
| ppd->profiles[j].gamma, ppd->profiles[j].density, |
| ppd->profiles[j].matrix[0][0], |
| ppd->profiles[j].matrix[0][1], |
| ppd->profiles[j].matrix[0][2], |
| ppd->profiles[j].matrix[1][0], |
| ppd->profiles[j].matrix[1][1], |
| ppd->profiles[j].matrix[1][2], |
| ppd->profiles[j].matrix[2][0], |
| ppd->profiles[j].matrix[2][1], |
| ppd->profiles[j].matrix[2][2]); |
| |
| _cupsLangPrintf(stdout, " num_fonts = %d", ppd->num_fonts); |
| for (j = 0; j < ppd->num_fonts; j ++) |
| _cupsLangPrintf(stdout, " fonts[%d] = %s", |
| j, ppd->fonts[j]); |
| |
| _cupsLangPrintf(stdout, " num_attrs = %d", ppd->num_attrs); |
| for (j = 0; j < ppd->num_attrs; j ++) |
| _cupsLangPrintf(stdout, |
| " attrs[%d] = %s %s%s%s: \"%s\"", j, |
| ppd->attrs[j]->name, ppd->attrs[j]->spec, |
| ppd->attrs[j]->text[0] ? "/" : "", |
| ppd->attrs[j]->text, |
| ppd->attrs[j]->value ? |
| ppd->attrs[j]->value : "(null)"); |
| } |
| |
| ppdClose(ppd); |
| } |
| |
| if (!files) |
| usage(); |
| |
| return (status); |
| } |
| |
| |
| /* |
| * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines. |
| */ |
| |
| static void |
| check_basics(const char *filename) /* I - PPD file to check */ |
| { |
| cups_file_t *fp; /* File pointer */ |
| int ch; /* Current character */ |
| int col, /* Current column */ |
| whitespace; /* Only seen whitespace? */ |
| int eol; /* Line endings */ |
| int linenum; /* Line number */ |
| int mixed; /* Mixed line endings? */ |
| |
| |
| if ((fp = cupsFileOpen(filename, "r")) == NULL) |
| return; |
| |
| linenum = 1; |
| col = 0; |
| eol = EOL_NONE; |
| mixed = 0; |
| whitespace = 1; |
| |
| while ((ch = cupsFileGetChar(fp)) != EOF) |
| { |
| if (ch == '\r' || ch == '\n') |
| { |
| if (ch == '\n') |
| { |
| if (eol == EOL_NONE) |
| eol = EOL_LF; |
| else if (eol != EOL_LF) |
| mixed = 1; |
| } |
| else if (ch == '\r') |
| { |
| if (cupsFilePeekChar(fp) == '\n') |
| { |
| cupsFileGetChar(fp); |
| |
| if (eol == EOL_NONE) |
| eol = EOL_CRLF; |
| else if (eol != EOL_CRLF) |
| mixed = 1; |
| } |
| else if (eol == EOL_NONE) |
| eol = EOL_CR; |
| else if (eol != EOL_CR) |
| mixed = 1; |
| } |
| |
| if (col > 0 && whitespace) |
| _cupsLangPrintf(stdout, |
| _(" WARN Line %d only contains whitespace."), |
| linenum); |
| |
| linenum ++; |
| col = 0; |
| whitespace = 1; |
| } |
| else |
| { |
| if (ch != ' ' && ch != '\t') |
| whitespace = 0; |
| |
| col ++; |
| } |
| } |
| |
| if (mixed) |
| _cupsLangPuts(stdout, |
| _(" WARN File contains a mix of CR, LF, and " |
| "CR LF line endings.")); |
| |
| if (eol == EOL_CRLF) |
| _cupsLangPuts(stdout, |
| _(" WARN Non-Windows PPD files should use lines " |
| "ending with only LF, not CR LF.")); |
| |
| cupsFileClose(fp); |
| } |
| |
| |
| /* |
| * '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 i; /* Looping var */ |
| const char *prefix; /* WARN/FAIL prefix */ |
| ppd_const_t *c; /* Current UIConstraints data */ |
| ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */ |
| const char *vptr; /* Pointer into constraint value */ |
| char option[PPD_MAX_NAME], |
| /* Option name/MainKeyword */ |
| choice[PPD_MAX_NAME], |
| /* Choice/OptionKeyword */ |
| *ptr; /* Pointer into option or choice */ |
| int num_options; /* Number of options */ |
| cups_option_t *options; /* Options */ |
| ppd_option_t *o; /* PPD option */ |
| |
| |
| prefix = warn ? " WARN " : "**FAIL**"; |
| |
| |
| /* |
| * See what kind of constraint data we have in the PPD... |
| */ |
| |
| if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL) |
| { |
| /* |
| * Check new-style cupsUIConstraints data... |
| */ |
| |
| for (; constattr; |
| constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL)) |
| { |
| if (!constattr->value) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Empty cupsUIConstraints %s"), |
| prefix, constattr->spec); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| for (i = 0, vptr = strchr(constattr->value, '*'); |
| vptr; |
| i ++, vptr = strchr(vptr + 1, '*')); |
| |
| if (i == 0) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Bad cupsUIConstraints %s: \"%s\""), |
| prefix, constattr->spec, constattr->value); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| cupsArraySave(ppd->sorted_attrs); |
| |
| if (constattr->spec[0] && |
| !ppdFindAttr(ppd, "cupsUIResolver", constattr->spec)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Missing cupsUIResolver %s"), |
| prefix, constattr->spec); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| cupsArrayRestore(ppd->sorted_attrs); |
| |
| num_options = 0; |
| options = NULL; |
| |
| for (vptr = strchr(constattr->value, '*'); |
| vptr; |
| vptr = strchr(vptr, '*')) |
| { |
| /* |
| * Extract "*Option Choice" or just "*Option"... |
| */ |
| |
| for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++) |
| if (ptr < (option + sizeof(option) - 1)) |
| *ptr++ = *vptr; |
| |
| *ptr = '\0'; |
| |
| while (isspace(*vptr & 255)) |
| vptr ++; |
| |
| if (*vptr == '*') |
| choice[0] = '\0'; |
| else |
| { |
| for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++) |
| if (ptr < (choice + sizeof(choice) - 1)) |
| *ptr++ = *vptr; |
| |
| *ptr = '\0'; |
| } |
| |
| if (!_cups_strncasecmp(option, "Custom", 6) && !_cups_strcasecmp(choice, "True")) |
| { |
| _cups_strcpy(option, option + 6); |
| strlcpy(choice, "Custom", sizeof(choice)); |
| } |
| |
| if ((o = ppdFindOption(ppd, option)) == NULL) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Missing option %s in " |
| "cupsUIConstraints %s: \"%s\""), |
| prefix, option, constattr->spec, constattr->value); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| if (choice[0] && !ppdFindChoice(o, choice)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Missing choice *%s %s in " |
| "cupsUIConstraints %s: \"%s\""), |
| prefix, option, choice, constattr->spec, |
| constattr->value); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| if (choice[0]) |
| num_options = cupsAddOption(option, choice, num_options, &options); |
| else |
| { |
| for (i = 0; i < o->num_choices; i ++) |
| if (_cups_strcasecmp(o->choices[i].choice, "None") && |
| _cups_strcasecmp(o->choices[i].choice, "Off") && |
| _cups_strcasecmp(o->choices[i].choice, "False")) |
| { |
| num_options = cupsAddOption(option, o->choices[i].choice, |
| num_options, &options); |
| break; |
| } |
| } |
| } |
| |
| /* |
| * Resolvers must list at least two options... |
| */ |
| |
| if (num_options < 2) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s cupsUIResolver %s does not list at least " |
| "two different options."), |
| prefix, constattr->spec); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| /* |
| * Test the resolver... |
| */ |
| |
| if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s cupsUIResolver %s causes a loop."), |
| prefix, constattr->spec); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| cupsFreeOptions(num_options, options); |
| } |
| } |
| else |
| { |
| /* |
| * Check old-style [Non]UIConstraints data... |
| */ |
| |
| for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++) |
| { |
| if (!_cups_strncasecmp(c->option1, "Custom", 6) && |
| !_cups_strcasecmp(c->choice1, "True")) |
| { |
| strlcpy(option, c->option1 + 6, sizeof(option)); |
| strlcpy(choice, "Custom", sizeof(choice)); |
| } |
| else |
| { |
| strlcpy(option, c->option1, sizeof(option)); |
| strlcpy(choice, c->choice1, sizeof(choice)); |
| } |
| |
| if ((o = ppdFindOption(ppd, option)) == NULL) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Missing option %s in " |
| "UIConstraints \"*%s %s *%s %s\"."), |
| prefix, c->option1, |
| c->option1, c->choice1, c->option2, c->choice2); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (choice[0] && !ppdFindChoice(o, choice)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Missing choice *%s %s in " |
| "UIConstraints \"*%s %s *%s %s\"."), |
| prefix, c->option1, c->choice1, |
| c->option1, c->choice1, c->option2, c->choice2); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| if (!_cups_strncasecmp(c->option2, "Custom", 6) && |
| !_cups_strcasecmp(c->choice2, "True")) |
| { |
| strlcpy(option, c->option2 + 6, sizeof(option)); |
| strlcpy(choice, "Custom", sizeof(choice)); |
| } |
| else |
| { |
| strlcpy(option, c->option2, sizeof(option)); |
| strlcpy(choice, c->choice2, sizeof(choice)); |
| } |
| |
| if ((o = ppdFindOption(ppd, option)) == NULL) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Missing option %s in " |
| "UIConstraints \"*%s %s *%s %s\"."), |
| prefix, c->option2, |
| c->option1, c->choice1, c->option2, c->choice2); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (choice[0] && !ppdFindChoice(o, choice)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Missing choice *%s %s in " |
| "UIConstraints \"*%s %s *%s %s\"."), |
| prefix, c->option2, c->choice2, |
| c->option1, c->choice1, c->option2, c->choice2); |
| |
| if (!warn) |
| errors ++; |
| } |
| } |
| } |
| |
| return (errors); |
| } |
| |
| |
| /* |
| * 'check_case()' - Check that there are no duplicate groups, options, |
| * or choices that differ only by case. |
| */ |
| |
| static int /* O - Errors found */ |
| check_case(ppd_file_t *ppd, /* I - PPD file */ |
| int errors, /* I - Errors found */ |
| int verbose) /* I - Verbosity level */ |
| { |
| int i, j; /* Looping vars */ |
| ppd_group_t *groupa, /* First group */ |
| *groupb; /* Second group */ |
| ppd_option_t *optiona, /* First option */ |
| *optionb; /* Second option */ |
| ppd_choice_t *choicea, /* First choice */ |
| *choiceb; /* Second choice */ |
| |
| |
| /* |
| * Check that the groups do not have any duplicate names... |
| */ |
| |
| for (i = ppd->num_groups, groupa = ppd->groups; i > 1; i --, groupa ++) |
| for (j = i - 1, groupb = groupa + 1; j > 0; j --, groupb ++) |
| if (!_cups_strcasecmp(groupa->name, groupb->name)) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Group names %s and %s differ only " |
| "by case."), |
| groupa->name, groupb->name); |
| |
| errors ++; |
| } |
| |
| /* |
| * Check that the options do not have any duplicate names... |
| */ |
| |
| for (optiona = ppdFirstOption(ppd); optiona; optiona = ppdNextOption(ppd)) |
| { |
| cupsArraySave(ppd->options); |
| for (optionb = ppdNextOption(ppd); optionb; optionb = ppdNextOption(ppd)) |
| if (!_cups_strcasecmp(optiona->keyword, optionb->keyword)) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Option names %s and %s differ only " |
| "by case."), |
| optiona->keyword, optionb->keyword); |
| |
| errors ++; |
| } |
| cupsArrayRestore(ppd->options); |
| |
| /* |
| * Then the choices... |
| */ |
| |
| for (i = optiona->num_choices, choicea = optiona->choices; |
| i > 1; |
| i --, choicea ++) |
| for (j = i - 1, choiceb = choicea + 1; j > 0; j --, choiceb ++) |
| if (!strcmp(choicea->choice, choiceb->choice)) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Multiple occurrences of " |
| "option %s choice name %s."), |
| optiona->keyword, choicea->choice); |
| |
| errors ++; |
| |
| choicea ++; |
| i --; |
| break; |
| } |
| else if (!_cups_strcasecmp(choicea->choice, choiceb->choice)) |
| { |
| if (!errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Option %s choice names %s and " |
| "%s differ only by case."), |
| optiona->keyword, choicea->choice, choiceb->choice); |
| |
| errors ++; |
| } |
| } |
| |
| /* |
| * Return the number of errors found... |
| */ |
| |
| 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**"; |
| |
| ppdMarkDefaults(ppd); |
| if (ppdConflicts(ppd)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Default choices conflicting."), prefix); |
| |
| show_conflicts(ppd, prefix); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| 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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s %s %s does not exist."), |
| prefix, attr->name, attr->value); |
| |
| if (!warn) |
| errors ++; |
| } |
| } |
| } |
| } |
| |
| return (errors); |
| } |
| |
| |
| /* |
| * 'check_duplex()' - Check duplex keywords in the PPD file. |
| */ |
| |
| static int /* O - Errors found */ |
| check_duplex(ppd_file_t *ppd, /* I - PPD file */ |
| int errors, /* I - Error found */ |
| int verbose, /* I - Verbosity level */ |
| int warn) /* I - Warnings only? */ |
| { |
| int i; /* Looping var */ |
| ppd_option_t *option; /* PPD option */ |
| ppd_choice_t *choice; /* Current choice */ |
| const char *prefix; /* Message prefix */ |
| |
| |
| prefix = warn ? " WARN " : "**FAIL**"; |
| |
| /* |
| * Check for a duplex option, and for standard values... |
| */ |
| |
| if ((option = ppdFindOption(ppd, "Duplex")) != NULL) |
| { |
| if (!ppdFindChoice(option, "None")) |
| { |
| if (verbose >= 0) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s REQUIRED %s does not define " |
| "choice None.\n" |
| " REF: Page 122, section 5.17"), |
| prefix, option->keyword); |
| } |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| for (i = option->num_choices, choice = option->choices; |
| i > 0; |
| i --, choice ++) |
| if (strcmp(choice->choice, "None") && |
| strcmp(choice->choice, "DuplexNoTumble") && |
| strcmp(choice->choice, "DuplexTumble") && |
| strcmp(choice->choice, "SimplexTumble")) |
| { |
| if (verbose >= 0) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| _cupsLangPrintf(stdout, |
| _(" %s Bad %s choice %s.\n" |
| " REF: Page 122, section 5.17"), |
| prefix, option->keyword, choice->choice); |
| } |
| |
| 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 */ |
| char super[16], /* Super-type for filter */ |
| type[256], /* Type for filter */ |
| dstsuper[16], /* Destination super-type for filter */ |
| dsttype[256], /* Destination type for filter */ |
| program[1024], /* Program/filter name */ |
| pathprog[1024]; /* Complete path to program/filter */ |
| int cost; /* Cost of filter */ |
| const char *prefix; /* WARN/FAIL prefix */ |
| struct stat fileinfo; /* File information */ |
| |
| |
| prefix = warn ? " WARN " : "**FAIL**"; |
| |
| /* |
| * cupsFilter |
| */ |
| |
| for (attr = ppdFindAttr(ppd, "cupsFilter", NULL); |
| attr; |
| attr = ppdFindNextAttr(ppd, "cupsFilter", NULL)) |
| { |
| if (strcmp(attr->name, "cupsFilter")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "cupsFilter"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| if (!attr->value || |
| sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, |
| &cost, program) != 4) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad cupsFilter value \"%s\"."), |
| prefix, attr->value); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| if (!strncmp(program, "maxsize(", 8)) |
| { |
| char *mptr; /* Pointer into maxsize(nnnn) program */ |
| |
| strtoll(program + 8, &mptr, 10); |
| |
| if (*mptr != ')') |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad cupsFilter value \"%s\"."), |
| prefix, attr->value); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| mptr ++; |
| while (_cups_isspace(*mptr)) |
| mptr ++; |
| |
| _cups_strcpy(program, mptr); |
| } |
| |
| if (strcmp(program, "-")) |
| { |
| 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, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "cupsFilter", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "cupsFilter", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("cupsFilter", pathprog, errors, verbose, warn); |
| } |
| } |
| |
| /* |
| * cupsFilter2 |
| */ |
| |
| for (attr = ppdFindAttr(ppd, "cupsFilter2", NULL); |
| attr; |
| attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) |
| { |
| if (strcmp(attr->name, "cupsFilter2")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "cupsFilter2"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| if (!attr->value || |
| sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]", |
| super, type, dstsuper, dsttype, &cost, program) != 6) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad cupsFilter2 value \"%s\"."), |
| prefix, attr->value); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| if (!strncmp(program, "maxsize(", 8)) |
| { |
| char *mptr; /* Pointer into maxsize(nnnn) program */ |
| |
| strtoll(program + 8, &mptr, 10); |
| |
| if (*mptr != ')') |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad cupsFilter2 value \"%s\"."), |
| prefix, attr->value); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| mptr ++; |
| while (_cups_isspace(*mptr)) |
| mptr ++; |
| |
| _cups_strcpy(program, mptr); |
| } |
| |
| if (strcmp(program, "-")) |
| { |
| if (strncmp(program, "maxsize(", 8) && |
| (ptr = strchr(program + 8, ')')) != NULL) |
| { |
| ptr ++; |
| while (_cups_isspace(*ptr)) |
| ptr ++; |
| |
| _cups_strcpy(program, ptr); |
| } |
| |
| 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, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "cupsFilter2", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "cupsFilter2", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("cupsFilter2", pathprog, errors, verbose, warn); |
| } |
| } |
| |
| /* |
| * cupsPreFilter |
| */ |
| |
| for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL); |
| attr; |
| attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) |
| { |
| if (strcmp(attr->name, "cupsPreFilter")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "cupsPreFilter"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| if (!attr->value || |
| sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, |
| &cost, program) != 4) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad cupsPreFilter value \"%s\"."), |
| prefix, attr->value ? attr->value : ""); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (strcmp(program, "-")) |
| { |
| 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, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "cupsPreFilter", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "cupsPreFilter", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("cupsPreFilter", pathprog, errors, verbose, warn); |
| } |
| } |
| |
| #ifdef __APPLE__ |
| /* |
| * APDialogExtension |
| */ |
| |
| for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL); |
| attr != NULL; |
| attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL)) |
| { |
| if (strcmp(attr->name, "APDialogExtension")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "APDialogExtension"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| snprintf(pathprog, sizeof(pathprog), "%s%s", root, |
| attr->value ? attr->value : "(null)"); |
| |
| if (!attr->value || stat(pathprog, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "APDialogExtension", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "APDialogExtension", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("APDialogExtension", pathprog, errors, verbose, |
| warn); |
| } |
| |
| /* |
| * APPrinterIconPath |
| */ |
| |
| if ((attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL) |
| { |
| if (strcmp(attr->name, "APPrinterIconPath")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "APPrinterIconPath"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| snprintf(pathprog, sizeof(pathprog), "%s%s", root, |
| attr->value ? attr->value : "(null)"); |
| |
| if (!attr->value || stat(pathprog, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "APPrinterIconPath", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_DATAFILE) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "APPrinterIconPath", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("APPrinterIconPath", pathprog, errors, verbose, |
| warn); |
| } |
| |
| /* |
| * APPrinterLowInkTool |
| */ |
| |
| if ((attr = ppdFindAttr(ppd, "APPrinterLowInkTool", NULL)) != NULL) |
| { |
| if (strcmp(attr->name, "APPrinterLowInkTool")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "APPrinterLowInkTool"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| snprintf(pathprog, sizeof(pathprog), "%s%s", root, |
| attr->value ? attr->value : "(null)"); |
| |
| if (!attr->value || stat(pathprog, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "APPrinterLowInkTool", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "APPrinterLowInkTool", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("APPrinterLowInkTool", pathprog, errors, verbose, |
| warn); |
| } |
| |
| /* |
| * APPrinterUtilityPath |
| */ |
| |
| if ((attr = ppdFindAttr(ppd, "APPrinterUtilityPath", NULL)) != NULL) |
| { |
| if (strcmp(attr->name, "APPrinterUtilityPath")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "APPrinterUtilityPath"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| snprintf(pathprog, sizeof(pathprog), "%s%s", root, |
| attr->value ? attr->value : "(null)"); |
| |
| if (!attr->value || stat(pathprog, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "APPrinterUtilityPath", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "APPrinterUtilityPath", pathprog); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("APPrinterUtilityPath", pathprog, errors, verbose, |
| warn); |
| } |
| |
| /* |
| * APScanAppBundleID and APScanAppPath |
| */ |
| |
| if ((attr = ppdFindAttr(ppd, "APScanAppPath", NULL)) != NULL) |
| { |
| if (strcmp(attr->name, "APScanAppPath")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad spelling of %s - should be %s."), |
| prefix, attr->name, "APScanAppPath"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| if (!attr->value || stat(attr->value, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "APScanAppPath", |
| attr->value ? attr->value : "<NULL>"); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "APScanAppPath", attr->value); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("APScanAppPath", attr->value, errors, verbose, |
| warn); |
| |
| if (ppdFindAttr(ppd, "APScanAppBundleID", NULL)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Cannot provide both " |
| "APScanAppPath and APScanAppBundleID."), |
| prefix); |
| |
| if (!warn) |
| errors ++; |
| } |
| } |
| #endif /* __APPLE__ */ |
| |
| return (errors); |
| } |
| |
| |
| /* |
| * 'check_profiles()' - Check ICC color profiles in the PPD file. |
| */ |
| |
| static int /* O - Errors found */ |
| check_profiles(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? */ |
| { |
| int i; /* Looping var */ |
| ppd_attr_t *attr; /* PPD attribute */ |
| const char *ptr; /* Pointer into string */ |
| const char *prefix; /* WARN/FAIL prefix */ |
| char filename[1024]; /* Profile filename */ |
| struct stat fileinfo; /* File information */ |
| int num_profiles = 0; /* Number of profiles */ |
| unsigned hash, /* Current hash value */ |
| hashes[1000]; /* Hash values of profile names */ |
| const char *specs[1000]; /* Specifiers for profiles */ |
| |
| |
| prefix = warn ? " WARN " : "**FAIL**"; |
| |
| for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); |
| attr; |
| attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) |
| { |
| /* |
| * Check for valid selector... |
| */ |
| |
| for (i = 0, ptr = strchr(attr->spec, '.'); ptr; ptr = strchr(ptr + 1, '.')) |
| i ++; |
| |
| if (!attr->value || i < 2) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad cupsICCProfile %s."), |
| prefix, attr->spec); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| /* |
| * Check for valid profile filename... |
| */ |
| |
| if (attr->value[0] == '/') |
| snprintf(filename, sizeof(filename), "%s%s", root, attr->value); |
| else |
| { |
| if ((ptr = getenv("CUPS_DATADIR")) == NULL) |
| ptr = CUPS_DATADIR; |
| |
| if (*ptr == '/' || !*root) |
| snprintf(filename, sizeof(filename), "%s%s/profiles/%s", root, ptr, |
| attr->value); |
| else |
| snprintf(filename, sizeof(filename), "%s/%s/profiles/%s", root, ptr, |
| attr->value); |
| } |
| |
| if (stat(filename, &fileinfo)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."), |
| prefix, "cupsICCProfile", filename); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (fileinfo.st_uid != 0 || |
| (fileinfo.st_mode & MODE_WRITE) || |
| (fileinfo.st_mode & MODE_MASK) != MODE_DATAFILE) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad permissions on %s file \"%s\"."), |
| prefix, "cupsICCProfile", filename); |
| |
| if (!warn) |
| errors ++; |
| } |
| else |
| errors = valid_path("cupsICCProfile", filename, errors, verbose, warn); |
| |
| /* |
| * Check for hash collisions... |
| */ |
| |
| hash = _ppdHashName(attr->spec); |
| |
| if (num_profiles > 0) |
| { |
| for (i = 0; i < num_profiles; i ++) |
| if (hashes[i] == hash) |
| break; |
| |
| if (i < num_profiles) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s cupsICCProfile %s hash value " |
| "collides with %s."), prefix, attr->spec, |
| specs[i]); |
| |
| if (!warn) |
| errors ++; |
| } |
| } |
| |
| /* |
| * Remember up to 1000 profiles... |
| */ |
| |
| if (num_profiles < 1000) |
| { |
| hashes[num_profiles] = hash; |
| specs[num_profiles] = attr->spec; |
| num_profiles ++; |
| } |
| } |
| |
| return (errors); |
| } |
| |
| |
| /* |
| * 'check_sizes()' - Check media sizes in the PPD file. |
| */ |
| |
| static int /* O - Errors found */ |
| check_sizes(ppd_file_t *ppd, /* I - PPD file */ |
| int errors, /* I - Errors found */ |
| int verbose, /* I - Verbosity level */ |
| int warn) /* I - Warnings only? */ |
| { |
| int i; /* Looping var */ |
| ppd_size_t *size; /* Current size */ |
| int width, /* Custom width */ |
| length; /* Custom length */ |
| const char *prefix; /* WARN/FAIL prefix */ |
| ppd_option_t *page_size, /* PageSize option */ |
| *page_region; /* PageRegion option */ |
| pwg_media_t *pwg_media; /* PWG media */ |
| char buf[PPD_MAX_NAME]; /* PapeSize name that is supposed to be */ |
| const char *ptr; /* Pointer into string */ |
| int width_2540ths, /* PageSize width in 2540ths */ |
| length_2540ths; /* PageSize length in 2540ths */ |
| int is_ok; /* Flag for PageSize name verification */ |
| double width_tmp, /* Width after rounded up */ |
| length_tmp, /* Length after rounded up */ |
| width_inch, /* Width in inches */ |
| length_inch, /* Length in inches */ |
| width_mm, /* Width in millimeters */ |
| length_mm; /* Length in millimeters */ |
| |
| |
| prefix = warn ? " WARN " : "**FAIL**"; |
| |
| if ((page_size = ppdFindOption(ppd, "PageSize")) == NULL && warn != 2) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Missing REQUIRED PageSize option.\n" |
| " REF: Page 99, section 5.14."), |
| prefix); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| if ((page_region = ppdFindOption(ppd, "PageRegion")) == NULL && warn != 2) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Missing REQUIRED PageRegion option.\n" |
| " REF: Page 100, section 5.14."), |
| prefix); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) |
| { |
| /* |
| * Check that the size name is standard... |
| */ |
| |
| if (!strcmp(size->name, "Custom")) |
| { |
| /* |
| * Skip custom page size... |
| */ |
| |
| continue; |
| } |
| |
| if (warn != 2 && size->name[0] == 'w' && |
| sscanf(size->name, "w%dh%d", &width, &length) == 2) |
| { |
| /* |
| * Validate device-specific size wNNNhNNN should have proper width and |
| * length... |
| */ |
| |
| if (fabs(width - size->width) >= 1.0 || |
| fabs(length - size->length) >= 1.0) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Size \"%s\" has unexpected dimensions " |
| "(%gx%g)."), |
| prefix, size->name, size->width, size->length); |
| |
| if (!warn) |
| errors ++; |
| } |
| } |
| |
| /* |
| * Verify that the size is defined for both PageSize and PageRegion... |
| */ |
| |
| if (warn != 2 && !ppdFindChoice(page_size, size->name)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Size \"%s\" defined for %s but not for " |
| "%s."), |
| prefix, size->name, "PageRegion", "PageSize"); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (warn != 2 && !ppdFindChoice(page_region, size->name)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Size \"%s\" defined for %s but not for " |
| "%s."), |
| prefix, size->name, "PageSize", "PageRegion"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| /* |
| * Verify that the size name is Adobe standard name if it's a standard size |
| * and the dimentional name if it's not a standard size. Suffix should be |
| * .Fullbleed, etc., or numeric, e.g., Letter, Letter.Fullbleed, |
| * Letter.Transverse, Letter1, Letter2, 4x8, 55x91mm, 55x91mm.Fullbleed, etc. |
| */ |
| |
| if (warn != 0) |
| { |
| is_ok = 1; |
| width_2540ths = (size->length > size->width) ? |
| PWG_FROM_POINTS(size->width) : |
| PWG_FROM_POINTS(size->length); |
| length_2540ths = (size->length > size->width) ? |
| PWG_FROM_POINTS(size->length) : |
| PWG_FROM_POINTS(size->width); |
| pwg_media = pwgMediaForSize(width_2540ths, length_2540ths); |
| |
| if (pwg_media && |
| (abs(pwg_media->width - width_2540ths) > 34 || |
| abs(pwg_media->length - length_2540ths) > 34)) |
| pwg_media = NULL; /* Only flag matches within a point */ |
| |
| if (pwg_media && pwg_media->ppd && |
| (pwg_media->ppd[0] < 'a' || pwg_media->ppd[0] > 'z')) |
| { |
| size_t ppdlen = strlen(pwg_media->ppd); |
| /* Length of standard PPD name */ |
| |
| strlcpy(buf, pwg_media->ppd, sizeof(buf)); |
| |
| if (strcmp(size->name, buf) && size->width > size->length) |
| { |
| if (!strcmp(pwg_media->ppd, "DoublePostcardRotated")) |
| strlcpy(buf, "DoublePostcard", sizeof(buf)); |
| else if (strstr(size->name, ".Transverse")) |
| snprintf(buf, sizeof(buf), "%s.Transverse", pwg_media->ppd); |
| else |
| snprintf(buf, sizeof(buf), "%sRotated", pwg_media->ppd); |
| |
| ppdlen = strlen(buf); |
| } |
| |
| if (size->left == 0 && size->bottom == 0 && |
| size->right == size->width && size->top == size->length) |
| { |
| strlcat(buf, ".Fullbleed", sizeof(buf) - strlen(buf)); |
| if (_cups_strcasecmp(size->name, buf)) |
| { |
| /* |
| * Allow an additional qualifier such as ".WithTab"... |
| */ |
| |
| size_t buflen = strlen(buf);/* Length of full bleed name */ |
| |
| if (_cups_strncasecmp(size->name, buf, buflen) || |
| size->name[buflen] != '.') |
| is_ok = 0; |
| } |
| } |
| else if (!strncmp(size->name, pwg_media->ppd, ppdlen)) |
| { |
| /* |
| * Check for a proper qualifier (number, "Small", or .something)... |
| */ |
| |
| ptr = size->name + ppdlen; |
| |
| if (isdigit(*ptr & 255)) |
| { |
| for (ptr ++; *ptr; ptr ++) |
| { |
| if (!isdigit(*ptr & 255)) |
| { |
| is_ok = 0; |
| break; |
| } |
| } |
| } |
| else if (*ptr != '.' && *ptr && strcmp(ptr, "Small")) |
| is_ok = 0; |
| } |
| else |
| { |
| /* |
| * Check for EnvSizeName as well... |
| */ |
| |
| if (strncmp(pwg_media->ppd, "Env", 3) && |
| !strncmp(size->name, "Env", 3)) |
| snprintf(buf, sizeof(buf), "Env%s", pwg_media->ppd); |
| |
| if (strcmp(size->name, buf)) |
| is_ok = 0; |
| } |
| |
| if (!is_ok) |
| _cupsLangPrintf(stdout, |
| _(" %s Size \"%s\" should be the Adobe " |
| "standard name \"%s\"."), |
| prefix, size->name, buf); |
| } |
| else |
| { |
| width_tmp = (fabs(size->width - ceil(size->width)) < 0.1) ? |
| ceil(size->width) : size->width; |
| length_tmp = (fabs(size->length - ceil(size->length)) < 0.1) ? |
| ceil(size->length) : size->length; |
| |
| if (fmod(width_tmp, 9.0) == 0.0 && fmod(length_tmp, 9.0) == 0.0) |
| { |
| width_inch = width_tmp / 72.0; |
| length_inch = length_tmp / 72.0; |
| |
| snprintf(buf, sizeof(buf), "%gx%g", width_inch, length_inch); |
| } |
| else |
| { |
| width_mm = size->width / 72.0 * 25.4; |
| length_mm = size->length / 72.0 * 25.4; |
| |
| snprintf(buf, sizeof(buf), "%.0fx%.0fmm", width_mm, length_mm); |
| } |
| |
| if (size->left == 0 && size->bottom == 0 && |
| size->right == size->width && size->top == size->length) |
| strlcat(buf, ".Fullbleed", sizeof(buf)); |
| else if (size->width > size->length) |
| strlcat(buf, ".Transverse", sizeof(buf)); |
| |
| if (_cups_strcasecmp(size->name, buf)) |
| { |
| size_t buflen = strlen(buf); |
| /* Length of proposed name */ |
| |
| if (_cups_strncasecmp(size->name, buf, buflen) || |
| (strcmp(size->name + buflen, "in") && |
| size->name[buflen] != '.')) |
| { |
| char altbuf[PPD_MAX_NAME]; |
| /* Alternate "wNNNhNNN" name */ |
| size_t altlen; /* Length of alternate name */ |
| |
| snprintf(altbuf, sizeof(altbuf), "w%.0fh%.0f", size->width, |
| size->length); |
| altlen = strlen(altbuf); |
| if (_cups_strncasecmp(size->name, altbuf, altlen) || |
| (size->name[altlen] && size->name[altlen] != '.')) |
| _cupsLangPrintf(stdout, |
| _(" %s Size \"%s\" should be \"%s\"."), |
| prefix, size->name, buf); |
| } |
| } |
| } |
| } |
| } |
| |
| 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 */ |
| cups_array_t *languages; /* Array of languages */ |
| int langlen; /* Length of language */ |
| char *language, /* Current language */ |
| 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 */ |
| char ll[3]; /* Base language */ |
| const char *prefix; /* WARN/FAIL prefix */ |
| const char *text; /* Pointer into UI text */ |
| |
| |
| prefix = warn ? " WARN " : "**FAIL**"; |
| |
| if ((languages = _ppdGetLanguages(ppd)) != NULL) |
| { |
| /* |
| * This file contains localizations, check them... |
| */ |
| |
| for (language = (char *)cupsArrayFirst(languages); |
| language; |
| language = (char *)cupsArrayNext(languages)) |
| { |
| langlen = (int)strlen(language); |
| if (langlen != 2 && langlen != 5) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad language \"%s\"."), |
| prefix, language); |
| |
| if (!warn) |
| errors ++; |
| |
| continue; |
| } |
| |
| if (!strcmp(language, "en")) |
| continue; |
| |
| strlcpy(ll, language, 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", language); |
| 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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Missing \"%s\" translation " |
| "string for option %s."), |
| prefix, language, option->keyword); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (!valid_utf8(attr->text)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad UTF-8 \"%s\" translation " |
| "string for option %s."), |
| prefix, language, option->keyword); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| snprintf(keyword, sizeof(keyword), "%s.%s", language, |
| option->keyword); |
| snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll, |
| option->keyword); |
| |
| for (j = 0; j < option->num_choices; j ++) |
| { |
| /* |
| * First see if this choice is a number; if so, don't require |
| * translation... |
| */ |
| |
| for (text = option->choices[j].text; *text; text ++) |
| if (!strchr("0123456789-+.", *text)) |
| break; |
| |
| if (!*text) |
| continue; |
| |
| /* |
| * Check custom choices differently... |
| */ |
| |
| if (!_cups_strcasecmp(option->choices[j].choice, "Custom") && |
| (coption = ppdFindCustomOption(ppd, |
| option->keyword)) != NULL) |
| { |
| snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s", |
| language, option->keyword); |
| |
| if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL && |
| !valid_utf8(attr->text)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad UTF-8 \"%s\" " |
| "translation string for option %s, " |
| "choice %s."), |
| prefix, language, |
| ckeyword + 1 + strlen(language), |
| "True"); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| if (_cups_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", |
| language, 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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Missing \"%s\" " |
| "translation string for option %s, " |
| "choice %s."), |
| prefix, language, |
| ckeyword + 1 + strlen(language), |
| cparam->name); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (!valid_utf8(attr->text)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad UTF-8 \"%s\" " |
| "translation string for option %s, " |
| "choice %s."), |
| prefix, language, |
| ckeyword + 1 + strlen(language), |
| 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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Missing \"%s\" " |
| "translation string for option %s, " |
| "choice %s."), |
| prefix, language, option->keyword, |
| option->choices[j].choice); |
| |
| if (!warn) |
| errors ++; |
| } |
| else if (!valid_utf8(attr->text)) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s Bad UTF-8 \"%s\" " |
| "translation string for option %s, " |
| "choice %s."), |
| prefix, language, option->keyword, |
| option->choices[j].choice); |
| |
| if (!warn) |
| errors ++; |
| } |
| } |
| } |
| } |
| |
| /* |
| * Verify that we have the base language for each localized one... |
| */ |
| |
| for (language = (char *)cupsArrayFirst(languages); |
| language; |
| language = (char *)cupsArrayNext(languages)) |
| if (language[2]) |
| { |
| /* |
| * Lookup the base language... |
| */ |
| |
| cupsArraySave(languages); |
| |
| strlcpy(ll, language, sizeof(ll)); |
| |
| if (!cupsArrayFind(languages, ll) && |
| strcmp(ll, "zh") && strcmp(ll, "en")) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s No base translation \"%s\" " |
| "is included in file."), prefix, ll); |
| |
| if (!warn) |
| errors ++; |
| } |
| |
| cupsArrayRestore(languages); |
| } |
| |
| /* |
| * Free memory used for the languages... |
| */ |
| |
| _ppdFreeLanguages(languages); |
| } |
| |
| return (errors); |
| } |
| |
| |
| /* |
| * 'show_conflicts()' - Show option conflicts in a PPD file. |
| */ |
| |
| static void |
| show_conflicts(ppd_file_t *ppd, /* I - PPD to check */ |
| const char *prefix) /* I - Prefix string */ |
| { |
| int i, j; /* Looping variables */ |
| ppd_const_t *c; /* Current constraint */ |
| ppd_option_t *o1, *o2; /* Options */ |
| ppd_choice_t *c1, *c2; /* Choices */ |
| |
| |
| /* |
| * Loop through all of the UI constraints and report any options |
| * that conflict... |
| */ |
| |
| for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++) |
| { |
| /* |
| * Grab pointers to the first option... |
| */ |
| |
| o1 = ppdFindOption(ppd, c->option1); |
| |
| if (o1 == NULL) |
| continue; |
| else if (c->choice1[0] != '\0') |
| { |
| /* |
| * This constraint maps to a specific choice. |
| */ |
| |
| c1 = ppdFindChoice(o1, c->choice1); |
| } |
| else |
| { |
| /* |
| * This constraint applies to any choice for this option. |
| */ |
| |
| for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++) |
| if (c1->marked) |
| break; |
| |
| if (j == 0 || |
| !_cups_strcasecmp(c1->choice, "None") || |
| !_cups_strcasecmp(c1->choice, "Off") || |
| !_cups_strcasecmp(c1->choice, "False")) |
| c1 = NULL; |
| } |
| |
| /* |
| * Grab pointers to the second option... |
| */ |
| |
| o2 = ppdFindOption(ppd, c->option2); |
| |
| if (o2 == NULL) |
| continue; |
| else if (c->choice2[0] != '\0') |
| { |
| /* |
| * This constraint maps to a specific choice. |
| */ |
| |
| c2 = ppdFindChoice(o2, c->choice2); |
| } |
| else |
| { |
| /* |
| * This constraint applies to any choice for this option. |
| */ |
| |
| for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++) |
| if (c2->marked) |
| break; |
| |
| if (j == 0 || |
| !_cups_strcasecmp(c2->choice, "None") || |
| !_cups_strcasecmp(c2->choice, "Off") || |
| !_cups_strcasecmp(c2->choice, "False")) |
| c2 = NULL; |
| } |
| |
| /* |
| * If both options are marked then there is a conflict... |
| */ |
| |
| if (c1 != NULL && c1->marked && c2 != NULL && c2->marked) |
| _cupsLangPrintf(stdout, |
| _(" %s \"%s %s\" conflicts with \"%s %s\"\n" |
| " (constraint=\"%s %s %s %s\")."), |
| prefix, o1->keyword, c1->choice, o2->keyword, c2->choice, |
| c->option1, c->choice1, c->option2, c->choice2); |
| } |
| } |
| |
| |
| /* |
| * '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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Default option code cannot be " |
| "interpreted: %s"), 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")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" **FAIL** Default option code cannot be " |
| "interpreted: %s"), cupsRasterErrorString()); |
| |
| return (0); |
| } |
| |
| return (1); |
| } |
| |
| |
| /* |
| * 'usage()' - Show program usage. |
| */ |
| |
| static void |
| usage(void) |
| { |
| _cupsLangPuts(stdout, _("Warning: This program will be removed in a future version of CUPS.")); |
| _cupsLangPuts(stdout, _("Usage: cupstestppd [options] filename1.ppd[.gz] [... filenameN.ppd[.gz]]\n" |
| " program | cupstestppd [options] -")); |
| _cupsLangPuts(stdout, _("Options:")); |
| _cupsLangPuts(stdout, _("-I {filename,filters,none,profiles}\n" |
| " Ignore specific warnings")); |
| _cupsLangPuts(stdout, _("-R root-directory Set alternate root")); |
| _cupsLangPuts(stdout, _("-W {all,none,constraints,defaults,duplex,filters,profiles,sizes,translations}\n" |
| " Issue warnings instead of errors")); |
| _cupsLangPuts(stdout, _("-q Run silently")); |
| _cupsLangPuts(stdout, _("-r Use 'relaxed' open mode")); |
| _cupsLangPuts(stdout, _("-v Be verbose")); |
| _cupsLangPuts(stdout, _("-vv Be very verbose")); |
| |
| exit(ERROR_USAGE); |
| } |
| |
| |
| /* |
| * 'valid_path()' - Check whether a path has the correct capitalization. |
| */ |
| |
| static int /* O - Errors found */ |
| valid_path(const char *keyword, /* I - Keyword using path */ |
| const char *path, /* I - Path to check */ |
| int errors, /* I - Errors found */ |
| int verbose, /* I - Verbosity level */ |
| int warn) /* I - Warnings only? */ |
| { |
| cups_dir_t *dir; /* Current directory */ |
| cups_dentry_t *dentry; /* Current directory entry */ |
| char temp[1024], /* Temporary path */ |
| *ptr; /* Pointer into temporary path */ |
| const char *prefix; /* WARN/FAIL prefix */ |
| |
| |
| prefix = warn ? " WARN " : "**FAIL**"; |
| |
| /* |
| * Loop over the components of the path, checking that the entry exists with |
| * the same capitalization... |
| */ |
| |
| strlcpy(temp, path, sizeof(temp)); |
| |
| while ((ptr = strrchr(temp, '/')) != NULL) |
| { |
| /* |
| * Chop off the trailing component so temp == dirname and ptr == basename. |
| */ |
| |
| *ptr++ = '\0'; |
| |
| /* |
| * Try opening the directory containing the base name... |
| */ |
| |
| if (temp[0]) |
| dir = cupsDirOpen(temp); |
| else |
| dir = cupsDirOpen("/"); |
| |
| if (!dir) |
| dentry = NULL; |
| else |
| { |
| while ((dentry = cupsDirRead(dir)) != NULL) |
| { |
| if (!strcmp(dentry->filename, ptr)) |
| break; |
| } |
| |
| cupsDirClose(dir); |
| } |
| |
| /* |
| * Display an error if the filename doesn't exist with the same |
| * capitalization... |
| */ |
| |
| if (!dentry) |
| { |
| if (!warn && !errors && !verbose) |
| _cupsLangPuts(stdout, _(" FAIL")); |
| |
| if (verbose >= 0) |
| _cupsLangPrintf(stdout, |
| _(" %s %s file \"%s\" has the wrong " |
| "capitalization."), prefix, keyword, path); |
| |
| if (!warn) |
| errors ++; |
| |
| break; |
| } |
| } |
| |
| return (errors); |
| } |
| |
| |
| /* |
| * '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); |
| } |