#include <cups/string.h>
#include <cups/cups.h>
#include <cups/i18n.h>
#include <filter/raster.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
enum
{
WARN_NONE = 0,
WARN_CONSTRAINTS = 1,
WARN_DEFAULTS = 2,
WARN_FILTERS = 4,
WARN_TRANSLATIONS = 8,
WARN_ALL = 15
};
enum
{
ERROR_NONE = 0,
ERROR_USAGE,
ERROR_FILE_OPEN,
ERROR_PPD_FORMAT,
ERROR_CONFORMANCE
};
enum
{
EOL_NONE = 0,
EOL_CR,
EOL_LF,
EOL_CRLF
};
static void check_basics(const char *filename);
static int check_constraints(ppd_file_t *ppd, int errors, int verbose,
int warn);
static int check_defaults(ppd_file_t *ppd, int errors, int verbose,
int warn);
static int check_filters(ppd_file_t *ppd, const char *root, int errors,
int verbose, int warn);
static int check_translations(ppd_file_t *ppd, int errors, int verbose,\
int warn);
static void show_conflicts(ppd_file_t *ppd);
static int test_raster(ppd_file_t *ppd, int verbose);
static void usage(void);
static int valid_utf8(const char *s);
int
main(int argc,
char *argv[])
{
int i, j, k, m, n;
int len;
char *opt;
const char *ptr;
int files;
int verbose;
int warn;
int status;
int errors;
int ppdversion;
ppd_status_t error;
int line;
struct stat statbuf;
char pathprog[1024],
*root;
int xdpi,
ydpi;
ppd_file_t *ppd;
ppd_attr_t *attr;
ppd_size_t *size;
ppd_group_t *group;
ppd_option_t *option;
ppd_group_t *group2;
ppd_option_t *option2;
ppd_choice_t *choice;
static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
"JCL", "PAGE", "PROLOG" };
_cupsSetLocale(argv);
ppdSetConformance(PPD_CONFORM_STRICT);
verbose = 0;
ppd = NULL;
files = 0;
status = ERROR_NONE;
root = "";
warn = WARN_NONE;
for (i = 1; i < argc; i ++)
if (argv[i][0] == '-' && argv[i][1])
{
for (opt = argv[i] + 1; *opt; opt ++)
switch (*opt)
{
case 'R' :
i ++;
if (i >= argc)
usage();
root = argv[i];
break;
case 'W' :
i ++;
if (i >= argc)
usage();
if (!strcmp(argv[i], "none"))
warn = WARN_NONE;
else if (!strcmp(argv[i], "constraints"))
warn |= WARN_CONSTRAINTS;
else if (!strcmp(argv[i], "defaults"))
warn |= WARN_DEFAULTS;
else if (!strcmp(argv[i], "filters"))
warn |= WARN_FILTERS;
else if (!strcmp(argv[i], "translations"))
warn |= WARN_TRANSLATIONS;
else if (!strcmp(argv[i], "all"))
warn = WARN_ALL;
else
usage();
break;
case 'q' :
if (verbose > 0)
{
_cupsLangPuts(stderr,
_("cupstestppd: The -q option is incompatible "
"with the -v option.\n"));
return (1);
}
verbose --;
break;
case 'r' :
ppdSetConformance(PPD_CONFORM_RELAXED);
break;
case 'v' :
if (verbose < 0)
{
_cupsLangPuts(stderr,
_("cupstestppd: The -v option is incompatible "
"with the -q option.\n"));
return (1);
}
verbose ++;
break;
default :
usage();
break;
}
}
else
{
if (files && verbose >= 0)
_cupsLangPuts(stdout, "\n");
files ++;
if (argv[i][0] == '-')
{
if (verbose >= 0)
printf("(stdin):");
ppd = ppdOpen(stdin);
}
else
{
if (verbose >= 0)
printf("%s:", argv[i]);
ppd = ppdOpenFile(argv[i]);
}
if (ppd == NULL)
{
error = ppdLastError(&line);
if (error <= PPD_ALLOC_ERROR)
{
status = ERROR_FILE_OPEN;
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" FAIL\n"
" **FAIL** Unable to open PPD file - %s\n"),
strerror(errno));
}
else
{
status = ERROR_PPD_FORMAT;
if (verbose >= 0)
{
_cupsLangPrintf(stdout,
_(" FAIL\n"
" **FAIL** Unable to open PPD file - "
"%s on line %d.\n"),
ppdErrorString(error), line);
switch (error)
{
case PPD_MISSING_PPDADOBE4 :
_cupsLangPuts(stdout,
_(" REF: Page 42, section 5.2.\n"));
break;
case PPD_MISSING_VALUE :
_cupsLangPuts(stdout,
_(" REF: Page 20, section 3.4.\n"));
break;
case PPD_BAD_OPEN_GROUP :
case PPD_NESTED_OPEN_GROUP :
_cupsLangPuts(stdout,
_(" REF: Pages 45-46, section 5.2.\n"));
break;
case PPD_BAD_OPEN_UI :
case PPD_NESTED_OPEN_UI :
_cupsLangPuts(stdout,
_(" REF: Pages 42-45, section 5.2.\n"));
break;
case PPD_BAD_ORDER_DEPENDENCY :
_cupsLangPuts(stdout,
_(" REF: Pages 48-49, section 5.2.\n"));
break;
case PPD_BAD_UI_CONSTRAINTS :
_cupsLangPuts(stdout,
_(" REF: Pages 52-54, section 5.2.\n"));
break;
case PPD_MISSING_ASTERISK :
_cupsLangPuts(stdout,
_(" REF: Page 15, section 3.2.\n"));
break;
case PPD_LINE_TOO_LONG :
_cupsLangPuts(stdout,
_(" REF: Page 15, section 3.1.\n"));
break;
case PPD_ILLEGAL_CHARACTER :
_cupsLangPuts(stdout,
_(" REF: Page 15, section 3.1.\n"));
break;
case PPD_ILLEGAL_MAIN_KEYWORD :
_cupsLangPuts(stdout,
_(" REF: Pages 16-17, section 3.2.\n"));
break;
case PPD_ILLEGAL_OPTION_KEYWORD :
_cupsLangPuts(stdout,
_(" REF: Page 19, section 3.3.\n"));
break;
case PPD_ILLEGAL_TRANSLATION :
_cupsLangPuts(stdout,
_(" REF: Page 27, section 3.5.\n"));
break;
default :
break;
}
check_basics(argv[i]);
}
}
continue;
}
errors = 0;
ppdversion = 43;
if (verbose > 0)
_cupsLangPuts(stdout,
_("\n DETAILED CONFORMANCE TEST RESULTS\n"));
if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
attr->value)
ppdversion = (int)(10 * atof(attr->value) + 0.5);
for (j = 0; j < ppd->num_filters; j ++)
if (strstr(ppd->filters[j], "application/vnd.cups-raster"))
{
if (!test_raster(ppd, verbose))
errors ++;
break;
}
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\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED DefaultImageableArea\n"
" REF: Page 102, section 5.15.\n"));
}
errors ++;
}
else if (ppdPageSize(ppd, attr->value) == NULL &&
strcmp(attr->value, "Unknown"))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** BAD DefaultImageableArea %s!\n"
" REF: Page 102, section 5.15.\n"),
attr->value);
}
errors ++;
}
else
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS DefaultImageableArea\n"));
}
if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED DefaultPaperDimension\n"
" REF: Page 103, section 5.15.\n"));
}
errors ++;
}
else if (ppdPageSize(ppd, attr->value) == NULL &&
strcmp(attr->value, "Unknown"))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** BAD DefaultPaperDimension %s!\n"
" REF: Page 103, section 5.15.\n"),
attr->value);
}
errors ++;
}
else if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS DefaultPaperDimension\n"));
for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
{
if (option->defchoice[0])
{
if (ppdFindChoice(option, option->defchoice) == NULL &&
strcmp(option->defchoice, "Unknown"))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** BAD Default%s %s\n"
" REF: Page 40, section 4.5.\n"),
option->keyword, option->defchoice);
}
errors ++;
}
else if (verbose > 0)
_cupsLangPrintf(stdout,
_(" PASS Default%s\n"),
option->keyword);
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** REQUIRED Default%s\n"
" REF: Page 40, section 4.5.\n"),
option->keyword);
}
errors ++;
}
}
if (ppdFindAttr(ppd, "FileVersion", NULL) != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS FileVersion\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED FileVersion\n"
" REF: Page 56, section 5.3.\n"));
}
errors ++;
}
if (ppdFindAttr(ppd, "FormatVersion", NULL) != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS FormatVersion\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED FormatVersion\n"
" REF: Page 56, section 5.3.\n"));
}
errors ++;
}
if (ppd->lang_encoding != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS LanguageEncoding\n"));
}
else if (ppdversion > 40)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED LanguageEncoding\n"
" REF: Pages 56-57, section 5.3.\n"));
}
errors ++;
}
if (ppd->lang_version != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS LanguageVersion\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED LanguageVersion\n"
" REF: Pages 57-58, section 5.3.\n"));
}
errors ++;
}
if (ppd->manufacturer != NULL)
{
if (!strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) ||
!strncasecmp(ppd->manufacturer, "Hewlett Packard", 15))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** BAD Manufacturer (should be "
"\"HP\")\n"
" REF: Page 211, table D.1.\n"));
}
errors ++;
}
else if (!strncasecmp(ppd->manufacturer, "OkiData", 7) ||
!strncasecmp(ppd->manufacturer, "Oki Data", 8))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** BAD Manufacturer (should be "
"\"Oki\")\n"
" REF: Page 211, table D.1.\n"));
}
errors ++;
}
else if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS Manufacturer\n"));
}
else if (ppdversion >= 43)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED Manufacturer\n"
" REF: Pages 58-59, section 5.3.\n"));
}
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\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** BAD ModelName - \"%c\" not "
"allowed in string.\n"
" REF: Pages 59-60, section 5.3.\n"),
*ptr);
}
errors ++;
}
else if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS ModelName\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED ModelName\n"
" REF: Pages 59-60, section 5.3.\n"));
}
errors ++;
}
if (ppd->nickname != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS NickName\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED NickName\n"
" REF: Page 60, section 5.3.\n"));
}
errors ++;
}
if (ppdFindOption(ppd, "PageSize") != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS PageSize\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED PageSize\n"
" REF: Pages 99-100, section 5.14.\n"));
}
errors ++;
}
if (ppdFindOption(ppd, "PageRegion") != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS PageRegion\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED PageRegion\n"
" REF: Page 100, section 5.14.\n"));
}
errors ++;
}
if (ppd->pcfilename != NULL)
{
if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS PCFileName\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED PCFileName\n"
" REF: Pages 61-62, section 5.3.\n"));
}
errors ++;
}
if (ppd->product != NULL)
{
if (ppd->product[0] != '(' ||
ppd->product[strlen(ppd->product) - 1] != ')')
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** BAD Product - not \"(string)\".\n"
" REF: Page 62, section 5.3.\n"));
}
errors ++;
}
else if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS Product\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED Product\n"
" REF: Page 62, section 5.3.\n"));
}
errors ++;
}
if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
attr->value != NULL)
{
char junkstr[255];
int junkint;
if (sscanf(attr->value, "(%[^)])%d", junkstr, &junkint) != 2)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** BAD PSVersion - not \"(string) "
"int\".\n"
" REF: Pages 62-64, section 5.3.\n"));
}
errors ++;
}
else if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS PSVersion\n"));
}
else
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED PSVersion\n"
" REF: Pages 62-64, section 5.3.\n"));
}
errors ++;
}
if (ppd->shortnickname != NULL)
{
if (strlen(ppd->shortnickname) > 31)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** BAD ShortNickName - longer "
"than 31 chars.\n"
" REF: Pages 64-65, section 5.3.\n"));
}
errors ++;
}
else if (verbose > 0)
_cupsLangPuts(stdout, _(" PASS ShortNickName\n"));
}
else if (ppdversion >= 43)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED ShortNickName\n"
" REF: Page 64-65, section 5.3.\n"));
}
errors ++;
}
if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
strstr(ppd->patches, "*End"))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** BAD JobPatchFile attribute in file\n"
" REF: Page 24, section 3.4.\n"));
}
errors ++;
}
if (ppd->num_sizes == 0)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPuts(stdout,
_(" **FAIL** REQUIRED PageSize\n"
" REF: Page 41, section 5.\n"
" REF: Page 99, section 5.14.\n"));
}
errors ++;
}
else
{
for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++)
{
if (!strcmp(size->name, "Custom"))
continue;
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\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** REQUIRED ImageableArea for "
"PageSize %s\n"
" REF: Page 41, section 5.\n"
" REF: Page 102, section 5.15.\n"),
size->name);
}
errors ++;
}
if (size->width == 0.0 && size->length == 0.0)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** REQUIRED PaperDimension "
"for PageSize %s\n"
" REF: Page 41, section 5.\n"
" REF: Page 103, section 5.15.\n"),
size->name);
}
errors ++;
}
}
}
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 ++)
{
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\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** Bad %s choice %s!\n"
" REF: Page 84, section 5.9\n"),
option->keyword, choice->choice);
}
errors ++;
}
}
}
if ((option = ppdFindOption(ppd, "Duplex")) == NULL)
if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
option = ppdFindOption(ppd, "KD03Duplex");
if (option != NULL)
{
if (ppdFindChoice(option, "None") == NULL)
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** REQUIRED %s does not define "
"choice None!\n"
" REF: Page 122, section 5.17\n"),
option->keyword);
}
errors ++;
}
for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
if (strcmp(choice->choice, "None") &&
strcmp(choice->choice, "DuplexNoTumble") &&
strcmp(choice->choice, "DuplexTumble") &&
strcmp(choice->choice, "SimplexTumble"))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** Bad %s choice %s!\n"
" REF: Page 122, section 5.17\n"),
option->keyword, choice->choice);
}
errors ++;
}
}
if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
strcmp(attr->name, "1284DeviceID"))
{
if (verbose >= 0)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" **FAIL** %s must be 1284DeviceID!\n"
" REF: Page 72, section 5.5\n"),
attr->name);
}
errors ++;
}
if (!(warn & WARN_CONSTRAINTS))
errors = check_constraints(ppd, errors, verbose, 0);
if (!(warn & WARN_FILTERS))
errors = check_filters(ppd, root, errors, verbose, 0);
if (!(warn & WARN_TRANSLATIONS))
errors = check_translations(ppd, errors, verbose, 0);
if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
attr->value)
{
if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL)
{
if (!attr->value || strcmp(attr->value, "ISOLatin1"))
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" **FAIL** Bad LanguageEncoding %s - "
"must be ISOLatin1!\n"),
attr->value ? attr->value : "(null)");
errors ++;
}
if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" **FAIL** Bad LanguageVersion %s - "
"must be English!\n"),
ppd->lang_version ? ppd->lang_version : "(null)");
errors ++;
}
for (option = ppdFirstOption(ppd);
option;
option = ppdNextOption(ppd))
{
for (ptr = option->text; *ptr; ptr ++)
if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
(*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
break;
if (*ptr)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" **FAIL** Default translation "
"string for option %s contains 8-bit "
"characters!\n"),
option->keyword);
errors ++;
}
for (j = 0; j < option->num_choices; j ++)
{
for (ptr = option->choices[j].text; *ptr; ptr ++)
if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
(*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
break;
if (*ptr)
{
if (!errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" **FAIL** Default translation "
"string for option %s choice %s contains "
"8-bit characters!\n"),
option->keyword,
option->choices[j].choice);
errors ++;
}
}
}
}
}
if (errors)
status = ERROR_CONFORMANCE;
else if (!verbose)
_cupsLangPuts(stdout, _(" PASS\n"));
if (verbose >= 0)
{
check_basics(argv[i]);
if (warn & WARN_CONSTRAINTS)
errors = check_constraints(ppd, errors, verbose, 1);
if (warn & WARN_DEFAULTS)
errors = check_defaults(ppd, errors, verbose, 1);
if (warn & WARN_FILTERS)
errors = check_filters(ppd, root, errors, verbose, 1);
if (warn & WARN_TRANSLATIONS)
errors = check_translations(ppd, errors, verbose, 1);
for (j = 0; j < ppd->num_attrs; j ++)
{
attr = ppd->attrs[j];
if (!strcmp(attr->name, "DefaultColorSpace") ||
!strcmp(attr->name, "DefaultColorSep") ||
!strcmp(attr->name, "DefaultFont") ||
!strcmp(attr->name, "DefaultHalftoneType") ||
!strcmp(attr->name, "DefaultImageableArea") ||
!strcmp(attr->name, "DefaultLeadingEdge") ||
!strcmp(attr->name, "DefaultOutputOrder") ||
!strcmp(attr->name, "DefaultPaperDimension") ||
!strcmp(attr->name, "DefaultResolution") ||
!strcmp(attr->name, "DefaultScreenProc") ||
!strcmp(attr->name, "DefaultTransfer"))
continue;
if (!strncmp(attr->name, "Default", 7) &&
!ppdFindOption(ppd, attr->name + 7))
_cupsLangPrintf(stdout,
_(" WARN %s has no corresponding "
"options!\n"),
attr->name);
}
if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
option = ppdFindOption(ppd, "KD03Duplex");
if (option)
{
_cupsLangPrintf(stdout,
_(" WARN Duplex option keyword %s "
"should be named Duplex or JCLDuplex!\n"
" REF: Page 122, section 5.17\n"),
option->keyword);
}
ppdMarkDefaults(ppd);
if (ppdConflicts(ppd))
{
_cupsLangPuts(stdout,
_(" WARN Default choices conflicting!\n"));
show_conflicts(ppd);
}
if (ppdversion < 43)
{
_cupsLangPrintf(stdout,
_(" WARN Obsolete PPD version %.1f!\n"
" REF: Page 42, section 5.2.\n"),
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.\n"));
}
if (!ppd->manufacturer && ppdversion < 43)
{
_cupsLangPuts(stdout,
_(" WARN Manufacturer required by PPD "
"4.3 spec.\n"
" REF: Pages 58-59, section 5.3.\n"));
}
if (ppd->pcfilename && 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.\n"));
}
if (!ppd->shortnickname && ppdversion < 43)
{
_cupsLangPuts(stdout,
_(" WARN ShortNickName required by PPD "
"4.3 spec.\n"
" REF: Pages 64-65, section 5.3.\n"));
}
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.\n"));
}
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.\n"));
}
}
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.\n"),
option->keyword, option2->keyword);
}
}
}
for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
attr != NULL;
attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
{
if (attr->value)
{
if (attr->value[0] == '/')
snprintf(pathprog, sizeof(pathprog), "%s%s", root, attr->value);
else
{
if ((ptr = getenv("CUPS_DATADIR")) == NULL)
ptr = CUPS_DATADIR;
if (*ptr == '/' || !*root)
snprintf(pathprog, sizeof(pathprog), "%s%s/profiles/%s", root,
ptr, attr->value);
else
snprintf(pathprog, sizeof(pathprog), "%s/%s/profiles/%s", root,
ptr, attr->value);
}
}
if (!attr->value || !attr->value[0] || stat(pathprog, &statbuf))
{
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" WARN Missing cupsICCProfile "
"file \"%s\"\n"),
!attr->value || !attr->value[0] ? "<NULL>" :
attr->value);
}
}
#ifdef __APPLE__
for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL);
attr != NULL;
attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
{
if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
_cupsLangPrintf(stdout, _(" WARN Missing "
"APDialogExtension file \"%s\"\n"),
attr->value ? attr->value : "<NULL>");
}
for (attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL);
attr != NULL;
attr = ppdFindNextAttr(ppd, "APPrinterIconPath", NULL))
{
if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
_cupsLangPrintf(stdout, _(" WARN Missing "
"APPrinterIconPath file \"%s\"\n"),
attr->value ? attr->value : "<NULL>");
}
#endif
if (verbose > 0)
{
if (errors)
_cupsLangPrintf(stdout, _(" %d ERRORS FOUND\n"), errors);
else
_cupsLangPuts(stdout, _(" NO ERRORS FOUND\n"));
}
if (verbose > 1)
{
_cupsLangPrintf(stdout,
"\n"
" language_level = %d\n"
" color_device = %s\n"
" variable_sizes = %s\n"
" landscape = %d\n",
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\n");
break;
case PPD_CS_CMY :
_cupsLangPuts(stdout, " colorspace = PPD_CS_CMY\n");
break;
case PPD_CS_GRAY :
_cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY\n");
break;
case PPD_CS_RGB :
_cupsLangPuts(stdout, " colorspace = PPD_CS_RGB\n");
break;
default :
_cupsLangPuts(stdout, " colorspace = <unknown>\n");
break;
}
_cupsLangPrintf(stdout, " num_emulations = %d\n",
ppd->num_emulations);
for (j = 0; j < ppd->num_emulations; j ++)
_cupsLangPrintf(stdout, " emulations[%d] = %s\n",
j, ppd->emulations[j].name);
_cupsLangPrintf(stdout, " lang_encoding = %s\n",
ppd->lang_encoding);
_cupsLangPrintf(stdout, " lang_version = %s\n",
ppd->lang_version);
_cupsLangPrintf(stdout, " modelname = %s\n", ppd->modelname);
_cupsLangPrintf(stdout, " ttrasterizer = %s\n",
ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
_cupsLangPrintf(stdout, " manufacturer = %s\n",
ppd->manufacturer);
_cupsLangPrintf(stdout, " product = %s\n", ppd->product);
_cupsLangPrintf(stdout, " nickname = %s\n", ppd->nickname);
_cupsLangPrintf(stdout, " shortnickname = %s\n",
ppd->shortnickname);
_cupsLangPrintf(stdout, " patches = %d bytes\n",
ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
_cupsLangPrintf(stdout, " num_groups = %d\n", ppd->num_groups);
for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
{
_cupsLangPrintf(stdout, " group[%d] = %s\n",
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)\n",
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",
choice->choice, choice->text);
else
_cupsLangPrintf(stdout,
" %s (%s) = %.2fx%.2fin "
"(%.1f,%.1f,%.1f,%.1f)",
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);
if (!strcmp(option->defchoice, choice->choice))
_cupsLangPuts(stdout, " *\n");
else
_cupsLangPuts(stdout, "\n");
}
}
else
{
for (m = option->num_choices, choice = option->choices;
m > 0;
m --, choice ++)
{
_cupsLangPrintf(stdout, " %s (%s)",
choice->choice, choice->text);
if (!strcmp(option->defchoice, choice->choice))
_cupsLangPuts(stdout, " *\n");
else
_cupsLangPuts(stdout, "\n");
}
}
}
}
_cupsLangPrintf(stdout, " num_consts = %d\n",
ppd->num_consts);
for (j = 0; j < ppd->num_consts; j ++)
_cupsLangPrintf(stdout,
" consts[%d] = *%s %s *%s %s\n",
j, ppd->consts[j].option1, ppd->consts[j].choice1,
ppd->consts[j].option2, ppd->consts[j].choice2);
_cupsLangPrintf(stdout, " num_profiles = %d\n",
ppd->num_profiles);
for (j = 0; j < ppd->num_profiles; j ++)
_cupsLangPrintf(stdout,
" profiles[%d] = %s/%s %.3f %.3f "
"[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
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\n", ppd->num_fonts);
for (j = 0; j < ppd->num_fonts; j ++)
_cupsLangPrintf(stdout, " fonts[%d] = %s\n",
j, ppd->fonts[j]);
_cupsLangPrintf(stdout, " num_attrs = %d\n", ppd->num_attrs);
for (j = 0; j < ppd->num_attrs; j ++)
_cupsLangPrintf(stdout,
" attrs[%d] = %s %s%s%s: \"%s\"\n", 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);
}
static void
check_basics(const char *filename)
{
cups_file_t *fp;
int ch;
int col,
whitespace;
int eol;
int linenum;
int mixed;
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
mixed = 1;
}
else if (eol == EOL_NONE)
eol = EOL_CR;
else
mixed = 1;
}
if (col > 0 && whitespace)
_cupsLangPrintf(stdout,
_(" WARN Line %d only contains whitespace!\n"),
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!\n"));
if (eol == EOL_CRLF)
_cupsLangPuts(stdout,
_(" WARN Non-Windows PPD files should use lines "
"ending with only LF, not CR LF!\n"));
cupsFileClose(fp);
}
static int
check_constraints(ppd_file_t *ppd,
int errors,
int verbose,
int warn)
{
int j;
ppd_const_t *c;
ppd_option_t *option;
ppd_option_t *option2;
const char *prefix;
prefix = warn ? " WARN " : "**FAIL**";
for (j = ppd->num_consts, c = ppd->consts; j > 0; j --, c ++)
{
option = ppdFindOption(ppd, c->option1);
option2 = ppdFindOption(ppd, c->option2);
if (!option || !option2)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (!option)
_cupsLangPrintf(stdout,
_(" %s Missing option %s in "
"UIConstraint \"*%s %s *%s %s\"!\n"),
prefix, c->option1,
c->option1, c->choice1, c->option2, c->choice2);
if (!option2)
_cupsLangPrintf(stdout,
_(" %s Missing option %s in "
"UIConstraint \"*%s %s *%s %s\"!\n"),
prefix, c->option2,
c->option1, c->choice1, c->option2, c->choice2);
if (!warn)
errors ++;
continue;
}
if (c->choice1[0] && !ppdFindChoice(option, c->choice1))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" %s Missing choice *%s %s in "
"UIConstraint \"*%s %s *%s %s\"!\n"),
prefix, c->option1, c->choice1,
c->option1, c->choice1, c->option2, c->choice2);
if (!warn)
errors ++;
}
if (c->choice2[0] && !ppdFindChoice(option2, c->choice2))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
_cupsLangPrintf(stdout,
_(" %s Missing choice *%s %s in "
"UIConstraint \"*%s %s *%s %s\"!\n"),
prefix, c->option2, c->choice2,
c->option1, c->choice1, c->option2, c->choice2);
if (!warn)
errors ++;
}
}
return (errors);
}
static int
check_defaults(ppd_file_t *ppd,
int errors,
int verbose,
int warn)
{
int j, k;
ppd_attr_t *attr;
ppd_option_t *option;
const char *prefix;
prefix = warn ? " WARN " : "**FAIL**";
for (j = 0; j < ppd->num_attrs; j ++)
{
attr = ppd->attrs[j];
if (!strcmp(attr->name, "DefaultColorSpace") ||
!strcmp(attr->name, "DefaultFont") ||
!strcmp(attr->name, "DefaultHalftoneType") ||
!strcmp(attr->name, "DefaultImageableArea") ||
!strcmp(attr->name, "DefaultLeadingEdge") ||
!strcmp(attr->name, "DefaultOutputOrder") ||
!strcmp(attr->name, "DefaultPaperDimension") ||
!strcmp(attr->name, "DefaultResolution") ||
!strcmp(attr->name, "DefaultTransfer"))
continue;
if (!strncmp(attr->name, "Default", 7))
{
if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
strcmp(attr->value, "Unknown"))
{
for (k = 0; k < option->num_choices; k ++)
if (!strcmp(option->choices[k].choice, attr->value))
break;
if (k >= option->num_choices)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s %s %s does not exist!\n"),
prefix, attr->name, attr->value);
if (!warn)
errors ++;
}
}
}
}
return (errors);
}
static int
check_filters(ppd_file_t *ppd,
const char *root,
int errors,
int verbose,
int warn)
{
ppd_attr_t *attr;
const char *ptr;
struct stat statbuf;
char super[16],
type[256],
program[256],
pathprog[1024];
int cost;
const char *prefix;
prefix = warn ? " WARN " : "**FAIL**";
for (attr = ppdFindAttr(ppd, "cupsFilter", NULL);
attr;
attr = ppdFindNextAttr(ppd, "cupsFilter", NULL))
{
if (!attr->value ||
sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
program) != 4)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Bad cupsFilter value \"%s\"!\n"),
prefix, attr->value ? attr->value : "");
if (!warn)
errors ++;
}
else
{
if (program[0] == '/')
snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
else
{
if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
ptr = CUPS_SERVERBIN;
if (*ptr == '/' || !*root)
snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
program);
else
snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
program);
}
if (stat(pathprog, &statbuf))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout, _(" %s Missing cupsFilter "
"file \"%s\"\n"), prefix, program);
if (!warn)
errors ++;
}
}
}
for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
attr;
attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
{
if (!attr->value ||
sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
program) != 4)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Bad cupsPreFilter value \"%s\"!\n"),
prefix, attr->value ? attr->value : "");
if (!warn)
errors ++;
}
else
{
if (program[0] == '/')
snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
else
{
if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
ptr = CUPS_SERVERBIN;
if (*ptr == '/' || !*root)
snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
program);
else
snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
program);
}
if (stat(pathprog, &statbuf))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout, _(" %s Missing cupsPreFilter "
"file \"%s\"\n"), prefix, program);
if (!warn)
errors ++;
}
}
}
return (errors);
}
static int
check_translations(ppd_file_t *ppd,
int errors,
int verbose,
int warn)
{
int j;
ppd_attr_t *attr;
char *languages,
*langstart,
*langptr,
keyword[PPD_MAX_NAME],
llkeyword[PPD_MAX_NAME],
ckeyword[PPD_MAX_NAME],
cllkeyword[PPD_MAX_NAME];
ppd_option_t *option;
ppd_coption_t *coption;
ppd_cparam_t *cparam;
cups_array_t *langlist;
char ll[3];
const char *prefix;
prefix = warn ? " WARN " : "**FAIL**";
if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
attr->value)
{
languages = strdup(attr->value);
langlist = cupsArrayNew((cups_array_func_t)strcmp, NULL);
for (langptr = languages; *langptr;)
{
while (isspace(*langptr & 255))
langptr ++;
if (!*langptr)
break;
for (langstart = langptr;
*langptr && !isspace(*langptr & 255);
langptr ++);
if (*langptr)
*langptr++ = '\0';
j = strlen(langstart);
if (j != 2 && j != 5)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Bad language \"%s\"!\n"),
prefix, langstart);
if (!warn)
errors ++;
continue;
}
cupsArrayAdd(langlist, langstart);
strlcpy(ll, langstart, sizeof(ll));
for (option = ppdFirstOption(ppd);
option;
option = ppdNextOption(ppd))
{
if (!strcmp(option->keyword, "PageRegion"))
continue;
snprintf(keyword, sizeof(keyword), "%s.Translation", langstart);
snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll);
if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL &&
(attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Missing \"%s\" translation "
"string for option %s!\n"),
prefix, langstart, option->keyword);
if (!warn)
errors ++;
}
else if (!valid_utf8(attr->text))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Bad UTF-8 \"%s\" translation "
"string for option %s!\n"),
prefix, langstart, option->keyword);
if (!warn)
errors ++;
}
snprintf(keyword, sizeof(keyword), "%s.%s", langstart,
option->keyword);
snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll,
option->keyword);
for (j = 0; j < option->num_choices; j ++)
{
if (!strcasecmp(option->choices[j].choice, "Custom") &&
(coption = ppdFindCustomOption(ppd,
option->keyword)) != NULL)
{
snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
langstart, option->keyword);
if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL &&
!valid_utf8(attr->text))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Bad UTF-8 \"%s\" "
"translation string for option %s, "
"choice %s!\n"),
prefix, langstart,
ckeyword + 1 + strlen(langstart),
"True");
if (!warn)
errors ++;
}
if (strcasecmp(option->keyword, "PageSize"))
{
for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
cparam;
cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
{
snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
langstart, option->keyword);
snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s",
ll, option->keyword);
if ((attr = ppdFindAttr(ppd, ckeyword,
cparam->name)) == NULL &&
(attr = ppdFindAttr(ppd, cllkeyword,
cparam->name)) == NULL)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Missing \"%s\" "
"translation string for option %s, "
"choice %s!\n"),
prefix, langstart,
ckeyword + 1 + strlen(langstart),
cparam->name);
if (!warn)
errors ++;
}
else if (!valid_utf8(attr->text))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Bad UTF-8 \"%s\" "
"translation string for option %s, "
"choice %s!\n"),
prefix, langstart,
ckeyword + 1 + strlen(langstart),
cparam->name);
if (!warn)
errors ++;
}
}
}
}
else if ((attr = ppdFindAttr(ppd, keyword,
option->choices[j].choice)) == NULL &&
(attr = ppdFindAttr(ppd, llkeyword,
option->choices[j].choice)) == NULL)
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Missing \"%s\" "
"translation string for option %s, "
"choice %s!\n"),
prefix, langstart, option->keyword,
option->choices[j].choice);
if (!warn)
errors ++;
}
else if (!valid_utf8(attr->text))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s Bad UTF-8 \"%s\" "
"translation string for option %s, "
"choice %s!\n"),
prefix, langstart, option->keyword,
option->choices[j].choice);
if (!warn)
errors ++;
}
}
}
}
for (langptr = (char *)cupsArrayFirst(langlist);
langptr;
langptr = (char *)cupsArrayNext(langlist))
if (langptr[2])
{
cupsArraySave(langlist);
strlcpy(ll, langptr, sizeof(ll));
if (!cupsArrayFind(langlist, ll) && strcmp(ll, "zh"))
{
if (!warn && !errors && !verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" %s No base translation \"%s\" "
"is included in file!\n"), prefix, ll);
if (!warn)
errors ++;
}
cupsArrayRestore(langlist);
}
cupsArrayDelete(langlist);
free(languages);
}
return (errors);
}
static void
show_conflicts(ppd_file_t *ppd)
{
int i, j;
ppd_const_t *c;
ppd_option_t *o1, *o2;
ppd_choice_t *c1, *c2;
for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
{
o1 = ppdFindOption(ppd, c->option1);
if (o1 == NULL)
continue;
else if (c->choice1[0] != '\0')
{
c1 = ppdFindChoice(o1, c->choice1);
}
else
{
for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
if (c1->marked)
break;
if (j == 0 ||
!strcasecmp(c1->choice, "None") ||
!strcasecmp(c1->choice, "Off") ||
!strcasecmp(c1->choice, "False"))
c1 = NULL;
}
o2 = ppdFindOption(ppd, c->option2);
if (o2 == NULL)
continue;
else if (c->choice2[0] != '\0')
{
c2 = ppdFindChoice(o2, c->choice2);
}
else
{
for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
if (c2->marked)
break;
if (j == 0 ||
!strcasecmp(c2->choice, "None") ||
!strcasecmp(c2->choice, "Off") ||
!strcasecmp(c2->choice, "False"))
c2 = NULL;
}
if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
_cupsLangPrintf(stdout,
_(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
" (constraint=\"%s %s %s %s\")\n"),
o1->keyword, c1->choice, o2->keyword, c2->choice,
c->option1, c->choice1, c->option2, c->choice2);
}
}
static int
test_raster(ppd_file_t *ppd,
int verbose)
{
cups_page_header2_t header;
ppdMarkDefaults(ppd);
if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
{
if (!verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" **FAIL** Default option code cannot be "
"interpreted: %s\n"), cupsRasterErrorString());
return (0);
}
if (!ppdPageSize(ppd, "Custom.612x792"))
return (1);
ppdMarkOption(ppd, "PageSize", "Custom.612x792");
if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
{
if (!verbose)
_cupsLangPuts(stdout, _(" FAIL\n"));
if (verbose >= 0)
_cupsLangPrintf(stdout,
_(" **FAIL** Default option code cannot be "
"interpreted: %s\n"), cupsRasterErrorString());
return (0);
}
return (1);
}
static void
usage(void)
{
_cupsLangPuts(stdout,
_("Usage: cupstestppd [options] filename1.ppd[.gz] "
"[... filenameN.ppd[.gz]]\n"
" program | cupstestppd [options] -\n"
"\n"
"Options:\n"
"\n"
" -R root-directory Set alternate root\n"
" -W {all,none,constraints,defaults,filters,translations}\n"
" Issue warnings instead of errors\n"
" -q Run silently\n"
" -r Use 'relaxed' open mode\n"
" -v Be slightly verbose\n"
" -vv Be very verbose\n"));
exit(ERROR_USAGE);
}
static int
valid_utf8(const char *s)
{
while (*s)
{
if (*s & 0x80)
{
if ((*s & 0xc0) == 0x80)
return (0);
else if ((*s & 0xe0) == 0xc0)
{
s ++;
if ((*s & 0xc0) != 0x80)
return (0);
}
else if ((*s & 0xf0) == 0xe0)
{
s ++;
if ((*s & 0xc0) != 0x80)
return (0);
s ++;
if ((*s & 0xc0) != 0x80)
return (0);
}
else if ((*s & 0xf8) == 0xf0)
{
s ++;
if ((*s & 0xc0) != 0x80)
return (0);
s ++;
if ((*s & 0xc0) != 0x80)
return (0);
s ++;
if ((*s & 0xc0) != 0x80)
return (0);
}
else
return (0);
}
s ++;
}
return (1);
}