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

4029 lines
98 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* 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);
}