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