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