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