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