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