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