]> git.ipfire.org Git - thirdparty/cups.git/blame - systemv/cupstestppd.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / systemv / cupstestppd.c
CommitLineData
ef416fc2 1/*
788d7a15 2 * PPD test program for CUPS.
ef416fc2 3 *
788d7a15
MS
4 * Copyright 2007-2016 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 8 *
788d7a15 9 * PostScript is a trademark of Adobe Systems, Inc.
ef416fc2 10 */
11
12/*
13 * Include necessary headers...
14 */
15
71e16022 16#include <cups/cups-private.h>
1340db2d 17#include <cups/dir.h>
a603edef 18#include <cups/ppd-private.h>
ac884b6a 19#include <cups/raster.h>
1340db2d 20#include <math.h>
7a0cbd5e
MS
21#ifdef WIN32
22# define X_OK 0
23#endif /* WIN32 */
ef416fc2 24
25
b94498cf 26/*
27 * Error warning overrides...
28 */
29
30enum
31{
32 WARN_NONE = 0,
33 WARN_CONSTRAINTS = 1,
34 WARN_DEFAULTS = 2,
35 WARN_FILTERS = 4,
a603edef
MS
36 WARN_PROFILES = 8,
37 WARN_TRANSLATIONS = 16,
749b1e90 38 WARN_DUPLEX = 32,
1340db2d 39 WARN_SIZES = 64,
ef55b745
MS
40 WARN_FILENAME = 128,
41 WARN_ALL = 255
b94498cf 42};
43
44
ef416fc2 45/*
46 * Error codes...
47 */
48
fa73b229 49enum
50{
51 ERROR_NONE = 0,
52 ERROR_USAGE,
53 ERROR_FILE_OPEN,
54 ERROR_PPD_FORMAT,
55 ERROR_CONFORMANCE
56};
57
58
59/*
60 * Line endings...
61 */
62
63enum
64{
65 EOL_NONE = 0,
66 EOL_CR,
67 EOL_LF,
68 EOL_CRLF
69};
ef416fc2 70
71
b226ab99
MS
72/*
73 * File permissions...
74 */
75
76#define MODE_WRITE 0022 /* Group/other write */
77#define MODE_MASK 0555 /* Owner/group/other read+exec/search */
78#define MODE_DATAFILE 0444 /* Owner/group/other read */
79#define MODE_DIRECTORY 0555 /* Owner/group/other read+search */
80#define MODE_PROGRAM 0555 /* Owner/group/other read+exec */
81
82
ef416fc2 83/*
84 * Local functions...
85 */
86
3d8365b8 87static void check_basics(const char *filename);
88static int check_constraints(ppd_file_t *ppd, int errors, int verbose,
89 int warn);
749b1e90 90static int check_case(ppd_file_t *ppd, int errors, int verbose);
3d8365b8 91static int check_defaults(ppd_file_t *ppd, int errors, int verbose,
92 int warn);
749b1e90
MS
93static int check_duplex(ppd_file_t *ppd, int errors, int verbose,
94 int warn);
3d8365b8 95static int check_filters(ppd_file_t *ppd, const char *root, int errors,
96 int verbose, int warn);
a603edef
MS
97static int check_profiles(ppd_file_t *ppd, const char *root, int errors,
98 int verbose, int warn);
1340db2d
MS
99static int check_sizes(ppd_file_t *ppd, int errors, int verbose, int warn);
100static int check_translations(ppd_file_t *ppd, int errors, int verbose,
3d8365b8 101 int warn);
22c9029b 102static void show_conflicts(ppd_file_t *ppd, const char *prefix);
09a101d6 103static int test_raster(ppd_file_t *ppd, int verbose);
85dda01c 104static void usage(void) __attribute__((noreturn));
1340db2d
MS
105static int valid_path(const char *keyword, const char *path, int errors,
106 int verbose, int warn);
3d8365b8 107static int valid_utf8(const char *s);
ef416fc2 108
b94498cf 109
ef416fc2 110/*
111 * 'main()' - Main entry for test program.
112 */
113
323c5de1 114int /* O - Exit status */
115main(int argc, /* I - Number of command-line args */
116 char *argv[]) /* I - Command-line arguments */
ef416fc2 117{
323c5de1 118 int i, j, k, m, n; /* Looping vars */
7e86f2f6 119 size_t len; /* Length of option name */
323c5de1 120 char *opt; /* Option character */
121 const char *ptr; /* Pointer into string */
82cc1f9a 122 cups_file_t *fp; /* PPD file */
323c5de1 123 int files; /* Number of files */
124 int verbose; /* Want verbose output? */
b94498cf 125 int warn; /* Which errors to just warn about */
b226ab99 126 int ignore; /* Which errors to ignore */
323c5de1 127 int status; /* Exit status */
128 int errors; /* Number of conformance errors */
129 int ppdversion; /* PPD spec version in PPD file */
130 ppd_status_t error; /* Status of ppdOpen*() */
131 int line; /* Line number for error */
a603edef 132 char *root; /* Root directory */
323c5de1 133 int xdpi, /* X resolution */
134 ydpi; /* Y resolution */
135 ppd_file_t *ppd; /* PPD file record */
136 ppd_attr_t *attr; /* PPD attribute */
137 ppd_size_t *size; /* Size record */
138 ppd_group_t *group; /* UI group */
139 ppd_option_t *option; /* Standard UI option */
140 ppd_group_t *group2; /* UI group */
141 ppd_option_t *option2; /* Standard UI option */
142 ppd_choice_t *choice; /* Standard UI option choice */
dd1abb6b 143 struct lconv *loc; /* Locale data */
ef416fc2 144 static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
145 static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
146 "JCL", "PAGE", "PROLOG" };
147
148
07725fee 149 _cupsSetLocale(argv);
dd1abb6b 150 loc = localeconv();
d09495fa 151
ef416fc2 152 /*
153 * Display PPD files for each file listed on the command-line...
154 */
155
156 ppdSetConformance(PPD_CONFORM_STRICT);
157
158 verbose = 0;
159 ppd = NULL;
160 files = 0;
161 status = ERROR_NONE;
323c5de1 162 root = "";
b94498cf 163 warn = WARN_NONE;
b226ab99 164 ignore = WARN_NONE;
ef416fc2 165
166 for (i = 1; i < argc; i ++)
167 if (argv[i][0] == '-' && argv[i][1])
168 {
169 for (opt = argv[i] + 1; *opt; opt ++)
170 switch (*opt)
171 {
b226ab99
MS
172 case 'I' : /* Ignore errors */
173 i ++;
174
175 if (i >= argc)
176 usage();
177
178 if (!strcmp(argv[i], "none"))
179 ignore = WARN_NONE;
ef55b745
MS
180 else if (!strcmp(argv[i], "filename"))
181 ignore |= WARN_FILENAME;
b226ab99
MS
182 else if (!strcmp(argv[i], "filters"))
183 ignore |= WARN_FILTERS;
184 else if (!strcmp(argv[i], "profiles"))
185 ignore |= WARN_PROFILES;
186 else if (!strcmp(argv[i], "all"))
187 ignore = WARN_FILTERS | WARN_PROFILES;
188 else
189 usage();
190 break;
191
323c5de1 192 case 'R' : /* Alternate root directory */
193 i ++;
194
195 if (i >= argc)
196 usage();
197
198 root = argv[i];
199 break;
200
b226ab99 201 case 'W' : /* Turn errors into warnings */
b94498cf 202 i ++;
203
204 if (i >= argc)
205 usage();
206
207 if (!strcmp(argv[i], "none"))
208 warn = WARN_NONE;
209 else if (!strcmp(argv[i], "constraints"))
210 warn |= WARN_CONSTRAINTS;
211 else if (!strcmp(argv[i], "defaults"))
212 warn |= WARN_DEFAULTS;
749b1e90
MS
213 else if (!strcmp(argv[i], "duplex"))
214 warn |= WARN_DUPLEX;
b94498cf 215 else if (!strcmp(argv[i], "filters"))
216 warn |= WARN_FILTERS;
a603edef
MS
217 else if (!strcmp(argv[i], "profiles"))
218 warn |= WARN_PROFILES;
1340db2d
MS
219 else if (!strcmp(argv[i], "sizes"))
220 warn |= WARN_SIZES;
b94498cf 221 else if (!strcmp(argv[i], "translations"))
222 warn |= WARN_TRANSLATIONS;
223 else if (!strcmp(argv[i], "all"))
224 warn = WARN_ALL;
225 else
226 usage();
227 break;
228
ef416fc2 229 case 'q' : /* Quiet mode */
230 if (verbose > 0)
231 {
fa73b229 232 _cupsLangPuts(stderr,
ef416fc2 233 _("cupstestppd: The -q option is incompatible "
0837b7e8 234 "with the -v option."));
ef416fc2 235 return (1);
236 }
237
238 verbose --;
239 break;
240
241 case 'r' : /* Relaxed mode */
242 ppdSetConformance(PPD_CONFORM_RELAXED);
243 break;
244
245 case 'v' : /* Verbose mode */
246 if (verbose < 0)
247 {
fa73b229 248 _cupsLangPuts(stderr,
ef416fc2 249 _("cupstestppd: The -v option is incompatible "
0837b7e8 250 "with the -q option."));
ef416fc2 251 return (1);
252 }
253
254 verbose ++;
255 break;
256
257 default :
258 usage();
259 break;
260 }
261 }
262 else
263 {
264 /*
265 * Open the PPD file...
266 */
267
268 if (files && verbose >= 0)
0837b7e8 269 puts("");
ef416fc2 270
271 files ++;
272
273 if (argv[i][0] == '-')
274 {
275 /*
276 * Read from stdin...
277 */
278
82cc1f9a 279 ppd = _ppdOpen(cupsFileStdin(), _PPD_LOCALIZATION_ALL);
bdd6c45b
MS
280
281 if (verbose >= 0)
282 printf("%s:", (ppd && ppd->pcfilename) ? ppd->pcfilename : "(stdin)");
ef416fc2 283 }
284 else
285 {
286 /*
287 * Read from a file...
288 */
289
290 if (verbose >= 0)
291 printf("%s:", argv[i]);
292
82cc1f9a
MS
293 if ((fp = cupsFileOpen(argv[i], "r")) != NULL)
294 {
295 ppd = _ppdOpen(fp, _PPD_LOCALIZATION_ALL);
296 cupsFileClose(fp);
297 }
298 else
299 {
300 status = ERROR_FILE_OPEN;
301
302 if (verbose >= 0)
303 {
304 _cupsLangPuts(stdout, _(" FAIL"));
305 _cupsLangPrintf(stdout,
f3c17241
MS
306 _(" **FAIL** Unable to open PPD file - %s on "
307 "line %d."), strerror(errno), 0);
82cc1f9a
MS
308 continue;
309 }
310 }
ef416fc2 311 }
312
313 if (ppd == NULL)
314 {
315 error = ppdLastError(&line);
316
317 if (error <= PPD_ALLOC_ERROR)
318 {
319 status = ERROR_FILE_OPEN;
320
321 if (verbose >= 0)
84315f46
MS
322 {
323 _cupsLangPuts(stdout, _(" FAIL"));
fa73b229 324 _cupsLangPrintf(stdout,
f3c17241
MS
325 _(" **FAIL** Unable to open PPD file - %s on "
326 "line %d."), strerror(errno), 0);
84315f46 327 }
ef416fc2 328 }
329 else
330 {
331 status = ERROR_PPD_FORMAT;
332
333 if (verbose >= 0)
84315f46
MS
334 {
335 _cupsLangPuts(stdout, _(" FAIL"));
fa73b229 336 _cupsLangPrintf(stdout,
84315f46 337 _(" **FAIL** Unable to open PPD file - "
0837b7e8 338 "%s on line %d."),
ef416fc2 339 ppdErrorString(error), line);
340
341 switch (error)
342 {
343 case PPD_MISSING_PPDADOBE4 :
fa73b229 344 _cupsLangPuts(stdout,
0837b7e8
MS
345 _(" REF: Page 42, section "
346 "5.2."));
ef416fc2 347 break;
348 case PPD_MISSING_VALUE :
fa73b229 349 _cupsLangPuts(stdout,
0837b7e8
MS
350 _(" REF: Page 20, section "
351 "3.4."));
ef416fc2 352 break;
353 case PPD_BAD_OPEN_GROUP :
354 case PPD_NESTED_OPEN_GROUP :
fa73b229 355 _cupsLangPuts(stdout,
0837b7e8
MS
356 _(" REF: Pages 45-46, section "
357 "5.2."));
ef416fc2 358 break;
359 case PPD_BAD_OPEN_UI :
360 case PPD_NESTED_OPEN_UI :
fa73b229 361 _cupsLangPuts(stdout,
0837b7e8
MS
362 _(" REF: Pages 42-45, section "
363 "5.2."));
ef416fc2 364 break;
365 case PPD_BAD_ORDER_DEPENDENCY :
fa73b229 366 _cupsLangPuts(stdout,
0837b7e8
MS
367 _(" REF: Pages 48-49, section "
368 "5.2."));
ef416fc2 369 break;
370 case PPD_BAD_UI_CONSTRAINTS :
fa73b229 371 _cupsLangPuts(stdout,
0837b7e8
MS
372 _(" REF: Pages 52-54, section "
373 "5.2."));
ef416fc2 374 break;
375 case PPD_MISSING_ASTERISK :
fa73b229 376 _cupsLangPuts(stdout,
0837b7e8
MS
377 _(" REF: Page 15, section "
378 "3.2."));
ef416fc2 379 break;
380 case PPD_LINE_TOO_LONG :
fa73b229 381 _cupsLangPuts(stdout,
0837b7e8
MS
382 _(" REF: Page 15, section "
383 "3.1."));
ef416fc2 384 break;
385 case PPD_ILLEGAL_CHARACTER :
fa73b229 386 _cupsLangPuts(stdout,
0837b7e8
MS
387 _(" REF: Page 15, section "
388 "3.1."));
ef416fc2 389 break;
390 case PPD_ILLEGAL_MAIN_KEYWORD :
fa73b229 391 _cupsLangPuts(stdout,
0837b7e8
MS
392 _(" REF: Pages 16-17, section "
393 "3.2."));
ef416fc2 394 break;
395 case PPD_ILLEGAL_OPTION_KEYWORD :
fa73b229 396 _cupsLangPuts(stdout,
0837b7e8
MS
397 _(" REF: Page 19, section "
398 "3.3."));
ef416fc2 399 break;
400 case PPD_ILLEGAL_TRANSLATION :
fa73b229 401 _cupsLangPuts(stdout,
0837b7e8
MS
402 _(" REF: Page 27, section "
403 "3.5."));
ef416fc2 404 break;
405 default :
406 break;
407 }
fa73b229 408
409 check_basics(argv[i]);
ef416fc2 410 }
411 }
412
413 continue;
414 }
415
416 /*
417 * Show the header and then perform basic conformance tests (limited
418 * only by what the CUPS PPD functions actually load...)
419 */
420
421 errors = 0;
422 ppdversion = 43;
423
424 if (verbose > 0)
fa73b229 425 _cupsLangPuts(stdout,
0837b7e8 426 _("\n DETAILED CONFORMANCE TEST RESULTS"));
ef416fc2 427
428 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
429 attr->value)
dd1abb6b 430 ppdversion = (int)(10 * _cupsStrScand(attr->value, NULL, loc) + 0.5);
ef416fc2 431
a4845881
MS
432 if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
433 {
434 do
435 {
436 if (strstr(attr->value, "application/vnd.cups-raster"))
437 {
438 if (!test_raster(ppd, verbose))
439 errors ++;
440 break;
441 }
09a101d6 442 }
a4845881
MS
443 while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
444 }
445 else
446 {
447 for (j = 0; j < ppd->num_filters; j ++)
448 if (strstr(ppd->filters[j], "application/vnd.cups-raster"))
449 {
450 if (!test_raster(ppd, verbose))
451 errors ++;
452 break;
453 }
454 }
09a101d6 455
b423cd4c 456 /*
457 * Look for default keywords with no matching option...
458 */
459
b94498cf 460 if (!(warn & WARN_DEFAULTS))
461 errors = check_defaults(ppd, errors, verbose, 0);
ef416fc2 462
463 if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
464 {
465 if (verbose >= 0)
466 {
467 if (!errors && !verbose)
0837b7e8 468 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 469
fa73b229 470 _cupsLangPuts(stdout,
b94498cf 471 _(" **FAIL** REQUIRED DefaultImageableArea\n"
0837b7e8 472 " REF: Page 102, section 5.15."));
b94498cf 473 }
ef416fc2 474
475 errors ++;
476 }
477 else if (ppdPageSize(ppd, attr->value) == NULL &&
b94498cf 478 strcmp(attr->value, "Unknown"))
ef416fc2 479 {
480 if (verbose >= 0)
481 {
482 if (!errors && !verbose)
0837b7e8 483 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 484
fa73b229 485 _cupsLangPrintf(stdout,
f3c17241 486 _(" **FAIL** Bad DefaultImageableArea %s\n"
0837b7e8 487 " REF: Page 102, section 5.15."),
ef416fc2 488 attr->value);
b94498cf 489 }
ef416fc2 490
491 errors ++;
492 }
493 else
494 {
495 if (verbose > 0)
0837b7e8 496 _cupsLangPuts(stdout, _(" PASS DefaultImageableArea"));
ef416fc2 497 }
498
499 if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
500 {
501 if (verbose >= 0)
502 {
503 if (!errors && !verbose)
0837b7e8 504 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 505
fa73b229 506 _cupsLangPuts(stdout,
b94498cf 507 _(" **FAIL** REQUIRED DefaultPaperDimension\n"
0837b7e8 508 " REF: Page 103, section 5.15."));
b94498cf 509 }
ef416fc2 510
511 errors ++;
512 }
513 else if (ppdPageSize(ppd, attr->value) == NULL &&
b94498cf 514 strcmp(attr->value, "Unknown"))
ef416fc2 515 {
516 if (verbose >= 0)
517 {
518 if (!errors && !verbose)
0837b7e8 519 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 520
fa73b229 521 _cupsLangPrintf(stdout,
f3c17241 522 _(" **FAIL** Bad DefaultPaperDimension %s\n"
0837b7e8 523 " REF: Page 103, section 5.15."),
ef416fc2 524 attr->value);
b94498cf 525 }
ef416fc2 526
527 errors ++;
528 }
529 else if (verbose > 0)
0837b7e8 530 _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension"));
ef416fc2 531
532 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
0837b7e8
MS
533 for (k = 0, option = group->options;
534 k < group->num_options;
535 k ++, option ++)
ef416fc2 536 {
537 /*
538 * Verify that we have a default choice...
539 */
540
541 if (option->defchoice[0])
542 {
b94498cf 543 if (ppdFindChoice(option, option->defchoice) == NULL &&
544 strcmp(option->defchoice, "Unknown"))
ef416fc2 545 {
546 if (verbose >= 0)
547 {
548 if (!errors && !verbose)
0837b7e8 549 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 550
fa73b229 551 _cupsLangPrintf(stdout,
f3c17241 552 _(" **FAIL** Bad Default%s %s\n"
0837b7e8 553 " REF: Page 40, section 4.5."),
ef416fc2 554 option->keyword, option->defchoice);
b94498cf 555 }
ef416fc2 556
557 errors ++;
558 }
559 else if (verbose > 0)
fa73b229 560 _cupsLangPrintf(stdout,
0837b7e8 561 _(" PASS Default%s"),
ef416fc2 562 option->keyword);
563 }
564 else
565 {
566 if (verbose >= 0)
567 {
568 if (!errors && !verbose)
0837b7e8 569 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 570
fa73b229 571 _cupsLangPrintf(stdout,
b94498cf 572 _(" **FAIL** REQUIRED Default%s\n"
0837b7e8 573 " REF: Page 40, section 4.5."),
b94498cf 574 option->keyword);
575 }
ef416fc2 576
577 errors ++;
578 }
579 }
580
557dde9f 581 if ((attr = ppdFindAttr(ppd, "FileVersion", NULL)) != NULL)
ef416fc2 582 {
557dde9f
MS
583 for (ptr = attr->value; *ptr; ptr ++)
584 if (!isdigit(*ptr & 255) && *ptr != '.')
585 break;
586
587 if (*ptr)
588 {
589 if (verbose >= 0)
590 {
591 if (!errors && !verbose)
0837b7e8 592 _cupsLangPuts(stdout, _(" FAIL"));
557dde9f
MS
593
594 _cupsLangPrintf(stdout,
595 _(" **FAIL** Bad FileVersion \"%s\"\n"
0837b7e8 596 " REF: Page 56, section 5.3."),
557dde9f
MS
597 attr->value);
598 }
599
600 errors ++;
601 }
602 else if (verbose > 0)
0837b7e8 603 _cupsLangPuts(stdout, _(" PASS FileVersion"));
ef416fc2 604 }
605 else
606 {
607 if (verbose >= 0)
608 {
609 if (!errors && !verbose)
0837b7e8 610 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 611
fa73b229 612 _cupsLangPuts(stdout,
ef416fc2 613 _(" **FAIL** REQUIRED FileVersion\n"
0837b7e8 614 " REF: Page 56, section 5.3."));
ef416fc2 615 }
616
617 errors ++;
618 }
619
557dde9f 620 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL)
ef416fc2 621 {
557dde9f
MS
622 ptr = attr->value;
623 if (*ptr == '4' && ptr[1] == '.')
624 {
88f9aafc 625
557dde9f
MS
626 for (ptr += 2; *ptr; ptr ++)
627 if (!isdigit(*ptr & 255))
628 break;
629 }
630
631 if (*ptr)
632 {
633 if (verbose >= 0)
634 {
635 if (!errors && !verbose)
0837b7e8 636 _cupsLangPuts(stdout, _(" FAIL"));
557dde9f
MS
637
638 _cupsLangPrintf(stdout,
639 _(" **FAIL** Bad FormatVersion \"%s\"\n"
0837b7e8 640 " REF: Page 56, section 5.3."),
557dde9f
MS
641 attr->value);
642 }
643
644 errors ++;
645 }
646 else if (verbose > 0)
0837b7e8 647 _cupsLangPuts(stdout, _(" PASS FormatVersion"));
ef416fc2 648 }
649 else
650 {
651 if (verbose >= 0)
652 {
653 if (!errors && !verbose)
0837b7e8 654 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 655
fa73b229 656 _cupsLangPuts(stdout,
ef416fc2 657 _(" **FAIL** REQUIRED FormatVersion\n"
0837b7e8 658 " REF: Page 56, section 5.3."));
ef416fc2 659 }
660
661 errors ++;
662 }
663
664 if (ppd->lang_encoding != NULL)
665 {
666 if (verbose > 0)
0837b7e8 667 _cupsLangPuts(stdout, _(" PASS LanguageEncoding"));
ef416fc2 668 }
669 else if (ppdversion > 40)
670 {
671 if (verbose >= 0)
672 {
673 if (!errors && !verbose)
0837b7e8 674 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 675
fa73b229 676 _cupsLangPuts(stdout,
ef416fc2 677 _(" **FAIL** REQUIRED LanguageEncoding\n"
0837b7e8 678 " REF: Pages 56-57, section 5.3."));
ef416fc2 679 }
680
681 errors ++;
682 }
683
684 if (ppd->lang_version != NULL)
685 {
686 if (verbose > 0)
0837b7e8 687 _cupsLangPuts(stdout, _(" PASS LanguageVersion"));
ef416fc2 688 }
689 else
690 {
691 if (verbose >= 0)
692 {
693 if (!errors && !verbose)
0837b7e8 694 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 695
fa73b229 696 _cupsLangPuts(stdout,
ef416fc2 697 _(" **FAIL** REQUIRED LanguageVersion\n"
0837b7e8 698 " REF: Pages 57-58, section 5.3."));
ef416fc2 699 }
700
701 errors ++;
702 }
703
704 if (ppd->manufacturer != NULL)
705 {
88f9aafc
MS
706 if (!_cups_strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) ||
707 !_cups_strncasecmp(ppd->manufacturer, "Hewlett Packard", 15))
ef416fc2 708 {
709 if (verbose >= 0)
710 {
711 if (!errors && !verbose)
0837b7e8 712 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 713
f3c17241
MS
714 _cupsLangPrintf(stdout,
715 _(" **FAIL** Bad Manufacturer (should be "
716 "\"%s\")\n"
717 " REF: Page 211, table D.1."),
718 "HP");
ef416fc2 719 }
720
721 errors ++;
722 }
88f9aafc
MS
723 else if (!_cups_strncasecmp(ppd->manufacturer, "OkiData", 7) ||
724 !_cups_strncasecmp(ppd->manufacturer, "Oki Data", 8))
b94498cf 725 {
726 if (verbose >= 0)
727 {
728 if (!errors && !verbose)
0837b7e8 729 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 730
f3c17241
MS
731 _cupsLangPrintf(stdout,
732 _(" **FAIL** Bad Manufacturer (should be "
733 "\"%s\")\n"
734 " REF: Page 211, table D.1."),
735 "Oki");
b94498cf 736 }
737
738 errors ++;
739 }
ef416fc2 740 else if (verbose > 0)
0837b7e8 741 _cupsLangPuts(stdout, _(" PASS Manufacturer"));
ef416fc2 742 }
743 else if (ppdversion >= 43)
744 {
745 if (verbose >= 0)
746 {
747 if (!errors && !verbose)
0837b7e8 748 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 749
fa73b229 750 _cupsLangPuts(stdout,
ef416fc2 751 _(" **FAIL** REQUIRED Manufacturer\n"
0837b7e8 752 " REF: Pages 58-59, section 5.3."));
ef416fc2 753 }
754
755 errors ++;
756 }
757
758 if (ppd->modelname != NULL)
759 {
760 for (ptr = ppd->modelname; *ptr; ptr ++)
761 if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr))
762 break;
763
764 if (*ptr)
765 {
766 if (verbose >= 0)
767 {
768 if (!errors && !verbose)
0837b7e8 769 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 770
fa73b229 771 _cupsLangPrintf(stdout,
f3c17241 772 _(" **FAIL** Bad ModelName - \"%c\" not "
ef416fc2 773 "allowed in string.\n"
0837b7e8 774 " REF: Pages 59-60, section 5.3."),
ef416fc2 775 *ptr);
776 }
777
778 errors ++;
779 }
780 else if (verbose > 0)
0837b7e8 781 _cupsLangPuts(stdout, _(" PASS ModelName"));
ef416fc2 782 }
783 else
784 {
785 if (verbose >= 0)
786 {
787 if (!errors && !verbose)
0837b7e8 788 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 789
fa73b229 790 _cupsLangPuts(stdout,
ef416fc2 791 _(" **FAIL** REQUIRED ModelName\n"
0837b7e8 792 " REF: Pages 59-60, section 5.3."));
ef416fc2 793 }
794
795 errors ++;
796 }
797
798 if (ppd->nickname != NULL)
799 {
800 if (verbose > 0)
0837b7e8 801 _cupsLangPuts(stdout, _(" PASS NickName"));
ef416fc2 802 }
803 else
804 {
805 if (verbose >= 0)
806 {
807 if (!errors && !verbose)
0837b7e8 808 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 809
fa73b229 810 _cupsLangPuts(stdout,
ef416fc2 811 _(" **FAIL** REQUIRED NickName\n"
0837b7e8 812 " REF: Page 60, section 5.3."));
ef416fc2 813 }
814
815 errors ++;
816 }
817
818 if (ppdFindOption(ppd, "PageSize") != NULL)
819 {
820 if (verbose > 0)
0837b7e8 821 _cupsLangPuts(stdout, _(" PASS PageSize"));
ef416fc2 822 }
823 else
824 {
825 if (verbose >= 0)
826 {
827 if (!errors && !verbose)
0837b7e8 828 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 829
fa73b229 830 _cupsLangPuts(stdout,
ef416fc2 831 _(" **FAIL** REQUIRED PageSize\n"
0837b7e8 832 " REF: Pages 99-100, section 5.14."));
ef416fc2 833 }
834
835 errors ++;
836 }
837
838 if (ppdFindOption(ppd, "PageRegion") != NULL)
839 {
840 if (verbose > 0)
0837b7e8 841 _cupsLangPuts(stdout, _(" PASS PageRegion"));
ef416fc2 842 }
843 else
844 {
845 if (verbose >= 0)
846 {
847 if (!errors && !verbose)
0837b7e8 848 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 849
fa73b229 850 _cupsLangPuts(stdout,
ef416fc2 851 _(" **FAIL** REQUIRED PageRegion\n"
0837b7e8 852 " REF: Page 100, section 5.14."));
ef416fc2 853 }
854
855 errors ++;
856 }
857
858 if (ppd->pcfilename != NULL)
859 {
860 if (verbose > 0)
0837b7e8 861 _cupsLangPuts(stdout, _(" PASS PCFileName"));
ef416fc2 862 }
ef55b745 863 else if (!(ignore & WARN_FILENAME))
ef416fc2 864 {
865 if (verbose >= 0)
866 {
867 if (!errors && !verbose)
0837b7e8 868 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 869
fa73b229 870 _cupsLangPuts(stdout,
ef416fc2 871 _(" **FAIL** REQUIRED PCFileName\n"
0837b7e8 872 " REF: Pages 61-62, section 5.3."));
ef416fc2 873 }
874
875 errors ++;
876 }
877
878 if (ppd->product != NULL)
879 {
880 if (ppd->product[0] != '(' ||
881 ppd->product[strlen(ppd->product) - 1] != ')')
882 {
883 if (verbose >= 0)
884 {
885 if (!errors && !verbose)
0837b7e8 886 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 887
fa73b229 888 _cupsLangPuts(stdout,
f3c17241 889 _(" **FAIL** Bad Product - not \"(string)\".\n"
0837b7e8 890 " REF: Page 62, section 5.3."));
ef416fc2 891 }
892
893 errors ++;
894 }
895 else if (verbose > 0)
0837b7e8 896 _cupsLangPuts(stdout, _(" PASS Product"));
ef416fc2 897 }
898 else
899 {
900 if (verbose >= 0)
901 {
902 if (!errors && !verbose)
0837b7e8 903 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 904
fa73b229 905 _cupsLangPuts(stdout,
ef416fc2 906 _(" **FAIL** REQUIRED Product\n"
0837b7e8 907 " REF: Page 62, section 5.3."));
ef416fc2 908 }
909
910 errors ++;
911 }
912
913 if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
914 attr->value != NULL)
915 {
916 char junkstr[255]; /* Temp string */
917 int junkint; /* Temp integer */
918
919
95633d62 920 if (sscanf(attr->value, "(%254[^)\n])%d", junkstr, &junkint) != 2)
ef416fc2 921 {
922 if (verbose >= 0)
923 {
924 if (!errors && !verbose)
0837b7e8 925 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 926
fa73b229 927 _cupsLangPuts(stdout,
f3c17241 928 _(" **FAIL** Bad PSVersion - not \"(string) "
ef416fc2 929 "int\".\n"
0837b7e8 930 " REF: Pages 62-64, section 5.3."));
ef416fc2 931 }
932
933 errors ++;
934 }
935 else if (verbose > 0)
0837b7e8 936 _cupsLangPuts(stdout, _(" PASS PSVersion"));
ef416fc2 937 }
938 else
939 {
940 if (verbose >= 0)
941 {
942 if (!errors && !verbose)
0837b7e8 943 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 944
fa73b229 945 _cupsLangPuts(stdout,
ef416fc2 946 _(" **FAIL** REQUIRED PSVersion\n"
0837b7e8 947 " REF: Pages 62-64, section 5.3."));
ef416fc2 948 }
949
950 errors ++;
951 }
952
953 if (ppd->shortnickname != NULL)
954 {
955 if (strlen(ppd->shortnickname) > 31)
956 {
957 if (verbose >= 0)
958 {
959 if (!errors && !verbose)
0837b7e8 960 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 961
fa73b229 962 _cupsLangPuts(stdout,
f3c17241 963 _(" **FAIL** Bad ShortNickName - longer "
ef416fc2 964 "than 31 chars.\n"
0837b7e8 965 " REF: Pages 64-65, section 5.3."));
ef416fc2 966 }
967
968 errors ++;
969 }
970 else if (verbose > 0)
0837b7e8 971 _cupsLangPuts(stdout, _(" PASS ShortNickName"));
ef416fc2 972 }
973 else if (ppdversion >= 43)
974 {
975 if (verbose >= 0)
976 {
977 if (!errors && !verbose)
0837b7e8 978 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 979
fa73b229 980 _cupsLangPuts(stdout,
ef416fc2 981 _(" **FAIL** REQUIRED ShortNickName\n"
0837b7e8 982 " REF: Page 64-65, section 5.3."));
ef416fc2 983 }
984
985 errors ++;
986 }
987
988 if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
989 strstr(ppd->patches, "*End"))
990 {
991 if (verbose >= 0)
992 {
993 if (!errors && !verbose)
0837b7e8 994 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 995
fa73b229 996 _cupsLangPuts(stdout,
f3c17241 997 _(" **FAIL** Bad JobPatchFile attribute in file\n"
0837b7e8 998 " REF: Page 24, section 3.4."));
ef416fc2 999 }
1000
1001 errors ++;
1002 }
1003
1004 /*
1005 * Check for page sizes without the corresponding ImageableArea or
1006 * PaperDimension values...
1007 */
1008
1009 if (ppd->num_sizes == 0)
1010 {
1011 if (verbose >= 0)
1012 {
1013 if (!errors && !verbose)
0837b7e8 1014 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 1015
fa73b229 1016 _cupsLangPuts(stdout,
ef416fc2 1017 _(" **FAIL** REQUIRED PageSize\n"
1018 " REF: Page 41, section 5.\n"
0837b7e8 1019 " REF: Page 99, section 5.14."));
ef416fc2 1020 }
1021
1022 errors ++;
1023 }
1024 else
1025 {
1026 for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++)
1027 {
1028 /*
1029 * Don't check custom size...
1030 */
1031
1032 if (!strcmp(size->name, "Custom"))
1033 continue;
1034
1035 /*
1036 * Check for ImageableArea...
1037 */
1038
1039 if (size->left == 0.0 && size->bottom == 0.0 &&
1040 size->right == 0.0 && size->top == 0.0)
1041 {
1042 if (verbose >= 0)
1043 {
1044 if (!errors && !verbose)
0837b7e8 1045 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 1046
fa73b229 1047 _cupsLangPrintf(stdout,
ef416fc2 1048 _(" **FAIL** REQUIRED ImageableArea for "
1049 "PageSize %s\n"
1050 " REF: Page 41, section 5.\n"
0837b7e8 1051 " REF: Page 102, section 5.15."),
ef416fc2 1052 size->name);
1053 }
1054
1055 errors ++;
1056 }
1057
1058 /*
1059 * Check for PaperDimension...
1060 */
1061
82cc1f9a 1062 if (size->width <= 0.0 && size->length <= 0.0)
ef416fc2 1063 {
1064 if (verbose >= 0)
1065 {
1066 if (!errors && !verbose)
0837b7e8 1067 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 1068
fa73b229 1069 _cupsLangPrintf(stdout,
ef416fc2 1070 _(" **FAIL** REQUIRED PaperDimension "
1071 "for PageSize %s\n"
1072 " REF: Page 41, section 5.\n"
0837b7e8 1073 " REF: Page 103, section 5.15."),
ef416fc2 1074 size->name);
1075 }
1076
1077 errors ++;
1078 }
1079 }
1080 }
1081
1082 /*
1083 * Check for valid Resolution, JCLResolution, or SetResolution values...
1084 */
1085
1086 if ((option = ppdFindOption(ppd, "Resolution")) == NULL)
1087 if ((option = ppdFindOption(ppd, "JCLResolution")) == NULL)
1088 option = ppdFindOption(ppd, "SetResolution");
1089
1090 if (option != NULL)
1091 {
0837b7e8
MS
1092 for (j = option->num_choices, choice = option->choices;
1093 j > 0;
1094 j --, choice ++)
ef416fc2 1095 {
1096 /*
1097 * Verify that all resolution options are of the form NNNdpi
1098 * or NNNxNNNdpi...
1099 */
1100
1101 xdpi = strtol(choice->choice, (char **)&ptr, 10);
1102 if (ptr > choice->choice && xdpi > 0)
1103 {
1104 if (*ptr == 'x')
1105 ydpi = strtol(ptr + 1, (char **)&ptr, 10);
1106 else
1107 ydpi = xdpi;
1108 }
1109 else
1110 ydpi = xdpi;
1111
db1f069b
MS
1112 if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 ||
1113 strcmp(ptr, "dpi"))
ef416fc2 1114 {
1115 if (verbose >= 0)
1116 {
1117 if (!errors && !verbose)
0837b7e8 1118 _cupsLangPuts(stdout, _(" FAIL"));
ef416fc2 1119
fa73b229 1120 _cupsLangPrintf(stdout,
f3c17241 1121 _(" **FAIL** Bad option %s choice %s\n"
0837b7e8 1122 " REF: Page 84, section 5.9"),
ef416fc2 1123 option->keyword, choice->choice);
1124 }
1125
1126 errors ++;
1127 }
1128 }
1129 }
1130
b94498cf 1131 if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
1132 strcmp(attr->name, "1284DeviceID"))
b423cd4c 1133 {
b94498cf 1134 if (verbose >= 0)
1135 {
1136 if (!errors && !verbose)
0837b7e8 1137 _cupsLangPuts(stdout, _(" FAIL"));
b423cd4c 1138
b94498cf 1139 _cupsLangPrintf(stdout,
4d301e69 1140 _(" **FAIL** %s must be 1284DeviceID\n"
0837b7e8 1141 " REF: Page 72, section 5.5"),
b94498cf 1142 attr->name);
1143 }
b423cd4c 1144
b94498cf 1145 errors ++;
1146 }
b423cd4c 1147
749b1e90
MS
1148 errors = check_case(ppd, errors, verbose);
1149
b94498cf 1150 if (!(warn & WARN_CONSTRAINTS))
1151 errors = check_constraints(ppd, errors, verbose, 0);
c0e1af83 1152
b226ab99 1153 if (!(warn & WARN_FILTERS) && !(ignore & WARN_FILTERS))
b94498cf 1154 errors = check_filters(ppd, root, errors, verbose, 0);
b423cd4c 1155
b226ab99 1156 if (!(warn & WARN_PROFILES) && !(ignore & WARN_PROFILES))
a603edef
MS
1157 errors = check_profiles(ppd, root, errors, verbose, 0);
1158
1340db2d
MS
1159 if (!(warn & WARN_SIZES))
1160 errors = check_sizes(ppd, errors, verbose, 0);
1161
b94498cf 1162 if (!(warn & WARN_TRANSLATIONS))
1163 errors = check_translations(ppd, errors, verbose, 0);
b423cd4c 1164
749b1e90
MS
1165 if (!(warn & WARN_DUPLEX))
1166 errors = check_duplex(ppd, errors, verbose, 0);
1167
b94498cf 1168 if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
1169 attr->value)
1170 {
1171 /*
1172 * This file contains localizations, check for conformance of the
1173 * base translation...
1174 */
b423cd4c 1175
b94498cf 1176 if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL)
1177 {
1178 if (!attr->value || strcmp(attr->value, "ISOLatin1"))
1179 {
1180 if (!errors && !verbose)
0837b7e8 1181 _cupsLangPuts(stdout, _(" FAIL"));
b423cd4c 1182
b94498cf 1183 if (verbose >= 0)
bc44d920 1184 _cupsLangPrintf(stdout,
b94498cf 1185 _(" **FAIL** Bad LanguageEncoding %s - "
0837b7e8 1186 "must be ISOLatin1."),
b94498cf 1187 attr->value ? attr->value : "(null)");
b423cd4c 1188
b94498cf 1189 errors ++;
1190 }
b423cd4c 1191
b94498cf 1192 if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
b423cd4c 1193 {
b94498cf 1194 if (!errors && !verbose)
0837b7e8 1195 _cupsLangPuts(stdout, _(" FAIL"));
b423cd4c 1196
b94498cf 1197 if (verbose >= 0)
bc44d920 1198 _cupsLangPrintf(stdout,
b94498cf 1199 _(" **FAIL** Bad LanguageVersion %s - "
0837b7e8 1200 "must be English."),
b94498cf 1201 ppd->lang_version ? ppd->lang_version : "(null)");
b423cd4c 1202
b94498cf 1203 errors ++;
b423cd4c 1204 }
88f9aafc 1205
b94498cf 1206 /*
b423cd4c 1207 * Loop through all options and choices...
1208 */
1209
b94498cf 1210 for (option = ppdFirstOption(ppd);
b423cd4c 1211 option;
1212 option = ppdNextOption(ppd))
1213 {
b94498cf 1214 /*
1215 * Check for special characters outside A0 to BF, F7, or F8
1216 * that are used for languages other than English.
1217 */
b423cd4c 1218
b94498cf 1219 for (ptr = option->text; *ptr; ptr ++)
1220 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1221 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1222 break;
b423cd4c 1223
b94498cf 1224 if (*ptr)
f7deaa1a 1225 {
b94498cf 1226 if (!errors && !verbose)
0837b7e8 1227 _cupsLangPuts(stdout, _(" FAIL"));
b423cd4c 1228
b423cd4c 1229 if (verbose >= 0)
b423cd4c 1230 _cupsLangPrintf(stdout,
b94498cf 1231 _(" **FAIL** Default translation "
b423cd4c 1232 "string for option %s contains 8-bit "
0837b7e8 1233 "characters."),
b423cd4c 1234 option->keyword);
b423cd4c 1235
1236 errors ++;
1237 }
1238
b94498cf 1239 for (j = 0; j < option->num_choices; j ++)
b423cd4c 1240 {
b94498cf 1241 /*
1242 * Check for special characters outside A0 to BF, F7, or F8
1243 * that are used for languages other than English.
1244 */
d09495fa 1245
b94498cf 1246 for (ptr = option->choices[j].text; *ptr; ptr ++)
1247 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1248 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1249 break;
b423cd4c 1250
b94498cf 1251 if (*ptr)
f7deaa1a 1252 {
b94498cf 1253 if (!errors && !verbose)
0837b7e8 1254 _cupsLangPuts(stdout, _(" FAIL"));
b423cd4c 1255
b423cd4c 1256 if (verbose >= 0)
b423cd4c 1257 _cupsLangPrintf(stdout,
b94498cf 1258 _(" **FAIL** Default translation "
b423cd4c 1259 "string for option %s choice %s contains "
0837b7e8 1260 "8-bit characters."),
b423cd4c 1261 option->keyword,
1262 option->choices[j].choice);
b423cd4c 1263
1264 errors ++;
1265 }
b94498cf 1266 }
b423cd4c 1267 }
b94498cf 1268 }
1269 }
c0e1af83 1270
b94498cf 1271 /*
1272 * Final pass/fail notification...
1273 */
c0e1af83 1274
b94498cf 1275 if (errors)
1276 status = ERROR_CONFORMANCE;
1277 else if (!verbose)
0837b7e8 1278 _cupsLangPuts(stdout, _(" PASS"));
c0e1af83 1279
b94498cf 1280 if (verbose >= 0)
1281 {
1282 check_basics(argv[i]);
c0e1af83 1283
b94498cf 1284 if (warn & WARN_DEFAULTS)
1285 errors = check_defaults(ppd, errors, verbose, 1);
c0e1af83 1286
b226ab99
MS
1287 if (warn & WARN_CONSTRAINTS)
1288 errors = check_constraints(ppd, errors, verbose, 1);
a603edef 1289
b226ab99 1290 if ((warn & WARN_FILTERS) && !(ignore & WARN_FILTERS))
b94498cf 1291 errors = check_filters(ppd, root, errors, verbose, 1);
c0e1af83 1292
b226ab99
MS
1293 if ((warn & WARN_PROFILES) && !(ignore & WARN_PROFILES))
1294 errors = check_profiles(ppd, root, errors, verbose, 1);
1295
1340db2d
MS
1296 if (warn & WARN_SIZES)
1297 errors = check_sizes(ppd, errors, verbose, 1);
1298 else
1299 errors = check_sizes(ppd, errors, verbose, 2);
1300
b94498cf 1301 if (warn & WARN_TRANSLATIONS)
1302 errors = check_translations(ppd, errors, verbose, 1);
c0e1af83 1303
749b1e90
MS
1304 if (warn & WARN_DUPLEX)
1305 errors = check_duplex(ppd, errors, verbose, 1);
1306
1307 /*
1308 * Look for legacy duplex keywords...
1309 */
1310
1311 if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1312 if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
1313 option = ppdFindOption(ppd, "KD03Duplex");
1314
1315 if (option)
1316 _cupsLangPrintf(stdout,
1317 _(" WARN Duplex option keyword %s may not "
0837b7e8
MS
1318 "work as expected and should be named Duplex.\n"
1319 " REF: Page 122, section 5.17"),
749b1e90
MS
1320 option->keyword);
1321
c0e1af83 1322 /*
b94498cf 1323 * Look for default keywords with no corresponding option...
c0e1af83 1324 */
1325
b94498cf 1326 for (j = 0; j < ppd->num_attrs; j ++)
1327 {
1328 attr = ppd->attrs[j];
323c5de1 1329
b94498cf 1330 if (!strcmp(attr->name, "DefaultColorSpace") ||
1331 !strcmp(attr->name, "DefaultColorSep") ||
1332 !strcmp(attr->name, "DefaultFont") ||
09a101d6 1333 !strcmp(attr->name, "DefaultHalftoneType") ||
b94498cf 1334 !strcmp(attr->name, "DefaultImageableArea") ||
09a101d6 1335 !strcmp(attr->name, "DefaultLeadingEdge") ||
b94498cf 1336 !strcmp(attr->name, "DefaultOutputOrder") ||
1337 !strcmp(attr->name, "DefaultPaperDimension") ||
09a101d6 1338 !strcmp(attr->name, "DefaultResolution") ||
b94498cf 1339 !strcmp(attr->name, "DefaultScreenProc") ||
1340 !strcmp(attr->name, "DefaultTransfer"))
1341 continue;
b423cd4c 1342
88f9aafc 1343 if (!strncmp(attr->name, "Default", 7) &&
b94498cf 1344 !ppdFindOption(ppd, attr->name + 7))
1345 _cupsLangPrintf(stdout,
1346 _(" WARN %s has no corresponding "
0837b7e8 1347 "options."),
b94498cf 1348 attr->name);
323c5de1 1349 }
323c5de1 1350
ef416fc2 1351 if (ppdversion < 43)
1352 {
fa73b229 1353 _cupsLangPrintf(stdout,
0837b7e8
MS
1354 _(" WARN Obsolete PPD version %.1f.\n"
1355 " REF: Page 42, section 5.2."),
ef416fc2 1356 0.1f * ppdversion);
1357 }
1358
1359 if (!ppd->lang_encoding && ppdversion < 41)
1360 {
fa73b229 1361 _cupsLangPuts(stdout,
ef416fc2 1362 _(" WARN LanguageEncoding required by PPD "
1363 "4.3 spec.\n"
0837b7e8 1364 " REF: Pages 56-57, section 5.3."));
ef416fc2 1365 }
1366
1367 if (!ppd->manufacturer && ppdversion < 43)
1368 {
fa73b229 1369 _cupsLangPuts(stdout,
ef416fc2 1370 _(" WARN Manufacturer required by PPD "
1371 "4.3 spec.\n"
0837b7e8 1372 " REF: Pages 58-59, section 5.3."));
ef416fc2 1373 }
1374
1375 /*
1376 * Treat a PCFileName attribute longer than 12 characters as
1377 * a warning and not a hard error...
1378 */
1379
ef55b745
MS
1380 if (!(ignore & WARN_FILENAME) && ppd->pcfilename)
1381 {
1382 if (strlen(ppd->pcfilename) > 12)
1383 {
1384 _cupsLangPuts(stdout,
1385 _(" WARN PCFileName longer than 8.3 in "
1386 "violation of PPD spec.\n"
1387 " REF: Pages 61-62, section "
0837b7e8 1388 "5.3."));
ef55b745
MS
1389 }
1390
88f9aafc 1391 if (!_cups_strcasecmp(ppd->pcfilename, "unused.ppd"))
ef55b745
MS
1392 _cupsLangPuts(stdout,
1393 _(" WARN PCFileName should contain a "
1394 "unique filename.\n"
1395 " REF: Pages 61-62, section "
0837b7e8 1396 "5.3."));
ef416fc2 1397 }
1398
1399 if (!ppd->shortnickname && ppdversion < 43)
1400 {
fa73b229 1401 _cupsLangPuts(stdout,
ef416fc2 1402 _(" WARN ShortNickName required by PPD "
1403 "4.3 spec.\n"
0837b7e8 1404 " REF: Pages 64-65, section 5.3."));
ef416fc2 1405 }
1406
1407 /*
1408 * Check the Protocols line and flag PJL + BCP since TBCP is
1409 * usually used with PJL...
1410 */
1411
1412 if (ppd->protocols)
1413 {
1414 if (strstr(ppd->protocols, "PJL") &&
1415 strstr(ppd->protocols, "BCP") &&
1416 !strstr(ppd->protocols, "TBCP"))
1417 {
fa73b229 1418 _cupsLangPuts(stdout,
ef416fc2 1419 _(" WARN Protocols contains both PJL "
1420 "and BCP; expected TBCP.\n"
0837b7e8 1421 " REF: Pages 78-79, section 5.7."));
ef416fc2 1422 }
1423
1424 if (strstr(ppd->protocols, "PJL") &&
1425 (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
1426 {
fa73b229 1427 _cupsLangPuts(stdout,
ef416fc2 1428 _(" WARN Protocols contains PJL but JCL "
1429 "attributes are not set.\n"
0837b7e8 1430 " REF: Pages 78-79, section 5.7."));
ef416fc2 1431 }
1432 }
1433
1434 /*
1435 * Check for options with a common prefix, e.g. Duplex and Duplexer,
1436 * which are errors according to the spec but won't cause problems
1437 * with CUPS specifically...
1438 */
1439
1440 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
0837b7e8
MS
1441 for (k = 0, option = group->options;
1442 k < group->num_options;
1443 k ++, option ++)
ef416fc2 1444 {
7e86f2f6 1445 len = strlen(option->keyword);
ef416fc2 1446
1447 for (m = 0, group2 = ppd->groups;
1448 m < ppd->num_groups;
1449 m ++, group2 ++)
1450 for (n = 0, option2 = group2->options;
1451 n < group2->num_options;
1452 n ++, option2 ++)
1453 if (option != option2 &&
7e86f2f6 1454 len < strlen(option2->keyword) &&
ef416fc2 1455 !strncmp(option->keyword, option2->keyword, len))
1456 {
fa73b229 1457 _cupsLangPrintf(stdout,
ef416fc2 1458 _(" WARN %s shares a common "
1459 "prefix with %s\n"
1460 " REF: Page 15, section "
0837b7e8 1461 "3.2."),
ef416fc2 1462 option->keyword, option2->keyword);
1463 }
1464 }
1465 }
1466
1467 if (verbose > 0)
1468 {
1469 if (errors)
0837b7e8 1470 _cupsLangPrintf(stdout, _(" %d ERRORS FOUND"), errors);
ef416fc2 1471 else
0837b7e8 1472 _cupsLangPuts(stdout, _(" NO ERRORS FOUND"));
ef416fc2 1473 }
1474
ef416fc2 1475 /*
1476 * Then list the options, if "-v" was provided...
88f9aafc 1477 */
ef416fc2 1478
1479 if (verbose > 1)
1480 {
fa73b229 1481 _cupsLangPrintf(stdout,
ef416fc2 1482 "\n"
1483 " language_level = %d\n"
1484 " color_device = %s\n"
1485 " variable_sizes = %s\n"
0837b7e8 1486 " landscape = %d",
ef416fc2 1487 ppd->language_level,
1488 ppd->color_device ? "TRUE" : "FALSE",
1489 ppd->variable_sizes ? "TRUE" : "FALSE",
1490 ppd->landscape);
1491
1492 switch (ppd->colorspace)
1493 {
1494 case PPD_CS_CMYK :
0837b7e8 1495 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK");
ef416fc2 1496 break;
1497 case PPD_CS_CMY :
0837b7e8 1498 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY");
ef416fc2 1499 break;
1500 case PPD_CS_GRAY :
0837b7e8 1501 _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY");
ef416fc2 1502 break;
1503 case PPD_CS_RGB :
0837b7e8 1504 _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB");
ef416fc2 1505 break;
1506 default :
0837b7e8 1507 _cupsLangPuts(stdout, " colorspace = <unknown>");
ef416fc2 1508 break;
1509 }
1510
0837b7e8 1511 _cupsLangPrintf(stdout, " num_emulations = %d",
ef416fc2 1512 ppd->num_emulations);
1513 for (j = 0; j < ppd->num_emulations; j ++)
0837b7e8 1514 _cupsLangPrintf(stdout, " emulations[%d] = %s",
ef416fc2 1515 j, ppd->emulations[j].name);
1516
0837b7e8 1517 _cupsLangPrintf(stdout, " lang_encoding = %s",
ef416fc2 1518 ppd->lang_encoding);
0837b7e8 1519 _cupsLangPrintf(stdout, " lang_version = %s",
ef416fc2 1520 ppd->lang_version);
0837b7e8
MS
1521 _cupsLangPrintf(stdout, " modelname = %s", ppd->modelname);
1522 _cupsLangPrintf(stdout, " ttrasterizer = %s",
ef416fc2 1523 ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
0837b7e8 1524 _cupsLangPrintf(stdout, " manufacturer = %s",
ef416fc2 1525 ppd->manufacturer);
0837b7e8
MS
1526 _cupsLangPrintf(stdout, " product = %s", ppd->product);
1527 _cupsLangPrintf(stdout, " nickname = %s", ppd->nickname);
1528 _cupsLangPrintf(stdout, " shortnickname = %s",
ef416fc2 1529 ppd->shortnickname);
0837b7e8 1530 _cupsLangPrintf(stdout, " patches = %d bytes",
ef416fc2 1531 ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
1532
0837b7e8 1533 _cupsLangPrintf(stdout, " num_groups = %d", ppd->num_groups);
ef416fc2 1534 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1535 {
0837b7e8 1536 _cupsLangPrintf(stdout, " group[%d] = %s",
ef416fc2 1537 j, group->text);
1538
1539 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1540 {
fa73b229 1541 _cupsLangPrintf(stdout,
ef416fc2 1542 " options[%d] = %s (%s) %s %s %.0f "
0837b7e8 1543 "(%d choices)",
ef416fc2 1544 k, option->keyword, option->text, uis[option->ui],
1545 sections[option->section], option->order,
1546 option->num_choices);
1547
1548 if (!strcmp(option->keyword, "PageSize") ||
1549 !strcmp(option->keyword, "PageRegion"))
1550 {
1551 for (m = option->num_choices, choice = option->choices;
1552 m > 0;
1553 m --, choice ++)
1554 {
1555 size = ppdPageSize(ppd, choice->choice);
1556
1557 if (size == NULL)
fa73b229 1558 _cupsLangPrintf(stdout,
0837b7e8
MS
1559 " %s (%s) = ERROR%s",
1560 choice->choice, choice->text,
1561 !strcmp(option->defchoice, choice->choice)
1562 ? " *" : "");
ef416fc2 1563 else
fa73b229 1564 _cupsLangPrintf(stdout,
ef416fc2 1565 " %s (%s) = %.2fx%.2fin "
0837b7e8 1566 "(%.1f,%.1f,%.1f,%.1f)%s",
ef416fc2 1567 choice->choice, choice->text,
1568 size->width / 72.0, size->length / 72.0,
1569 size->left / 72.0, size->bottom / 72.0,
0837b7e8
MS
1570 size->right / 72.0, size->top / 72.0,
1571 !strcmp(option->defchoice, choice->choice)
1572 ? " *" : "");
ef416fc2 1573 }
1574 }
1575 else
1576 {
1577 for (m = option->num_choices, choice = option->choices;
1578 m > 0;
1579 m --, choice ++)
1580 {
0837b7e8
MS
1581 _cupsLangPrintf(stdout, " %s (%s)%s",
1582 choice->choice, choice->text,
1583 !strcmp(option->defchoice, choice->choice)
1584 ? " *" : "");
ef416fc2 1585 }
1586 }
1587 }
1588 }
1589
0837b7e8 1590 _cupsLangPrintf(stdout, " num_consts = %d",
09a101d6 1591 ppd->num_consts);
1592 for (j = 0; j < ppd->num_consts; j ++)
1593 _cupsLangPrintf(stdout,
0837b7e8 1594 " consts[%d] = *%s %s *%s %s",
09a101d6 1595 j, ppd->consts[j].option1, ppd->consts[j].choice1,
1596 ppd->consts[j].option2, ppd->consts[j].choice2);
1597
0837b7e8 1598 _cupsLangPrintf(stdout, " num_profiles = %d",
ef416fc2 1599 ppd->num_profiles);
1600 for (j = 0; j < ppd->num_profiles; j ++)
fa73b229 1601 _cupsLangPrintf(stdout,
ef416fc2 1602 " profiles[%d] = %s/%s %.3f %.3f "
0837b7e8 1603 "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]",
ef416fc2 1604 j, ppd->profiles[j].resolution,
1605 ppd->profiles[j].media_type,
1606 ppd->profiles[j].gamma, ppd->profiles[j].density,
1607 ppd->profiles[j].matrix[0][0],
1608 ppd->profiles[j].matrix[0][1],
1609 ppd->profiles[j].matrix[0][2],
1610 ppd->profiles[j].matrix[1][0],
1611 ppd->profiles[j].matrix[1][1],
1612 ppd->profiles[j].matrix[1][2],
1613 ppd->profiles[j].matrix[2][0],
1614 ppd->profiles[j].matrix[2][1],
1615 ppd->profiles[j].matrix[2][2]);
1616
0837b7e8 1617 _cupsLangPrintf(stdout, " num_fonts = %d", ppd->num_fonts);
ef416fc2 1618 for (j = 0; j < ppd->num_fonts; j ++)
0837b7e8 1619 _cupsLangPrintf(stdout, " fonts[%d] = %s",
ef416fc2 1620 j, ppd->fonts[j]);
1621
0837b7e8 1622 _cupsLangPrintf(stdout, " num_attrs = %d", ppd->num_attrs);
ef416fc2 1623 for (j = 0; j < ppd->num_attrs; j ++)
fa73b229 1624 _cupsLangPrintf(stdout,
0837b7e8 1625 " attrs[%d] = %s %s%s%s: \"%s\"", j,
ef416fc2 1626 ppd->attrs[j]->name, ppd->attrs[j]->spec,
1627 ppd->attrs[j]->text[0] ? "/" : "",
1628 ppd->attrs[j]->text,
1629 ppd->attrs[j]->value ?
1630 ppd->attrs[j]->value : "(null)");
1631 }
1632
1633 ppdClose(ppd);
1634 }
1635
1636 if (!files)
1637 usage();
1638
1639 return (status);
1640}
1641
1642
fa73b229 1643/*
1644 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1645 */
1646
3d8365b8 1647static void
fa73b229 1648check_basics(const char *filename) /* I - PPD file to check */
1649{
1650 cups_file_t *fp; /* File pointer */
1651 int ch; /* Current character */
1652 int col, /* Current column */
1653 whitespace; /* Only seen whitespace? */
1654 int eol; /* Line endings */
1655 int linenum; /* Line number */
1656 int mixed; /* Mixed line endings? */
1657
1658
1659 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1660 return;
1661
1662 linenum = 1;
1663 col = 0;
1664 eol = EOL_NONE;
1665 mixed = 0;
1666 whitespace = 1;
1667
1668 while ((ch = cupsFileGetChar(fp)) != EOF)
1669 {
1670 if (ch == '\r' || ch == '\n')
1671 {
1672 if (ch == '\n')
1673 {
1674 if (eol == EOL_NONE)
1675 eol = EOL_LF;
1676 else if (eol != EOL_LF)
1677 mixed = 1;
1678 }
1679 else if (ch == '\r')
1680 {
1681 if (cupsFilePeekChar(fp) == '\n')
1682 {
1683 cupsFileGetChar(fp);
1684
1685 if (eol == EOL_NONE)
1686 eol = EOL_CRLF;
1f0275e3 1687 else if (eol != EOL_CRLF)
fa73b229 1688 mixed = 1;
1689 }
1690 else if (eol == EOL_NONE)
1691 eol = EOL_CR;
1f0275e3 1692 else if (eol != EOL_CR)
fa73b229 1693 mixed = 1;
1694 }
88f9aafc 1695
fa73b229 1696 if (col > 0 && whitespace)
1697 _cupsLangPrintf(stdout,
0837b7e8 1698 _(" WARN Line %d only contains whitespace."),
fa73b229 1699 linenum);
1700
1701 linenum ++;
1702 col = 0;
1703 whitespace = 1;
1704 }
1705 else
1706 {
1707 if (ch != ' ' && ch != '\t')
1708 whitespace = 0;
1709
1710 col ++;
1711 }
1712 }
1713
1714 if (mixed)
1715 _cupsLangPuts(stdout,
1716 _(" WARN File contains a mix of CR, LF, and "
0837b7e8 1717 "CR LF line endings."));
fa73b229 1718
1719 if (eol == EOL_CRLF)
1720 _cupsLangPuts(stdout,
1721 _(" WARN Non-Windows PPD files should use lines "
0837b7e8 1722 "ending with only LF, not CR LF."));
fa73b229 1723
1724 cupsFileClose(fp);
1725}
1726
1727
b94498cf 1728/*
1729 * 'check_constraints()' - Check UIConstraints in the PPD file.
1730 */
1731
3d8365b8 1732static int /* O - Errors found */
b94498cf 1733check_constraints(ppd_file_t *ppd, /* I - PPD file */
1734 int errors, /* I - Errors found */
1735 int verbose, /* I - Verbosity level */
1736 int warn) /* I - Warnings only? */
1737{
66ab9486
MS
1738 int i; /* Looping var */
1739 const char *prefix; /* WARN/FAIL prefix */
1740 ppd_const_t *c; /* Current UIConstraints data */
1741 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
1742 const char *vptr; /* Pointer into constraint value */
1743 char option[PPD_MAX_NAME],
1744 /* Option name/MainKeyword */
1745 choice[PPD_MAX_NAME],
1746 /* Choice/OptionKeyword */
1747 *ptr; /* Pointer into option or choice */
1748 int num_options; /* Number of options */
1749 cups_option_t *options; /* Options */
1750 ppd_option_t *o; /* PPD option */
b94498cf 1751
1752
1753 prefix = warn ? " WARN " : "**FAIL**";
1754
66ab9486
MS
1755
1756 /*
1757 * See what kind of constraint data we have in the PPD...
1758 */
1759
1760 if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
1761 {
1762 /*
1763 * Check new-style cupsUIConstraints data...
1764 */
1765
1766 for (; constattr;
1767 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
1768 {
1769 if (!constattr->value)
1770 {
1771 if (!warn && !errors && !verbose)
0837b7e8 1772 _cupsLangPuts(stdout, _(" FAIL"));
66ab9486
MS
1773
1774 _cupsLangPrintf(stdout,
0837b7e8 1775 _(" %s Empty cupsUIConstraints %s"),
66ab9486
MS
1776 prefix, constattr->spec);
1777
1778 if (!warn)
1779 errors ++;
1780
1781 continue;
1782 }
1783
1784 for (i = 0, vptr = strchr(constattr->value, '*');
1785 vptr;
1786 i ++, vptr = strchr(vptr + 1, '*'));
1787
1788 if (i == 0)
1789 {
1790 if (!warn && !errors && !verbose)
0837b7e8 1791 _cupsLangPuts(stdout, _(" FAIL"));
66ab9486
MS
1792
1793 _cupsLangPrintf(stdout,
0837b7e8 1794 _(" %s Bad cupsUIConstraints %s: \"%s\""),
66ab9486
MS
1795 prefix, constattr->spec, constattr->value);
1796
1797 if (!warn)
1798 errors ++;
1799
1800 continue;
1801 }
1802
1803 cupsArraySave(ppd->sorted_attrs);
1804
1805 if (constattr->spec[0] &&
1806 !ppdFindAttr(ppd, "cupsUIResolver", constattr->spec))
1807 {
1808 if (!warn && !errors && !verbose)
0837b7e8 1809 _cupsLangPuts(stdout, _(" FAIL"));
66ab9486
MS
1810
1811 _cupsLangPrintf(stdout,
0837b7e8 1812 _(" %s Missing cupsUIResolver %s"),
66ab9486
MS
1813 prefix, constattr->spec);
1814
1815 if (!warn)
1816 errors ++;
1817 }
1818
1819 cupsArrayRestore(ppd->sorted_attrs);
1820
1821 num_options = 0;
1822 options = NULL;
1823
1824 for (vptr = strchr(constattr->value, '*');
1825 vptr;
005dd1eb 1826 vptr = strchr(vptr, '*'))
66ab9486
MS
1827 {
1828 /*
1829 * Extract "*Option Choice" or just "*Option"...
1830 */
1831
1832 for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
1833 if (ptr < (option + sizeof(option) - 1))
1834 *ptr++ = *vptr;
1835
1836 *ptr = '\0';
1837
1838 while (isspace(*vptr & 255))
1839 vptr ++;
1840
1841 if (*vptr == '*')
66ab9486 1842 choice[0] = '\0';
66ab9486
MS
1843 else
1844 {
1845 for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
1846 if (ptr < (choice + sizeof(choice) - 1))
1847 *ptr++ = *vptr;
1848
1849 *ptr = '\0';
1850 }
1851
88f9aafc 1852 if (!_cups_strncasecmp(option, "Custom", 6) && !_cups_strcasecmp(choice, "True"))
66ab9486
MS
1853 {
1854 _cups_strcpy(option, option + 6);
5a9febac 1855 strlcpy(choice, "Custom", sizeof(choice));
66ab9486
MS
1856 }
1857
1858 if ((o = ppdFindOption(ppd, option)) == NULL)
1859 {
1860 if (!warn && !errors && !verbose)
0837b7e8 1861 _cupsLangPuts(stdout, _(" FAIL"));
66ab9486
MS
1862
1863 _cupsLangPrintf(stdout,
1864 _(" %s Missing option %s in "
0837b7e8 1865 "cupsUIConstraints %s: \"%s\""),
66ab9486 1866 prefix, option, constattr->spec, constattr->value);
88f9aafc 1867
66ab9486
MS
1868 if (!warn)
1869 errors ++;
1870
1871 continue;
1872 }
1873
1874 if (choice[0] && !ppdFindChoice(o, choice))
1875 {
1876 if (!warn && !errors && !verbose)
0837b7e8 1877 _cupsLangPuts(stdout, _(" FAIL"));
66ab9486
MS
1878
1879 _cupsLangPrintf(stdout,
1880 _(" %s Missing choice *%s %s in "
0837b7e8 1881 "cupsUIConstraints %s: \"%s\""),
66ab9486
MS
1882 prefix, option, choice, constattr->spec,
1883 constattr->value);
1884
1885 if (!warn)
1886 errors ++;
1887
1888 continue;
1889 }
1890
1891 if (choice[0])
1892 num_options = cupsAddOption(option, choice, num_options, &options);
1893 else
1894 {
1895 for (i = 0; i < o->num_choices; i ++)
88f9aafc
MS
1896 if (_cups_strcasecmp(o->choices[i].choice, "None") &&
1897 _cups_strcasecmp(o->choices[i].choice, "Off") &&
1898 _cups_strcasecmp(o->choices[i].choice, "False"))
66ab9486
MS
1899 {
1900 num_options = cupsAddOption(option, o->choices[i].choice,
1901 num_options, &options);
1902 break;
1903 }
1904 }
1905 }
1906
1340db2d
MS
1907 /*
1908 * Resolvers must list at least two options...
1909 */
1910
1911 if (num_options < 2)
1912 {
1913 if (!warn && !errors && !verbose)
0837b7e8 1914 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
1915
1916 _cupsLangPrintf(stdout,
1917 _(" %s cupsUIResolver %s does not list at least "
0837b7e8 1918 "two different options."),
1340db2d
MS
1919 prefix, constattr->spec);
1920
1921 if (!warn)
1922 errors ++;
1923 }
1924
66ab9486
MS
1925 /*
1926 * Test the resolver...
1927 */
1928
1929 if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options))
1930 {
1931 if (!warn && !errors && !verbose)
0837b7e8 1932 _cupsLangPuts(stdout, _(" FAIL"));
66ab9486
MS
1933
1934 _cupsLangPrintf(stdout,
0837b7e8 1935 _(" %s cupsUIResolver %s causes a loop."),
66ab9486
MS
1936 prefix, constattr->spec);
1937
1938 if (!warn)
1939 errors ++;
1940 }
1941
1942 cupsFreeOptions(num_options, options);
1943 }
1944 }
1945 else
b94498cf 1946 {
66ab9486
MS
1947 /*
1948 * Check old-style [Non]UIConstraints data...
1949 */
b94498cf 1950
66ab9486 1951 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
b94498cf 1952 {
88f9aafc
MS
1953 if (!_cups_strncasecmp(c->option1, "Custom", 6) &&
1954 !_cups_strcasecmp(c->choice1, "True"))
66ab9486 1955 {
5a9febac
MS
1956 strlcpy(option, c->option1 + 6, sizeof(option));
1957 strlcpy(choice, "Custom", sizeof(choice));
66ab9486
MS
1958 }
1959 else
1960 {
5a9febac
MS
1961 strlcpy(option, c->option1, sizeof(option));
1962 strlcpy(choice, c->choice1, sizeof(choice));
66ab9486
MS
1963 }
1964
1965 if ((o = ppdFindOption(ppd, option)) == NULL)
1966 {
1967 if (!warn && !errors && !verbose)
0837b7e8 1968 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 1969
b94498cf 1970 _cupsLangPrintf(stdout,
1971 _(" %s Missing option %s in "
0837b7e8 1972 "UIConstraints \"*%s %s *%s %s\"."),
b94498cf 1973 prefix, c->option1,
1974 c->option1, c->choice1, c->option2, c->choice2);
66ab9486
MS
1975
1976 if (!warn)
1977 errors ++;
1978 }
1979 else if (choice[0] && !ppdFindChoice(o, choice))
1980 {
1981 if (!warn && !errors && !verbose)
0837b7e8 1982 _cupsLangPuts(stdout, _(" FAIL"));
66ab9486 1983
b94498cf 1984 _cupsLangPrintf(stdout,
66ab9486 1985 _(" %s Missing choice *%s %s in "
0837b7e8 1986 "UIConstraints \"*%s %s *%s %s\"."),
66ab9486 1987 prefix, c->option1, c->choice1,
b94498cf 1988 c->option1, c->choice1, c->option2, c->choice2);
1989
66ab9486
MS
1990 if (!warn)
1991 errors ++;
1992 }
b94498cf 1993
88f9aafc
MS
1994 if (!_cups_strncasecmp(c->option2, "Custom", 6) &&
1995 !_cups_strcasecmp(c->choice2, "True"))
66ab9486 1996 {
5a9febac
MS
1997 strlcpy(option, c->option2 + 6, sizeof(option));
1998 strlcpy(choice, "Custom", sizeof(choice));
66ab9486
MS
1999 }
2000 else
2001 {
5a9febac
MS
2002 strlcpy(option, c->option2, sizeof(option));
2003 strlcpy(choice, c->choice2, sizeof(choice));
66ab9486 2004 }
b94498cf 2005
66ab9486
MS
2006 if ((o = ppdFindOption(ppd, option)) == NULL)
2007 {
2008 if (!warn && !errors && !verbose)
0837b7e8 2009 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 2010
66ab9486
MS
2011 _cupsLangPrintf(stdout,
2012 _(" %s Missing option %s in "
0837b7e8 2013 "UIConstraints \"*%s %s *%s %s\"."),
66ab9486
MS
2014 prefix, c->option2,
2015 c->option1, c->choice1, c->option2, c->choice2);
b94498cf 2016
66ab9486
MS
2017 if (!warn)
2018 errors ++;
2019 }
2020 else if (choice[0] && !ppdFindChoice(o, choice))
2021 {
2022 if (!warn && !errors && !verbose)
0837b7e8 2023 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 2024
66ab9486
MS
2025 _cupsLangPrintf(stdout,
2026 _(" %s Missing choice *%s %s in "
0837b7e8 2027 "UIConstraints \"*%s %s *%s %s\"."),
66ab9486
MS
2028 prefix, c->option2, c->choice2,
2029 c->option1, c->choice1, c->option2, c->choice2);
b94498cf 2030
66ab9486
MS
2031 if (!warn)
2032 errors ++;
2033 }
b94498cf 2034 }
2035 }
2036
2037 return (errors);
2038}
2039
2040
749b1e90
MS
2041/*
2042 * 'check_case()' - Check that there are no duplicate groups, options,
2043 * or choices that differ only by case.
2044 */
2045
2046static int /* O - Errors found */
2047check_case(ppd_file_t *ppd, /* I - PPD file */
2048 int errors, /* I - Errors found */
2049 int verbose) /* I - Verbosity level */
2050{
2051 int i, j; /* Looping vars */
2052 ppd_group_t *groupa, /* First group */
2053 *groupb; /* Second group */
2054 ppd_option_t *optiona, /* First option */
2055 *optionb; /* Second option */
2056 ppd_choice_t *choicea, /* First choice */
2057 *choiceb; /* Second choice */
2058
2059
2060 /*
2061 * Check that the groups do not have any duplicate names...
2062 */
2063
2064 for (i = ppd->num_groups, groupa = ppd->groups; i > 1; i --, groupa ++)
2065 for (j = i - 1, groupb = groupa + 1; j > 0; j --, groupb ++)
88f9aafc 2066 if (!_cups_strcasecmp(groupa->name, groupb->name))
749b1e90
MS
2067 {
2068 if (!errors && !verbose)
0837b7e8 2069 _cupsLangPuts(stdout, _(" FAIL"));
749b1e90
MS
2070
2071 if (verbose >= 0)
2072 _cupsLangPrintf(stdout,
2073 _(" **FAIL** Group names %s and %s differ only "
0837b7e8 2074 "by case."),
749b1e90
MS
2075 groupa->name, groupb->name);
2076
2077 errors ++;
2078 }
2079
2080 /*
2081 * Check that the options do not have any duplicate names...
2082 */
2083
2084 for (optiona = ppdFirstOption(ppd); optiona; optiona = ppdNextOption(ppd))
2085 {
2086 cupsArraySave(ppd->options);
2087 for (optionb = ppdNextOption(ppd); optionb; optionb = ppdNextOption(ppd))
88f9aafc 2088 if (!_cups_strcasecmp(optiona->keyword, optionb->keyword))
749b1e90
MS
2089 {
2090 if (!errors && !verbose)
0837b7e8 2091 _cupsLangPuts(stdout, _(" FAIL"));
749b1e90
MS
2092
2093 if (verbose >= 0)
2094 _cupsLangPrintf(stdout,
2095 _(" **FAIL** Option names %s and %s differ only "
0837b7e8 2096 "by case."),
749b1e90
MS
2097 optiona->keyword, optionb->keyword);
2098
2099 errors ++;
2100 }
2101 cupsArrayRestore(ppd->options);
2102
2103 /*
2104 * Then the choices...
2105 */
2106
2107 for (i = optiona->num_choices, choicea = optiona->choices;
2108 i > 1;
2109 i --, choicea ++)
2110 for (j = i - 1, choiceb = choicea + 1; j > 0; j --, choiceb ++)
2111 if (!strcmp(choicea->choice, choiceb->choice))
2112 {
2113 if (!errors && !verbose)
0837b7e8 2114 _cupsLangPuts(stdout, _(" FAIL"));
749b1e90
MS
2115
2116 if (verbose >= 0)
2117 _cupsLangPrintf(stdout,
f3c17241
MS
2118 _(" **FAIL** Multiple occurrences of "
2119 "option %s choice name %s."),
749b1e90
MS
2120 optiona->keyword, choicea->choice);
2121
2122 errors ++;
2123
2124 choicea ++;
2125 i --;
2126 break;
2127 }
88f9aafc 2128 else if (!_cups_strcasecmp(choicea->choice, choiceb->choice))
749b1e90
MS
2129 {
2130 if (!errors && !verbose)
0837b7e8 2131 _cupsLangPuts(stdout, _(" FAIL"));
749b1e90
MS
2132
2133 if (verbose >= 0)
2134 _cupsLangPrintf(stdout,
f3c17241
MS
2135 _(" **FAIL** Option %s choice names %s and "
2136 "%s differ only by case."),
749b1e90
MS
2137 optiona->keyword, choicea->choice, choiceb->choice);
2138
2139 errors ++;
2140 }
2141 }
2142
2143 /*
2144 * Return the number of errors found...
2145 */
2146
2147 return (errors);
2148}
2149
2150
b94498cf 2151/*
2152 * 'check_defaults()' - Check default option keywords in the PPD file.
2153 */
2154
3d8365b8 2155static int /* O - Errors found */
b94498cf 2156check_defaults(ppd_file_t *ppd, /* I - PPD file */
2157 int errors, /* I - Errors found */
2158 int verbose, /* I - Verbosity level */
2159 int warn) /* I - Warnings only? */
2160{
2161 int j, k; /* Looping vars */
2162 ppd_attr_t *attr; /* PPD attribute */
2163 ppd_option_t *option; /* Standard UI option */
2164 const char *prefix; /* WARN/FAIL prefix */
2165
2166
2167 prefix = warn ? " WARN " : "**FAIL**";
2168
22c9029b
MS
2169 ppdMarkDefaults(ppd);
2170 if (ppdConflicts(ppd))
2171 {
2172 if (!warn && !errors && !verbose)
2173 _cupsLangPuts(stdout, _(" FAIL"));
2174
2175 if (verbose >= 0)
2176 _cupsLangPrintf(stdout,
2177 _(" %s Default choices conflicting."), prefix);
2178
2179 show_conflicts(ppd, prefix);
2180
2181 if (!warn)
2182 errors ++;
2183 }
2184
b94498cf 2185 for (j = 0; j < ppd->num_attrs; j ++)
2186 {
2187 attr = ppd->attrs[j];
2188
2189 if (!strcmp(attr->name, "DefaultColorSpace") ||
2190 !strcmp(attr->name, "DefaultFont") ||
09a101d6 2191 !strcmp(attr->name, "DefaultHalftoneType") ||
b94498cf 2192 !strcmp(attr->name, "DefaultImageableArea") ||
09a101d6 2193 !strcmp(attr->name, "DefaultLeadingEdge") ||
b94498cf 2194 !strcmp(attr->name, "DefaultOutputOrder") ||
2195 !strcmp(attr->name, "DefaultPaperDimension") ||
2196 !strcmp(attr->name, "DefaultResolution") ||
2197 !strcmp(attr->name, "DefaultTransfer"))
2198 continue;
2199
2200 if (!strncmp(attr->name, "Default", 7))
2201 {
2202 if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
2203 strcmp(attr->value, "Unknown"))
2204 {
2205 /*
2206 * Check that the default option value matches a choice...
2207 */
2208
2209 for (k = 0; k < option->num_choices; k ++)
2210 if (!strcmp(option->choices[k].choice, attr->value))
2211 break;
2212
2213 if (k >= option->num_choices)
2214 {
2215 if (!warn && !errors && !verbose)
0837b7e8 2216 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 2217
2218 if (verbose >= 0)
2219 _cupsLangPrintf(stdout,
0837b7e8 2220 _(" %s %s %s does not exist."),
b94498cf 2221 prefix, attr->name, attr->value);
2222
2223 if (!warn)
2224 errors ++;
2225 }
2226 }
2227 }
2228 }
2229
2230 return (errors);
2231}
2232
2233
749b1e90
MS
2234/*
2235 * 'check_duplex()' - Check duplex keywords in the PPD file.
2236 */
2237
2238static int /* O - Errors found */
2239check_duplex(ppd_file_t *ppd, /* I - PPD file */
2240 int errors, /* I - Error found */
2241 int verbose, /* I - Verbosity level */
2242 int warn) /* I - Warnings only? */
2243{
2244 int i; /* Looping var */
2245 ppd_option_t *option; /* PPD option */
2246 ppd_choice_t *choice; /* Current choice */
2247 const char *prefix; /* Message prefix */
2248
2249
2250 prefix = warn ? " WARN " : "**FAIL**";
2251
2252 /*
2253 * Check for a duplex option, and for standard values...
2254 */
2255
2256 if ((option = ppdFindOption(ppd, "Duplex")) != NULL)
2257 {
2258 if (!ppdFindChoice(option, "None"))
2259 {
2260 if (verbose >= 0)
2261 {
2262 if (!warn && !errors && !verbose)
0837b7e8 2263 _cupsLangPuts(stdout, _(" FAIL"));
749b1e90
MS
2264
2265 _cupsLangPrintf(stdout,
2266 _(" %s REQUIRED %s does not define "
0837b7e8
MS
2267 "choice None.\n"
2268 " REF: Page 122, section 5.17"),
749b1e90
MS
2269 prefix, option->keyword);
2270 }
2271
2272 if (!warn)
2273 errors ++;
2274 }
2275
2276 for (i = option->num_choices, choice = option->choices;
2277 i > 0;
2278 i --, choice ++)
2279 if (strcmp(choice->choice, "None") &&
2280 strcmp(choice->choice, "DuplexNoTumble") &&
2281 strcmp(choice->choice, "DuplexTumble") &&
2282 strcmp(choice->choice, "SimplexTumble"))
2283 {
2284 if (verbose >= 0)
2285 {
2286 if (!warn && !errors && !verbose)
0837b7e8 2287 _cupsLangPuts(stdout, _(" FAIL"));
749b1e90
MS
2288
2289 _cupsLangPrintf(stdout,
0837b7e8
MS
2290 _(" %s Bad %s choice %s.\n"
2291 " REF: Page 122, section 5.17"),
749b1e90
MS
2292 prefix, option->keyword, choice->choice);
2293 }
2294
2295 if (!warn)
2296 errors ++;
2297 }
2298 }
2299
2300 return (errors);
2301}
2302
2303
b94498cf 2304/*
2305 * 'check_filters()' - Check filters in the PPD file.
2306 */
2307
3d8365b8 2308static int /* O - Errors found */
b94498cf 2309check_filters(ppd_file_t *ppd, /* I - PPD file */
2310 const char *root, /* I - Root directory */
2311 int errors, /* I - Errors found */
2312 int verbose, /* I - Verbosity level */
2313 int warn) /* I - Warnings only? */
2314{
2315 ppd_attr_t *attr; /* PPD attribute */
2316 const char *ptr; /* Pointer into string */
b94498cf 2317 char super[16], /* Super-type for filter */
2318 type[256], /* Type for filter */
a4845881
MS
2319 dstsuper[16], /* Destination super-type for filter */
2320 dsttype[256], /* Destination type for filter */
1f0275e3 2321 program[1024], /* Program/filter name */
b94498cf 2322 pathprog[1024]; /* Complete path to program/filter */
2323 int cost; /* Cost of filter */
2324 const char *prefix; /* WARN/FAIL prefix */
b226ab99 2325 struct stat fileinfo; /* File information */
b94498cf 2326
2327
2328 prefix = warn ? " WARN " : "**FAIL**";
2329
1340db2d
MS
2330 /*
2331 * cupsFilter
2332 */
2333
a4845881
MS
2334 for (attr = ppdFindAttr(ppd, "cupsFilter", NULL);
2335 attr;
2336 attr = ppdFindNextAttr(ppd, "cupsFilter", NULL))
b94498cf 2337 {
a4845881
MS
2338 if (strcmp(attr->name, "cupsFilter"))
2339 {
2340 if (!warn && !errors && !verbose)
2341 _cupsLangPuts(stdout, _(" FAIL"));
2342
2343 if (verbose >= 0)
2344 _cupsLangPrintf(stdout,
2345 _(" %s Bad spelling of %s - should be %s."),
2346 prefix, attr->name, "cupsFilter");
2347
2348 if (!warn)
2349 errors ++;
2350 }
2351
2352 if (!attr->value ||
2353 sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
1f0275e3 2354 &cost, program) != 4)
b94498cf 2355 {
2356 if (!warn && !errors && !verbose)
0837b7e8 2357 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 2358
2359 if (verbose >= 0)
2360 _cupsLangPrintf(stdout,
0837b7e8 2361 _(" %s Bad cupsFilter value \"%s\"."),
85dda01c 2362 prefix, attr->value);
b94498cf 2363
2364 if (!warn)
2365 errors ++;
ea72cf2b
MS
2366
2367 continue;
2368 }
2369
2370 if (!strncmp(program, "maxsize(", 8))
2371 {
f8f7636e 2372 char *mptr; /* Pointer into maxsize(nnnn) program */
ea72cf2b 2373
c8fdb001 2374 strtoll(program + 8, &mptr, 10);
ea72cf2b 2375
f8f7636e 2376 if (*mptr != ')')
ea72cf2b
MS
2377 {
2378 if (!warn && !errors && !verbose)
2379 _cupsLangPuts(stdout, _(" FAIL"));
2380
2381 if (verbose >= 0)
2382 _cupsLangPrintf(stdout,
2383 _(" %s Bad cupsFilter value \"%s\"."),
2384 prefix, attr->value);
2385
2386 if (!warn)
2387 errors ++;
2388
2389 continue;
2390 }
2391
f8f7636e
MS
2392 mptr ++;
2393 while (_cups_isspace(*mptr))
2394 mptr ++;
ea72cf2b 2395
f8f7636e 2396 _cups_strcpy(program, mptr);
b94498cf 2397 }
ea72cf2b
MS
2398
2399 if (strcmp(program, "-"))
b94498cf 2400 {
2401 if (program[0] == '/')
2402 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
2403 else
2404 {
2405 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
2406 ptr = CUPS_SERVERBIN;
2407
2408 if (*ptr == '/' || !*root)
2409 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
2410 program);
2411 else
2412 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
2413 program);
2414 }
2415
b226ab99 2416 if (stat(pathprog, &fileinfo))
b94498cf 2417 {
2418 if (!warn && !errors && !verbose)
0837b7e8 2419 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 2420
2421 if (verbose >= 0)
84315f46
MS
2422 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2423 prefix, "cupsFilter", pathprog);
b226ab99
MS
2424
2425 if (!warn)
2426 errors ++;
2427 }
2428 else if (fileinfo.st_uid != 0 ||
2429 (fileinfo.st_mode & MODE_WRITE) ||
2430 (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM)
2431 {
2432 if (!warn && !errors && !verbose)
0837b7e8 2433 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
2434
2435 if (verbose >= 0)
84315f46
MS
2436 _cupsLangPrintf(stdout,
2437 _(" %s Bad permissions on %s file \"%s\"."),
2438 prefix, "cupsFilter", pathprog);
b94498cf 2439
2440 if (!warn)
2441 errors ++;
2442 }
1340db2d
MS
2443 else
2444 errors = valid_path("cupsFilter", pathprog, errors, verbose, warn);
b94498cf 2445 }
2446 }
2447
a4845881
MS
2448 /*
2449 * cupsFilter2
2450 */
2451
2452 for (attr = ppdFindAttr(ppd, "cupsFilter2", NULL);
2453 attr;
2454 attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL))
2455 {
2456 if (strcmp(attr->name, "cupsFilter2"))
2457 {
2458 if (!warn && !errors && !verbose)
2459 _cupsLangPuts(stdout, _(" FAIL"));
2460
2461 if (verbose >= 0)
2462 _cupsLangPrintf(stdout,
2463 _(" %s Bad spelling of %s - should be %s."),
2464 prefix, attr->name, "cupsFilter2");
2465
2466 if (!warn)
2467 errors ++;
2468 }
2469
2470 if (!attr->value ||
2471 sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
2472 super, type, dstsuper, dsttype, &cost, program) != 6)
2473 {
2474 if (!warn && !errors && !verbose)
2475 _cupsLangPuts(stdout, _(" FAIL"));
2476
2477 if (verbose >= 0)
2478 _cupsLangPrintf(stdout,
2479 _(" %s Bad cupsFilter2 value \"%s\"."),
85dda01c 2480 prefix, attr->value);
a4845881
MS
2481
2482 if (!warn)
2483 errors ++;
ea72cf2b
MS
2484
2485 continue;
2486 }
2487
2488 if (!strncmp(program, "maxsize(", 8))
2489 {
f8f7636e 2490 char *mptr; /* Pointer into maxsize(nnnn) program */
ea72cf2b 2491
c8fdb001 2492 strtoll(program + 8, &mptr, 10);
ea72cf2b 2493
f8f7636e 2494 if (*mptr != ')')
ea72cf2b
MS
2495 {
2496 if (!warn && !errors && !verbose)
2497 _cupsLangPuts(stdout, _(" FAIL"));
2498
2499 if (verbose >= 0)
2500 _cupsLangPrintf(stdout,
2501 _(" %s Bad cupsFilter2 value \"%s\"."),
2502 prefix, attr->value);
2503
2504 if (!warn)
2505 errors ++;
2506
2507 continue;
2508 }
2509
f8f7636e
MS
2510 mptr ++;
2511 while (_cups_isspace(*mptr))
2512 mptr ++;
ea72cf2b 2513
f8f7636e 2514 _cups_strcpy(program, mptr);
a4845881 2515 }
ea72cf2b
MS
2516
2517 if (strcmp(program, "-"))
a4845881
MS
2518 {
2519 if (strncmp(program, "maxsize(", 8) &&
2520 (ptr = strchr(program + 8, ')')) != NULL)
2521 {
2522 ptr ++;
2523 while (_cups_isspace(*ptr))
2524 ptr ++;
2525
2526 _cups_strcpy(program, ptr);
2527 }
2528
2529 if (program[0] == '/')
2530 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
2531 else
2532 {
2533 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
2534 ptr = CUPS_SERVERBIN;
2535
2536 if (*ptr == '/' || !*root)
2537 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
2538 program);
2539 else
2540 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
2541 program);
2542 }
2543
2544 if (stat(pathprog, &fileinfo))
2545 {
2546 if (!warn && !errors && !verbose)
2547 _cupsLangPuts(stdout, _(" FAIL"));
2548
2549 if (verbose >= 0)
2550 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2551 prefix, "cupsFilter2", pathprog);
2552
2553 if (!warn)
2554 errors ++;
2555 }
2556 else if (fileinfo.st_uid != 0 ||
2557 (fileinfo.st_mode & MODE_WRITE) ||
2558 (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM)
2559 {
2560 if (!warn && !errors && !verbose)
2561 _cupsLangPuts(stdout, _(" FAIL"));
2562
2563 if (verbose >= 0)
2564 _cupsLangPrintf(stdout,
2565 _(" %s Bad permissions on %s file \"%s\"."),
2566 prefix, "cupsFilter2", pathprog);
2567
2568 if (!warn)
2569 errors ++;
2570 }
2571 else
2572 errors = valid_path("cupsFilter2", pathprog, errors, verbose, warn);
2573 }
2574 }
2575
1340db2d
MS
2576 /*
2577 * cupsPreFilter
2578 */
2579
b94498cf 2580 for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
2581 attr;
2582 attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
2583 {
8b116e60
MS
2584 if (strcmp(attr->name, "cupsPreFilter"))
2585 {
2586 if (!warn && !errors && !verbose)
0837b7e8 2587 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2588
2589 if (verbose >= 0)
2590 _cupsLangPrintf(stdout,
0837b7e8 2591 _(" %s Bad spelling of %s - should be %s."),
8b116e60
MS
2592 prefix, attr->name, "cupsPreFilter");
2593
2594 if (!warn)
2595 errors ++;
2596 }
2597
b94498cf 2598 if (!attr->value ||
1f0275e3
MS
2599 sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
2600 &cost, program) != 4)
b94498cf 2601 {
2602 if (!warn && !errors && !verbose)
0837b7e8 2603 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 2604
2605 if (verbose >= 0)
2606 _cupsLangPrintf(stdout,
0837b7e8 2607 _(" %s Bad cupsPreFilter value \"%s\"."),
b94498cf 2608 prefix, attr->value ? attr->value : "");
2609
2610 if (!warn)
2611 errors ++;
2612 }
749b1e90 2613 else if (strcmp(program, "-"))
b94498cf 2614 {
2615 if (program[0] == '/')
2616 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
2617 else
2618 {
2619 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
2620 ptr = CUPS_SERVERBIN;
2621
2622 if (*ptr == '/' || !*root)
2623 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
2624 program);
2625 else
2626 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
2627 program);
2628 }
2629
b226ab99 2630 if (stat(pathprog, &fileinfo))
b94498cf 2631 {
2632 if (!warn && !errors && !verbose)
0837b7e8 2633 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 2634
2635 if (verbose >= 0)
84315f46
MS
2636 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2637 prefix, "cupsPreFilter", pathprog);
b94498cf 2638
2639 if (!warn)
2640 errors ++;
2641 }
b226ab99
MS
2642 else if (fileinfo.st_uid != 0 ||
2643 (fileinfo.st_mode & MODE_WRITE) ||
2644 (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM)
2645 {
2646 if (!warn && !errors && !verbose)
0837b7e8 2647 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
2648
2649 if (verbose >= 0)
84315f46
MS
2650 _cupsLangPrintf(stdout,
2651 _(" %s Bad permissions on %s file \"%s\"."),
2652 prefix, "cupsPreFilter", pathprog);
b226ab99
MS
2653
2654 if (!warn)
2655 errors ++;
2656 }
1340db2d
MS
2657 else
2658 errors = valid_path("cupsPreFilter", pathprog, errors, verbose, warn);
2659 }
2660 }
2661
2662#ifdef __APPLE__
2663 /*
2664 * APDialogExtension
2665 */
2666
88f9aafc
MS
2667 for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL);
2668 attr != NULL;
1340db2d
MS
2669 attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
2670 {
8b116e60
MS
2671 if (strcmp(attr->name, "APDialogExtension"))
2672 {
2673 if (!warn && !errors && !verbose)
0837b7e8 2674 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2675
2676 if (verbose >= 0)
2677 _cupsLangPrintf(stdout,
0837b7e8 2678 _(" %s Bad spelling of %s - should be %s."),
8b116e60
MS
2679 prefix, attr->name, "APDialogExtension");
2680
2681 if (!warn)
2682 errors ++;
2683 }
88f9aafc 2684
b226ab99
MS
2685 snprintf(pathprog, sizeof(pathprog), "%s%s", root,
2686 attr->value ? attr->value : "(null)");
8b116e60 2687
b226ab99 2688 if (!attr->value || stat(pathprog, &fileinfo))
1340db2d
MS
2689 {
2690 if (!warn && !errors && !verbose)
0837b7e8 2691 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
2692
2693 if (verbose >= 0)
84315f46
MS
2694 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2695 prefix, "APDialogExtension", pathprog);
b226ab99
MS
2696
2697 if (!warn)
2698 errors ++;
2699 }
2700 else if (fileinfo.st_uid != 0 ||
2701 (fileinfo.st_mode & MODE_WRITE) ||
2702 (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
2703 {
2704 if (!warn && !errors && !verbose)
0837b7e8 2705 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
2706
2707 if (verbose >= 0)
84315f46
MS
2708 _cupsLangPrintf(stdout,
2709 _(" %s Bad permissions on %s file \"%s\"."),
2710 prefix, "APDialogExtension", pathprog);
1340db2d
MS
2711
2712 if (!warn)
2713 errors ++;
2714 }
2715 else
b226ab99 2716 errors = valid_path("APDialogExtension", pathprog, errors, verbose,
1340db2d
MS
2717 warn);
2718 }
2719
2720 /*
2721 * APPrinterIconPath
2722 */
2723
2724 if ((attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
2725 {
8b116e60
MS
2726 if (strcmp(attr->name, "APPrinterIconPath"))
2727 {
2728 if (!warn && !errors && !verbose)
0837b7e8 2729 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2730
2731 if (verbose >= 0)
2732 _cupsLangPrintf(stdout,
0837b7e8 2733 _(" %s Bad spelling of %s - should be %s."),
8b116e60
MS
2734 prefix, attr->name, "APPrinterIconPath");
2735
2736 if (!warn)
2737 errors ++;
2738 }
2739
b226ab99
MS
2740 snprintf(pathprog, sizeof(pathprog), "%s%s", root,
2741 attr->value ? attr->value : "(null)");
2742
2743 if (!attr->value || stat(pathprog, &fileinfo))
1340db2d
MS
2744 {
2745 if (!warn && !errors && !verbose)
0837b7e8 2746 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
2747
2748 if (verbose >= 0)
84315f46
MS
2749 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2750 prefix, "APPrinterIconPath", pathprog);
b226ab99
MS
2751
2752 if (!warn)
2753 errors ++;
2754 }
2755 else if (fileinfo.st_uid != 0 ||
2756 (fileinfo.st_mode & MODE_WRITE) ||
2757 (fileinfo.st_mode & MODE_MASK) != MODE_DATAFILE)
2758 {
2759 if (!warn && !errors && !verbose)
0837b7e8 2760 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
2761
2762 if (verbose >= 0)
84315f46
MS
2763 _cupsLangPrintf(stdout,
2764 _(" %s Bad permissions on %s file \"%s\"."),
2765 prefix, "APPrinterIconPath", pathprog);
1340db2d
MS
2766
2767 if (!warn)
2768 errors ++;
2769 }
2770 else
b226ab99 2771 errors = valid_path("APPrinterIconPath", pathprog, errors, verbose,
1340db2d
MS
2772 warn);
2773 }
2774
2775 /*
2776 * APPrinterLowInkTool
2777 */
2778
2779 if ((attr = ppdFindAttr(ppd, "APPrinterLowInkTool", NULL)) != NULL)
2780 {
8b116e60
MS
2781 if (strcmp(attr->name, "APPrinterLowInkTool"))
2782 {
2783 if (!warn && !errors && !verbose)
0837b7e8 2784 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2785
2786 if (verbose >= 0)
2787 _cupsLangPrintf(stdout,
0837b7e8 2788 _(" %s Bad spelling of %s - should be %s."),
8b116e60
MS
2789 prefix, attr->name, "APPrinterLowInkTool");
2790
2791 if (!warn)
2792 errors ++;
2793 }
2794
b226ab99
MS
2795 snprintf(pathprog, sizeof(pathprog), "%s%s", root,
2796 attr->value ? attr->value : "(null)");
2797
2798 if (!attr->value || stat(pathprog, &fileinfo))
1340db2d
MS
2799 {
2800 if (!warn && !errors && !verbose)
0837b7e8 2801 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
2802
2803 if (verbose >= 0)
84315f46
MS
2804 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2805 prefix, "APPrinterLowInkTool", pathprog);
b226ab99
MS
2806
2807 if (!warn)
2808 errors ++;
2809 }
2810 else if (fileinfo.st_uid != 0 ||
2811 (fileinfo.st_mode & MODE_WRITE) ||
2812 (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
2813 {
2814 if (!warn && !errors && !verbose)
0837b7e8 2815 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
2816
2817 if (verbose >= 0)
84315f46
MS
2818 _cupsLangPrintf(stdout,
2819 _(" %s Bad permissions on %s file \"%s\"."),
2820 prefix, "APPrinterLowInkTool", pathprog);
1340db2d
MS
2821
2822 if (!warn)
2823 errors ++;
2824 }
2825 else
b226ab99 2826 errors = valid_path("APPrinterLowInkTool", pathprog, errors, verbose,
1340db2d
MS
2827 warn);
2828 }
2829
2830 /*
2831 * APPrinterUtilityPath
2832 */
2833
2834 if ((attr = ppdFindAttr(ppd, "APPrinterUtilityPath", NULL)) != NULL)
2835 {
8b116e60
MS
2836 if (strcmp(attr->name, "APPrinterUtilityPath"))
2837 {
2838 if (!warn && !errors && !verbose)
0837b7e8 2839 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2840
2841 if (verbose >= 0)
2842 _cupsLangPrintf(stdout,
0837b7e8 2843 _(" %s Bad spelling of %s - should be %s."),
8b116e60
MS
2844 prefix, attr->name, "APPrinterUtilityPath");
2845
2846 if (!warn)
2847 errors ++;
2848 }
2849
b226ab99
MS
2850 snprintf(pathprog, sizeof(pathprog), "%s%s", root,
2851 attr->value ? attr->value : "(null)");
2852
2853 if (!attr->value || stat(pathprog, &fileinfo))
1340db2d
MS
2854 {
2855 if (!warn && !errors && !verbose)
0837b7e8 2856 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
2857
2858 if (verbose >= 0)
84315f46
MS
2859 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2860 prefix, "APPrinterUtilityPath", pathprog);
b226ab99
MS
2861
2862 if (!warn)
2863 errors ++;
2864 }
2865 else if (fileinfo.st_uid != 0 ||
2866 (fileinfo.st_mode & MODE_WRITE) ||
2867 (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
2868 {
2869 if (!warn && !errors && !verbose)
0837b7e8 2870 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
2871
2872 if (verbose >= 0)
84315f46
MS
2873 _cupsLangPrintf(stdout,
2874 _(" %s Bad permissions on %s file \"%s\"."),
2875 prefix, "APPrinterUtilityPath", pathprog);
1340db2d
MS
2876
2877 if (!warn)
2878 errors ++;
b94498cf 2879 }
1340db2d 2880 else
b226ab99 2881 errors = valid_path("APPrinterUtilityPath", pathprog, errors, verbose,
1340db2d 2882 warn);
b94498cf 2883 }
8b116e60
MS
2884
2885 /*
2886 * APScanAppBundleID and APScanAppPath
2887 */
2888
2889 if ((attr = ppdFindAttr(ppd, "APScanAppPath", NULL)) != NULL)
2890 {
2891 if (strcmp(attr->name, "APScanAppPath"))
2892 {
2893 if (!warn && !errors && !verbose)
0837b7e8 2894 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2895
2896 if (verbose >= 0)
2897 _cupsLangPrintf(stdout,
0837b7e8 2898 _(" %s Bad spelling of %s - should be %s."),
8b116e60
MS
2899 prefix, attr->name, "APScanAppPath");
2900
2901 if (!warn)
2902 errors ++;
2903 }
2904
b226ab99 2905 if (!attr->value || stat(attr->value, &fileinfo))
8b116e60
MS
2906 {
2907 if (!warn && !errors && !verbose)
0837b7e8 2908 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2909
2910 if (verbose >= 0)
84315f46
MS
2911 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
2912 prefix, "APScanAppPath",
2913 attr->value ? attr->value : "<NULL>");
8b116e60
MS
2914
2915 if (!warn)
2916 errors ++;
2917 }
b226ab99
MS
2918 else if (fileinfo.st_uid != 0 ||
2919 (fileinfo.st_mode & MODE_WRITE) ||
2920 (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
2921 {
2922 if (!warn && !errors && !verbose)
0837b7e8 2923 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
2924
2925 if (verbose >= 0)
84315f46
MS
2926 _cupsLangPrintf(stdout,
2927 _(" %s Bad permissions on %s file \"%s\"."),
2928 prefix, "APScanAppPath", attr->value);
b226ab99
MS
2929
2930 if (!warn)
2931 errors ++;
2932 }
8b116e60
MS
2933 else
2934 errors = valid_path("APScanAppPath", attr->value, errors, verbose,
2935 warn);
2936
2937 if (ppdFindAttr(ppd, "APScanAppBundleID", NULL))
2938 {
2939 if (!warn && !errors && !verbose)
0837b7e8 2940 _cupsLangPuts(stdout, _(" FAIL"));
8b116e60
MS
2941
2942 if (verbose >= 0)
2943 _cupsLangPrintf(stdout, _(" %s Cannot provide both "
0837b7e8 2944 "APScanAppPath and APScanAppBundleID."),
8b116e60
MS
2945 prefix);
2946
2947 if (!warn)
2948 errors ++;
2949 }
2950 }
1340db2d 2951#endif /* __APPLE__ */
b94498cf 2952
2953 return (errors);
2954}
2955
2956
a603edef
MS
2957/*
2958 * 'check_profiles()' - Check ICC color profiles in the PPD file.
2959 */
2960
2961static int /* O - Errors found */
2962check_profiles(ppd_file_t *ppd, /* I - PPD file */
2963 const char *root, /* I - Root directory */
2964 int errors, /* I - Errors found */
2965 int verbose, /* I - Verbosity level */
2966 int warn) /* I - Warnings only? */
2967{
2968 int i; /* Looping var */
2969 ppd_attr_t *attr; /* PPD attribute */
2970 const char *ptr; /* Pointer into string */
2971 const char *prefix; /* WARN/FAIL prefix */
2972 char filename[1024]; /* Profile filename */
b226ab99 2973 struct stat fileinfo; /* File information */
a603edef
MS
2974 int num_profiles = 0; /* Number of profiles */
2975 unsigned hash, /* Current hash value */
2976 hashes[1000]; /* Hash values of profile names */
2977 const char *specs[1000]; /* Specifiers for profiles */
2978
2979
2980 prefix = warn ? " WARN " : "**FAIL**";
2981
2982 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
2983 attr;
2984 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
2985 {
2986 /*
2987 * Check for valid selector...
2988 */
2989
2990 for (i = 0, ptr = strchr(attr->spec, '.'); ptr; ptr = strchr(ptr + 1, '.'))
2991 i ++;
2992
2993 if (!attr->value || i < 2)
2994 {
2995 if (!warn && !errors && !verbose)
0837b7e8 2996 _cupsLangPuts(stdout, _(" FAIL"));
a603edef
MS
2997
2998 if (verbose >= 0)
2999 _cupsLangPrintf(stdout,
0837b7e8 3000 _(" %s Bad cupsICCProfile %s."),
a603edef
MS
3001 prefix, attr->spec);
3002
3003 if (!warn)
3004 errors ++;
3005
3006 continue;
3007 }
3008
3009 /*
3010 * Check for valid profile filename...
3011 */
3012
3013 if (attr->value[0] == '/')
3014 snprintf(filename, sizeof(filename), "%s%s", root, attr->value);
3015 else
3016 {
3017 if ((ptr = getenv("CUPS_DATADIR")) == NULL)
3018 ptr = CUPS_DATADIR;
3019
3020 if (*ptr == '/' || !*root)
3021 snprintf(filename, sizeof(filename), "%s%s/profiles/%s", root, ptr,
3022 attr->value);
3023 else
3024 snprintf(filename, sizeof(filename), "%s/%s/profiles/%s", root, ptr,
3025 attr->value);
3026 }
3027
b226ab99 3028 if (stat(filename, &fileinfo))
a603edef
MS
3029 {
3030 if (!warn && !errors && !verbose)
0837b7e8 3031 _cupsLangPuts(stdout, _(" FAIL"));
a603edef
MS
3032
3033 if (verbose >= 0)
84315f46
MS
3034 _cupsLangPrintf(stdout, _(" %s Missing %s file \"%s\"."),
3035 prefix, "cupsICCProfile", filename);
b226ab99
MS
3036
3037 if (!warn)
3038 errors ++;
3039 }
3040 else if (fileinfo.st_uid != 0 ||
3041 (fileinfo.st_mode & MODE_WRITE) ||
3042 (fileinfo.st_mode & MODE_MASK) != MODE_DATAFILE)
3043 {
3044 if (!warn && !errors && !verbose)
0837b7e8 3045 _cupsLangPuts(stdout, _(" FAIL"));
b226ab99
MS
3046
3047 if (verbose >= 0)
84315f46
MS
3048 _cupsLangPrintf(stdout,
3049 _(" %s Bad permissions on %s file \"%s\"."),
3050 prefix, "cupsICCProfile", filename);
a603edef
MS
3051
3052 if (!warn)
3053 errors ++;
3054 }
1340db2d
MS
3055 else
3056 errors = valid_path("cupsICCProfile", filename, errors, verbose, warn);
a603edef
MS
3057
3058 /*
3059 * Check for hash collisions...
3060 */
3061
3062 hash = _ppdHashName(attr->spec);
3063
3064 if (num_profiles > 0)
3065 {
3066 for (i = 0; i < num_profiles; i ++)
3067 if (hashes[i] == hash)
3068 break;
3069
3070 if (i < num_profiles)
3071 {
3072 if (!warn && !errors && !verbose)
0837b7e8 3073 _cupsLangPuts(stdout, _(" FAIL"));
a603edef
MS
3074
3075 if (verbose >= 0)
3076 _cupsLangPrintf(stdout,
3077 _(" %s cupsICCProfile %s hash value "
0837b7e8 3078 "collides with %s."), prefix, attr->spec,
a603edef
MS
3079 specs[i]);
3080
3081 if (!warn)
3082 errors ++;
3083 }
3084 }
3085
3086 /*
3087 * Remember up to 1000 profiles...
3088 */
3089
3090 if (num_profiles < 1000)
3091 {
3092 hashes[num_profiles] = hash;
3093 specs[num_profiles] = attr->spec;
3094 num_profiles ++;
3095 }
3096 }
3097
3098 return (errors);
3099}
3100
3101
1340db2d
MS
3102/*
3103 * 'check_sizes()' - Check media sizes in the PPD file.
3104 */
3105
3106static int /* O - Errors found */
3107check_sizes(ppd_file_t *ppd, /* I - PPD file */
3108 int errors, /* I - Errors found */
3109 int verbose, /* I - Verbosity level */
3110 int warn) /* I - Warnings only? */
3111{
22c9029b 3112 int i; /* Looping var */
1340db2d
MS
3113 ppd_size_t *size; /* Current size */
3114 int width, /* Custom width */
3115 length; /* Custom length */
1340db2d
MS
3116 const char *prefix; /* WARN/FAIL prefix */
3117 ppd_option_t *page_size, /* PageSize option */
3118 *page_region; /* PageRegion option */
788d7a15 3119 pwg_media_t *pwg_media; /* PWG media */
c1420c87 3120 char buf[PPD_MAX_NAME]; /* PapeSize name that is supposed to be */
22c9029b
MS
3121 const char *ptr; /* Pointer into string */
3122 int width_2540ths, /* PageSize width in 2540ths */
3123 length_2540ths; /* PageSize length in 2540ths */
3124 int is_ok; /* Flag for PageSize name verification */
3125 double width_tmp, /* Width after rounded up */
3126 length_tmp, /* Length after rounded up */
3127 width_inch, /* Width in inches */
3128 length_inch, /* Length in inches */
3129 width_mm, /* Width in millimeters */
3130 length_mm; /* Length in millimeters */
1340db2d
MS
3131
3132
3133 prefix = warn ? " WARN " : "**FAIL**";
3134
3135 if ((page_size = ppdFindOption(ppd, "PageSize")) == NULL && warn != 2)
3136 {
3137 if (!warn && !errors && !verbose)
0837b7e8 3138 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
3139
3140 if (verbose >= 0)
3141 _cupsLangPrintf(stdout,
0837b7e8
MS
3142 _(" %s Missing REQUIRED PageSize option.\n"
3143 " REF: Page 99, section 5.14."),
1340db2d
MS
3144 prefix);
3145
3146 if (!warn)
3147 errors ++;
3148 }
3149
3150 if ((page_region = ppdFindOption(ppd, "PageRegion")) == NULL && warn != 2)
3151 {
3152 if (!warn && !errors && !verbose)
0837b7e8 3153 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
3154
3155 if (verbose >= 0)
3156 _cupsLangPrintf(stdout,
0837b7e8
MS
3157 _(" %s Missing REQUIRED PageRegion option.\n"
3158 " REF: Page 100, section 5.14."),
1340db2d
MS
3159 prefix);
3160
3161 if (!warn)
3162 errors ++;
3163 }
3164
3165 for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
3166 {
3167 /*
3168 * Check that the size name is standard...
3169 */
3170
3171 if (!strcmp(size->name, "Custom"))
3172 {
3173 /*
3174 * Skip custom page size...
3175 */
3176
3177 continue;
3178 }
82cc1f9a
MS
3179
3180 if (warn != 2 && size->name[0] == 'w' &&
3181 sscanf(size->name, "w%dh%d", &width, &length) == 2)
1340db2d
MS
3182 {
3183 /*
3184 * Validate device-specific size wNNNhNNN should have proper width and
3185 * length...
3186 */
3187
3188 if (fabs(width - size->width) >= 1.0 ||
3189 fabs(length - size->length) >= 1.0)
3190 {
3191 if (!warn && !errors && !verbose)
0837b7e8 3192 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
3193
3194 if (verbose >= 0)
3195 _cupsLangPrintf(stdout,
3196 _(" %s Size \"%s\" has unexpected dimensions "
0837b7e8 3197 "(%gx%g)."),
1340db2d
MS
3198 prefix, size->name, size->width, size->length);
3199
3200 if (!warn)
3201 errors ++;
3202 }
3203 }
1340db2d
MS
3204
3205 /*
3206 * Verify that the size is defined for both PageSize and PageRegion...
3207 */
3208
3209 if (warn != 2 && !ppdFindChoice(page_size, size->name))
3210 {
3211 if (!warn && !errors && !verbose)
0837b7e8 3212 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
3213
3214 if (verbose >= 0)
3215 _cupsLangPrintf(stdout,
3216 _(" %s Size \"%s\" defined for %s but not for "
0837b7e8 3217 "%s."),
1340db2d
MS
3218 prefix, size->name, "PageRegion", "PageSize");
3219
3220 if (!warn)
3221 errors ++;
3222 }
3223 else if (warn != 2 && !ppdFindChoice(page_region, size->name))
3224 {
3225 if (!warn && !errors && !verbose)
0837b7e8 3226 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
3227
3228 if (verbose >= 0)
3229 _cupsLangPrintf(stdout,
3230 _(" %s Size \"%s\" defined for %s but not for "
0837b7e8 3231 "%s."),
1340db2d
MS
3232 prefix, size->name, "PageSize", "PageRegion");
3233
3234 if (!warn)
3235 errors ++;
3236 }
22c9029b
MS
3237
3238 /*
3239 * Verify that the size name is Adobe standard name if it's a standard size
d7225fc2 3240 * and the dimentional name if it's not a standard size. Suffix should be
22c9029b
MS
3241 * .Fullbleed, etc., or numeric, e.g., Letter, Letter.Fullbleed,
3242 * Letter.Transverse, Letter1, Letter2, 4x8, 55x91mm, 55x91mm.Fullbleed, etc.
3243 */
3244
3245 if (warn != 0)
3246 {
3247 is_ok = 1;
3248 width_2540ths = (size->length > size->width) ?
6961465f
MS
3249 PWG_FROM_POINTS(size->width) :
3250 PWG_FROM_POINTS(size->length);
22c9029b 3251 length_2540ths = (size->length > size->width) ?
6961465f
MS
3252 PWG_FROM_POINTS(size->length) :
3253 PWG_FROM_POINTS(size->width);
94436c5a 3254 pwg_media = pwgMediaForSize(width_2540ths, length_2540ths);
22c9029b 3255
c1420c87 3256 if (pwg_media &&
6884f8da
MS
3257 (abs(pwg_media->width - width_2540ths) > 34 ||
3258 abs(pwg_media->length - length_2540ths) > 34))
c1420c87
MS
3259 pwg_media = NULL; /* Only flag matches within a point */
3260
a469f8a5
MS
3261 if (pwg_media && pwg_media->ppd &&
3262 (pwg_media->ppd[0] < 'a' || pwg_media->ppd[0] > 'z'))
22c9029b 3263 {
d7225fc2
MS
3264 size_t ppdlen = strlen(pwg_media->ppd);
3265 /* Length of standard PPD name */
3266
22c9029b
MS
3267 strlcpy(buf, pwg_media->ppd, sizeof(buf));
3268
f3c17241
MS
3269 if (strcmp(size->name, buf) && size->width > size->length)
3270 {
3271 if (!strcmp(pwg_media->ppd, "DoublePostcardRotated"))
3272 strlcpy(buf, "DoublePostcard", sizeof(buf));
3273 else if (strstr(size->name, ".Transverse"))
3274 snprintf(buf, sizeof(buf), "%s.Transverse", pwg_media->ppd);
3275 else
3276 snprintf(buf, sizeof(buf), "%sRotated", pwg_media->ppd);
c1420c87
MS
3277
3278 ppdlen = strlen(buf);
f3c17241
MS
3279 }
3280
22c9029b
MS
3281 if (size->left == 0 && size->bottom == 0 &&
3282 size->right == size->width && size->top == size->length)
3283 {
f3c17241 3284 strlcat(buf, ".Fullbleed", sizeof(buf) - strlen(buf));
d7225fc2 3285 if (_cups_strcasecmp(size->name, buf))
22c9029b 3286 {
d7225fc2
MS
3287 /*
3288 * Allow an additional qualifier such as ".WithTab"...
3289 */
3290
3291 size_t buflen = strlen(buf);/* Length of full bleed name */
3292
3293 if (_cups_strncasecmp(size->name, buf, buflen) ||
3294 size->name[buflen] != '.')
22c9029b
MS
3295 is_ok = 0;
3296 }
d7225fc2 3297 }
d7225fc2
MS
3298 else if (!strncmp(size->name, pwg_media->ppd, ppdlen))
3299 {
3300 /*
3301 * Check for a proper qualifier (number, "Small", or .something)...
3302 */
3303
3304 ptr = size->name + ppdlen;
3305
3306 if (isdigit(*ptr & 255))
22c9029b 3307 {
d7225fc2 3308 for (ptr ++; *ptr; ptr ++)
22c9029b
MS
3309 {
3310 if (!isdigit(*ptr & 255))
3311 {
3312 is_ok = 0;
3313 break;
3314 }
3315 }
3316 }
d7225fc2
MS
3317 else if (*ptr != '.' && *ptr && strcmp(ptr, "Small"))
3318 is_ok = 0;
22c9029b 3319 }
d7225fc2
MS
3320 else
3321 {
3322 /*
3323 * Check for EnvSizeName as well...
3324 */
3325
a469f8a5
MS
3326 if (strncmp(pwg_media->ppd, "Env", 3) &&
3327 !strncmp(size->name, "Env", 3))
82cc1f9a 3328 snprintf(buf, sizeof(buf), "Env%s", pwg_media->ppd);
d7225fc2
MS
3329
3330 if (strcmp(size->name, buf))
3331 is_ok = 0;
3332 }
88f9aafc 3333
22c9029b
MS
3334 if (!is_ok)
3335 _cupsLangPrintf(stdout,
3336 _(" %s Size \"%s\" should be the Adobe "
3337 "standard name \"%s\"."),
3338 prefix, size->name, buf);
3339 }
3340 else
3341 {
3342 width_tmp = (fabs(size->width - ceil(size->width)) < 0.1) ?
3343 ceil(size->width) : size->width;
3344 length_tmp = (fabs(size->length - ceil(size->length)) < 0.1) ?
3345 ceil(size->length) : size->length;
3346
c1420c87 3347 if (fmod(width_tmp, 9.0) == 0.0 && fmod(length_tmp, 9.0) == 0.0)
22c9029b
MS
3348 {
3349 width_inch = width_tmp / 72.0;
3350 length_inch = length_tmp / 72.0;
3351
3352 snprintf(buf, sizeof(buf), "%gx%g", width_inch, length_inch);
3353 }
3354 else
3355 {
3356 width_mm = size->width / 72.0 * 25.4;
3357 length_mm = size->length / 72.0 * 25.4;
3358
3359 snprintf(buf, sizeof(buf), "%.0fx%.0fmm", width_mm, length_mm);
3360 }
3361
3362 if (size->left == 0 && size->bottom == 0 &&
3363 size->right == size->width && size->top == size->length)
3364 strlcat(buf, ".Fullbleed", sizeof(buf));
3365 else if (size->width > size->length)
3366 strlcat(buf, ".Transverse", sizeof(buf));
3367
d7225fc2
MS
3368 if (_cups_strcasecmp(size->name, buf))
3369 {
c1420c87
MS
3370 size_t buflen = strlen(buf);
3371 /* Length of proposed name */
d7225fc2
MS
3372
3373 if (_cups_strncasecmp(size->name, buf, buflen) ||
f3c17241
MS
3374 (strcmp(size->name + buflen, "in") &&
3375 size->name[buflen] != '.'))
c1420c87
MS
3376 {
3377 char altbuf[PPD_MAX_NAME];
3378 /* Alternate "wNNNhNNN" name */
3379 size_t altlen; /* Length of alternate name */
3380
3381 snprintf(altbuf, sizeof(altbuf), "w%.0fh%.0f", size->width,
3382 size->length);
3383 altlen = strlen(altbuf);
3384 if (_cups_strncasecmp(size->name, altbuf, altlen) ||
3385 (size->name[altlen] && size->name[altlen] != '.'))
3386 _cupsLangPrintf(stdout,
3387 _(" %s Size \"%s\" should be \"%s\"."),
3388 prefix, size->name, buf);
3389 }
d7225fc2 3390 }
22c9029b
MS
3391 }
3392 }
1340db2d
MS
3393 }
3394
3395 return (errors);
3396}
3397
3398
b94498cf 3399/*
3400 * 'check_translations()' - Check translations in the PPD file.
3401 */
3402
3d8365b8 3403static int /* O - Errors found */
b94498cf 3404check_translations(ppd_file_t *ppd, /* I - PPD file */
3405 int errors, /* I - Errors found */
3406 int verbose, /* I - Verbosity level */
3407 int warn) /* I - Warnings only? */
3408{
3409 int j; /* Looping var */
3410 ppd_attr_t *attr; /* PPD attribute */
a603edef
MS
3411 cups_array_t *languages; /* Array of languages */
3412 int langlen; /* Length of language */
3413 char *language, /* Current language */
b94498cf 3414 keyword[PPD_MAX_NAME], /* Localization keyword (full) */
3415 llkeyword[PPD_MAX_NAME],/* Localization keyword (base) */
3416 ckeyword[PPD_MAX_NAME], /* Custom option keyword (full) */
3417 cllkeyword[PPD_MAX_NAME];
3418 /* Custom option keyword (base) */
3419 ppd_option_t *option; /* Standard UI option */
3420 ppd_coption_t *coption; /* Custom option */
3421 ppd_cparam_t *cparam; /* Custom parameter */
b94498cf 3422 char ll[3]; /* Base language */
3423 const char *prefix; /* WARN/FAIL prefix */
749b1e90 3424 const char *text; /* Pointer into UI text */
b94498cf 3425
3426
3427 prefix = warn ? " WARN " : "**FAIL**";
3428
a603edef 3429 if ((languages = _ppdGetLanguages(ppd)) != NULL)
b94498cf 3430 {
3431 /*
3432 * This file contains localizations, check them...
3433 */
3434
a603edef
MS
3435 for (language = (char *)cupsArrayFirst(languages);
3436 language;
3437 language = (char *)cupsArrayNext(languages))
b94498cf 3438 {
7a0cbd5e 3439 langlen = (int)strlen(language);
a603edef 3440 if (langlen != 2 && langlen != 5)
b94498cf 3441 {
3442 if (!warn && !errors && !verbose)
0837b7e8 3443 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3444
3445 if (verbose >= 0)
3446 _cupsLangPrintf(stdout,
0837b7e8 3447 _(" %s Bad language \"%s\"."),
a603edef 3448 prefix, language);
b94498cf 3449
3450 if (!warn)
3451 errors ++;
3452
3453 continue;
3454 }
3455
a603edef 3456 if (!strcmp(language, "en"))
0a682745
MS
3457 continue;
3458
a603edef 3459 strlcpy(ll, language, sizeof(ll));
b94498cf 3460
3461 /*
3462 * Loop through all options and choices...
3463 */
3464
3465 for (option = ppdFirstOption(ppd);
3466 option;
3467 option = ppdNextOption(ppd))
3468 {
09a101d6 3469 if (!strcmp(option->keyword, "PageRegion"))
3470 continue;
3471
a603edef 3472 snprintf(keyword, sizeof(keyword), "%s.Translation", language);
b94498cf 3473 snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll);
3474
3475 if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL &&
3476 (attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL)
3477 {
3478 if (!warn && !errors && !verbose)
0837b7e8 3479 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3480
3481 if (verbose >= 0)
3482 _cupsLangPrintf(stdout,
3483 _(" %s Missing \"%s\" translation "
0837b7e8 3484 "string for option %s."),
a603edef 3485 prefix, language, option->keyword);
b94498cf 3486
3487 if (!warn)
3488 errors ++;
3489 }
3490 else if (!valid_utf8(attr->text))
3491 {
3492 if (!warn && !errors && !verbose)
0837b7e8 3493 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3494
3495 if (verbose >= 0)
3496 _cupsLangPrintf(stdout,
3497 _(" %s Bad UTF-8 \"%s\" translation "
0837b7e8 3498 "string for option %s."),
a603edef 3499 prefix, language, option->keyword);
b94498cf 3500
3501 if (!warn)
3502 errors ++;
3503 }
3504
a603edef 3505 snprintf(keyword, sizeof(keyword), "%s.%s", language,
b94498cf 3506 option->keyword);
3507 snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll,
3508 option->keyword);
3509
3510 for (j = 0; j < option->num_choices; j ++)
3511 {
749b1e90
MS
3512 /*
3513 * First see if this choice is a number; if so, don't require
3514 * translation...
3515 */
3516
3517 for (text = option->choices[j].text; *text; text ++)
3518 if (!strchr("0123456789-+.", *text))
3519 break;
3520
3521 if (!*text)
3522 continue;
3523
3524 /*
3525 * Check custom choices differently...
3526 */
3527
88f9aafc 3528 if (!_cups_strcasecmp(option->choices[j].choice, "Custom") &&
b94498cf 3529 (coption = ppdFindCustomOption(ppd,
3530 option->keyword)) != NULL)
3531 {
3532 snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
a603edef 3533 language, option->keyword);
b94498cf 3534
3535 if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL &&
3536 !valid_utf8(attr->text))
3537 {
3538 if (!warn && !errors && !verbose)
0837b7e8 3539 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3540
3541 if (verbose >= 0)
3542 _cupsLangPrintf(stdout,
3543 _(" %s Bad UTF-8 \"%s\" "
3544 "translation string for option %s, "
0837b7e8 3545 "choice %s."),
a603edef
MS
3546 prefix, language,
3547 ckeyword + 1 + strlen(language),
b94498cf 3548 "True");
3549
3550 if (!warn)
3551 errors ++;
3552 }
3553
88f9aafc 3554 if (_cups_strcasecmp(option->keyword, "PageSize"))
b94498cf 3555 {
3556 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
3557 cparam;
3558 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
3559 {
3560 snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
a603edef 3561 language, option->keyword);
b94498cf 3562 snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s",
3563 ll, option->keyword);
3564
3565 if ((attr = ppdFindAttr(ppd, ckeyword,
3566 cparam->name)) == NULL &&
3567 (attr = ppdFindAttr(ppd, cllkeyword,
3568 cparam->name)) == NULL)
3569 {
3570 if (!warn && !errors && !verbose)
0837b7e8 3571 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3572
3573 if (verbose >= 0)
3574 _cupsLangPrintf(stdout,
3575 _(" %s Missing \"%s\" "
3576 "translation string for option %s, "
0837b7e8 3577 "choice %s."),
a603edef
MS
3578 prefix, language,
3579 ckeyword + 1 + strlen(language),
b94498cf 3580 cparam->name);
3581
3582 if (!warn)
3583 errors ++;
3584 }
3585 else if (!valid_utf8(attr->text))
3586 {
3587 if (!warn && !errors && !verbose)
0837b7e8 3588 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3589
3590 if (verbose >= 0)
3591 _cupsLangPrintf(stdout,
3592 _(" %s Bad UTF-8 \"%s\" "
3593 "translation string for option %s, "
0837b7e8 3594 "choice %s."),
a603edef
MS
3595 prefix, language,
3596 ckeyword + 1 + strlen(language),
b94498cf 3597 cparam->name);
3598
3599 if (!warn)
3600 errors ++;
3601 }
3602 }
3603 }
3604 }
3605 else if ((attr = ppdFindAttr(ppd, keyword,
3606 option->choices[j].choice)) == NULL &&
3607 (attr = ppdFindAttr(ppd, llkeyword,
3608 option->choices[j].choice)) == NULL)
3609 {
3610 if (!warn && !errors && !verbose)
0837b7e8 3611 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3612
3613 if (verbose >= 0)
3614 _cupsLangPrintf(stdout,
3615 _(" %s Missing \"%s\" "
3616 "translation string for option %s, "
0837b7e8 3617 "choice %s."),
a603edef 3618 prefix, language, option->keyword,
b94498cf 3619 option->choices[j].choice);
3620
3621 if (!warn)
3622 errors ++;
3623 }
3624 else if (!valid_utf8(attr->text))
3625 {
3626 if (!warn && !errors && !verbose)
0837b7e8 3627 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3628
3629 if (verbose >= 0)
3630 _cupsLangPrintf(stdout,
3631 _(" %s Bad UTF-8 \"%s\" "
3632 "translation string for option %s, "
0837b7e8 3633 "choice %s."),
a603edef 3634 prefix, language, option->keyword,
b94498cf 3635 option->choices[j].choice);
3636
3637 if (!warn)
3638 errors ++;
3639 }
3640 }
3641 }
3642 }
3643
3644 /*
3645 * Verify that we have the base language for each localized one...
3646 */
3647
a603edef
MS
3648 for (language = (char *)cupsArrayFirst(languages);
3649 language;
3650 language = (char *)cupsArrayNext(languages))
3651 if (language[2])
b94498cf 3652 {
3653 /*
3654 * Lookup the base language...
3655 */
3656
a603edef 3657 cupsArraySave(languages);
b94498cf 3658
a603edef 3659 strlcpy(ll, language, sizeof(ll));
b94498cf 3660
005dd1eb
MS
3661 if (!cupsArrayFind(languages, ll) &&
3662 strcmp(ll, "zh") && strcmp(ll, "en"))
b94498cf 3663 {
3664 if (!warn && !errors && !verbose)
0837b7e8 3665 _cupsLangPuts(stdout, _(" FAIL"));
b94498cf 3666
3667 if (verbose >= 0)
3668 _cupsLangPrintf(stdout,
3669 _(" %s No base translation \"%s\" "
0837b7e8 3670 "is included in file."), prefix, ll);
b94498cf 3671
3672 if (!warn)
3673 errors ++;
3674 }
3675
a603edef 3676 cupsArrayRestore(languages);
b94498cf 3677 }
3678
3679 /*
3680 * Free memory used for the languages...
3681 */
3682
a603edef 3683 _ppdFreeLanguages(languages);
b94498cf 3684 }
3685
3686 return (errors);
3687}
3688
3689
ef416fc2 3690/*
3691 * 'show_conflicts()' - Show option conflicts in a PPD file.
3692 */
3693
3d8365b8 3694static void
22c9029b
MS
3695show_conflicts(ppd_file_t *ppd, /* I - PPD to check */
3696 const char *prefix) /* I - Prefix string */
ef416fc2 3697{
3698 int i, j; /* Looping variables */
3699 ppd_const_t *c; /* Current constraint */
3700 ppd_option_t *o1, *o2; /* Options */
3701 ppd_choice_t *c1, *c2; /* Choices */
3702
3703
3704 /*
3705 * Loop through all of the UI constraints and report any options
3706 * that conflict...
3707 */
3708
3709 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
3710 {
3711 /*
3712 * Grab pointers to the first option...
3713 */
3714
3715 o1 = ppdFindOption(ppd, c->option1);
3716
3717 if (o1 == NULL)
3718 continue;
3719 else if (c->choice1[0] != '\0')
3720 {
3721 /*
3722 * This constraint maps to a specific choice.
3723 */
3724
3725 c1 = ppdFindChoice(o1, c->choice1);
3726 }
3727 else
3728 {
3729 /*
3730 * This constraint applies to any choice for this option.
3731 */
3732
3733 for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
3734 if (c1->marked)
3735 break;
3736
3737 if (j == 0 ||
88f9aafc
MS
3738 !_cups_strcasecmp(c1->choice, "None") ||
3739 !_cups_strcasecmp(c1->choice, "Off") ||
3740 !_cups_strcasecmp(c1->choice, "False"))
ef416fc2 3741 c1 = NULL;
3742 }
3743
3744 /*
3745 * Grab pointers to the second option...
3746 */
3747
3748 o2 = ppdFindOption(ppd, c->option2);
3749
3750 if (o2 == NULL)
3751 continue;
3752 else if (c->choice2[0] != '\0')
3753 {
3754 /*
3755 * This constraint maps to a specific choice.
3756 */
3757
3758 c2 = ppdFindChoice(o2, c->choice2);
3759 }
3760 else
3761 {
3762 /*
3763 * This constraint applies to any choice for this option.
3764 */
3765
3766 for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
3767 if (c2->marked)
3768 break;
3769
3770 if (j == 0 ||
88f9aafc
MS
3771 !_cups_strcasecmp(c2->choice, "None") ||
3772 !_cups_strcasecmp(c2->choice, "Off") ||
3773 !_cups_strcasecmp(c2->choice, "False"))
ef416fc2 3774 c2 = NULL;
3775 }
3776
3777 /*
3778 * If both options are marked then there is a conflict...
3779 */
3780
3781 if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
fa73b229 3782 _cupsLangPrintf(stdout,
22c9029b 3783 _(" %s \"%s %s\" conflicts with \"%s %s\"\n"
0837b7e8 3784 " (constraint=\"%s %s %s %s\")."),
22c9029b 3785 prefix, o1->keyword, c1->choice, o2->keyword, c2->choice,
ef416fc2 3786 c->option1, c->choice1, c->option2, c->choice2);
3787 }
3788}
3789
3790
09a101d6 3791/*
3792 * 'test_raster()' - Test PostScript commands for raster printers.
3793 */
3794
3795static int /* O - 1 on success, 0 on failure */
3796test_raster(ppd_file_t *ppd, /* I - PPD file */
3797 int verbose) /* I - Verbosity */
3798{
3799 cups_page_header2_t header; /* Page header */
3800
3801
3802 ppdMarkDefaults(ppd);
3803 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
3804 {
3805 if (!verbose)
0837b7e8 3806 _cupsLangPuts(stdout, _(" FAIL"));
09a101d6 3807
3808 if (verbose >= 0)
3809 _cupsLangPrintf(stdout,
3810 _(" **FAIL** Default option code cannot be "
0837b7e8 3811 "interpreted: %s"), cupsRasterErrorString());
09a101d6 3812
3813 return (0);
3814 }
db1f069b
MS
3815
3816 /*
3817 * Try a test of custom page size code, if available...
3818 */
3819
3820 if (!ppdPageSize(ppd, "Custom.612x792"))
09a101d6 3821 return (1);
db1f069b
MS
3822
3823 ppdMarkOption(ppd, "PageSize", "Custom.612x792");
3824
3825 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
3826 {
3827 if (!verbose)
0837b7e8 3828 _cupsLangPuts(stdout, _(" FAIL"));
db1f069b
MS
3829
3830 if (verbose >= 0)
3831 _cupsLangPrintf(stdout,
3832 _(" **FAIL** Default option code cannot be "
0837b7e8 3833 "interpreted: %s"), cupsRasterErrorString());
db1f069b
MS
3834
3835 return (0);
3836 }
3837
3838 return (1);
09a101d6 3839}
3840
3841
ef416fc2 3842/*
22c9029b 3843 * 'usage()' - Show program usage.
ef416fc2 3844 */
3845
3d8365b8 3846static void
ef416fc2 3847usage(void)
3848{
0837b7e8
MS
3849 _cupsLangPuts(stdout, _("Usage: cupstestppd [options] filename1.ppd[.gz] "
3850 "[... filenameN.ppd[.gz]]"));
3851 _cupsLangPuts(stdout, _(" program | cupstestppd [options] -"));
3852 _cupsLangPuts(stdout, "");
3853 _cupsLangPuts(stdout, _("Options:"));
3854 _cupsLangPuts(stdout, "");
84315f46
MS
3855 _cupsLangPuts(stdout, _(" -I {filename,filters,none,profiles}"));
3856 _cupsLangPuts(stdout, _(" Ignore specific warnings."));
3857 _cupsLangPuts(stdout, _(" -R root-directory Set alternate root."));
3858 _cupsLangPuts(stdout, _(" -W {all,none,constraints,defaults,duplex,"
0837b7e8 3859 "filters,profiles,sizes,translations}"));
84315f46 3860 _cupsLangPuts(stdout, _(" Issue warnings instead of "
0837b7e8 3861 "errors."));
84315f46
MS
3862 _cupsLangPuts(stdout, _(" -q Run silently."));
3863 _cupsLangPuts(stdout, _(" -r Use 'relaxed' open mode."));
f3c17241 3864 _cupsLangPuts(stdout, _(" -v Be verbose."));
84315f46 3865 _cupsLangPuts(stdout, _(" -vv Be very verbose."));
ef416fc2 3866
3867 exit(ERROR_USAGE);
3868}
3869
3870
1340db2d
MS
3871/*
3872 * 'valid_path()' - Check whether a path has the correct capitalization.
3873 */
3874
3875static int /* O - Errors found */
3876valid_path(const char *keyword, /* I - Keyword using path */
3877 const char *path, /* I - Path to check */
3878 int errors, /* I - Errors found */
3879 int verbose, /* I - Verbosity level */
3880 int warn) /* I - Warnings only? */
3881{
3882 cups_dir_t *dir; /* Current directory */
3883 cups_dentry_t *dentry; /* Current directory entry */
3884 char temp[1024], /* Temporary path */
3885 *ptr; /* Pointer into temporary path */
3886 const char *prefix; /* WARN/FAIL prefix */
3887
3888
3889 prefix = warn ? " WARN " : "**FAIL**";
3890
3891 /*
3892 * Loop over the components of the path, checking that the entry exists with
3893 * the same capitalization...
3894 */
3895
3896 strlcpy(temp, path, sizeof(temp));
3897
3898 while ((ptr = strrchr(temp, '/')) != NULL)
3899 {
3900 /*
3901 * Chop off the trailing component so temp == dirname and ptr == basename.
3902 */
3903
3904 *ptr++ = '\0';
3905
3906 /*
3907 * Try opening the directory containing the base name...
3908 */
3909
3910 if (temp[0])
3911 dir = cupsDirOpen(temp);
3912 else
3913 dir = cupsDirOpen("/");
3914
3915 if (!dir)
3916 dentry = NULL;
3917 else
3918 {
3919 while ((dentry = cupsDirRead(dir)) != NULL)
3920 {
3921 if (!strcmp(dentry->filename, ptr))
3922 break;
3923 }
3924
3925 cupsDirClose(dir);
3926 }
3927
3928 /*
3929 * Display an error if the filename doesn't exist with the same
3930 * capitalization...
3931 */
3932
3933 if (!dentry)
3934 {
3935 if (!warn && !errors && !verbose)
0837b7e8 3936 _cupsLangPuts(stdout, _(" FAIL"));
1340db2d
MS
3937
3938 if (verbose >= 0)
3939 _cupsLangPrintf(stdout,
3940 _(" %s %s file \"%s\" has the wrong "
0837b7e8 3941 "capitalization."), prefix, keyword, path);
1340db2d
MS
3942
3943 if (!warn)
3944 errors ++;
3945
3946 break;
3947 }
3948 }
3949
3950 return (errors);
3951}
3952
3953
ef416fc2 3954/*
f7deaa1a 3955 * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
3956 */
3957
3d8365b8 3958static int /* O - 1 if valid, 0 if not */
f7deaa1a 3959valid_utf8(const char *s) /* I - String to check */
3960{
3961 while (*s)
3962 {
3963 if (*s & 0x80)
3964 {
3965 /*
3966 * Check for valid UTF-8 sequence...
3967 */
3968
3969 if ((*s & 0xc0) == 0x80)
3970 return (0); /* Illegal suffix byte */
3971 else if ((*s & 0xe0) == 0xc0)
3972 {
3973 /*
3974 * 2-byte sequence...
3975 */
3976
3977 s ++;
3978
3979 if ((*s & 0xc0) != 0x80)
3980 return (0); /* Missing suffix byte */
3981 }
3982 else if ((*s & 0xf0) == 0xe0)
3983 {
3984 /*
3985 * 3-byte sequence...
3986 */
3987
3988 s ++;
3989
3990 if ((*s & 0xc0) != 0x80)
3991 return (0); /* Missing suffix byte */
3992
3993 s ++;
3994
3995 if ((*s & 0xc0) != 0x80)
3996 return (0); /* Missing suffix byte */
3997 }
3998 else if ((*s & 0xf8) == 0xf0)
3999 {
4000 /*
4001 * 4-byte sequence...
4002 */
4003
4004 s ++;
4005
4006 if ((*s & 0xc0) != 0x80)
4007 return (0); /* Missing suffix byte */
4008
4009 s ++;
4010
4011 if ((*s & 0xc0) != 0x80)
4012 return (0); /* Missing suffix byte */
4013
4014 s ++;
4015
4016 if ((*s & 0xc0) != 0x80)
4017 return (0); /* Missing suffix byte */
4018 }
4019 else
4020 return (0); /* Bad sequence */
4021 }
4022
4023 s ++;
4024 }
4025
4026 return (1);
4027}