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