]> git.ipfire.org Git - thirdparty/cups.git/blob - systemv/cupstestppd.c
Merge changes from CUPS 1.4svn-r7932.
[thirdparty/cups.git] / systemv / cupstestppd.c
1 /*
2 * "$Id: cupstestppd.c 7637 2008-06-11 17:25:36Z mike $"
3 *
4 * PPD test program for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 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_case() - Check that there are no duplicate groups, options,
25 * or choices that differ only by case.
26 * check_constraints() - Check UIConstraints in the PPD file.
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_translations() - Check translations in the PPD file.
32 * show_conflicts() - Show option conflicts in a PPD file.
33 * test_raster() - Test PostScript commands for raster printers.
34 * usage() - Show program usage...
35 * valid_utf8() - Check whether a string contains valid UTF-8 text.
36 */
37
38 /*
39 * Include necessary headers...
40 */
41
42 #include <cups/string.h>
43 #include <cups/cups.h>
44 #include <cups/ppd-private.h>
45 #include <cups/i18n.h>
46 #include <cups/raster.h>
47 #include <errno.h>
48 #include <stdlib.h>
49 #include <sys/stat.h>
50
51
52 /*
53 * Error warning overrides...
54 */
55
56 enum
57 {
58 WARN_NONE = 0,
59 WARN_CONSTRAINTS = 1,
60 WARN_DEFAULTS = 2,
61 WARN_FILTERS = 4,
62 WARN_PROFILES = 8,
63 WARN_TRANSLATIONS = 16,
64 WARN_DUPLEX = 32,
65 WARN_ALL = 63
66 };
67
68
69 /*
70 * Error codes...
71 */
72
73 enum
74 {
75 ERROR_NONE = 0,
76 ERROR_USAGE,
77 ERROR_FILE_OPEN,
78 ERROR_PPD_FORMAT,
79 ERROR_CONFORMANCE
80 };
81
82
83 /*
84 * Line endings...
85 */
86
87 enum
88 {
89 EOL_NONE = 0,
90 EOL_CR,
91 EOL_LF,
92 EOL_CRLF
93 };
94
95
96 /*
97 * Local functions...
98 */
99
100 static void check_basics(const char *filename);
101 static int check_constraints(ppd_file_t *ppd, int errors, int verbose,
102 int warn);
103 static int check_case(ppd_file_t *ppd, int errors, int verbose);
104 static int check_defaults(ppd_file_t *ppd, int errors, int verbose,
105 int warn);
106 static int check_duplex(ppd_file_t *ppd, int errors, int verbose,
107 int warn);
108 static int check_filters(ppd_file_t *ppd, const char *root, int errors,
109 int verbose, int warn);
110 static int check_profiles(ppd_file_t *ppd, const char *root, int errors,
111 int verbose, int warn);
112 static int check_translations(ppd_file_t *ppd, int errors, int verbose,\
113 int warn);
114 static void show_conflicts(ppd_file_t *ppd);
115 static int test_raster(ppd_file_t *ppd, int verbose);
116 static void usage(void);
117 static int valid_utf8(const char *s);
118
119
120 /*
121 * 'main()' - Main entry for test program.
122 */
123
124 int /* O - Exit status */
125 main(int argc, /* I - Number of command-line args */
126 char *argv[]) /* I - Command-line arguments */
127 {
128 int i, j, k, m, n; /* Looping vars */
129 int len; /* Length of option name */
130 char *opt; /* Option character */
131 const char *ptr; /* Pointer into string */
132 int files; /* Number of files */
133 int verbose; /* Want verbose output? */
134 int warn; /* Which errors to just warn about */
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
173 for (i = 1; i < argc; i ++)
174 if (argv[i][0] == '-' && argv[i][1])
175 {
176 for (opt = argv[i] + 1; *opt; opt ++)
177 switch (*opt)
178 {
179 case 'R' : /* Alternate root directory */
180 i ++;
181
182 if (i >= argc)
183 usage();
184
185 root = argv[i];
186 break;
187
188 case 'W' : /* Turn errors into warnings */
189 i ++;
190
191 if (i >= argc)
192 usage();
193
194 if (!strcmp(argv[i], "none"))
195 warn = WARN_NONE;
196 else if (!strcmp(argv[i], "constraints"))
197 warn |= WARN_CONSTRAINTS;
198 else if (!strcmp(argv[i], "defaults"))
199 warn |= WARN_DEFAULTS;
200 else if (!strcmp(argv[i], "duplex"))
201 warn |= WARN_DUPLEX;
202 else if (!strcmp(argv[i], "filters"))
203 warn |= WARN_FILTERS;
204 else if (!strcmp(argv[i], "profiles"))
205 warn |= WARN_PROFILES;
206 else if (!strcmp(argv[i], "translations"))
207 warn |= WARN_TRANSLATIONS;
208 else if (!strcmp(argv[i], "all"))
209 warn = WARN_ALL;
210 else
211 usage();
212 break;
213
214 case 'q' : /* Quiet mode */
215 if (verbose > 0)
216 {
217 _cupsLangPuts(stderr,
218 _("cupstestppd: The -q option is incompatible "
219 "with the -v option.\n"));
220 return (1);
221 }
222
223 verbose --;
224 break;
225
226 case 'r' : /* Relaxed mode */
227 ppdSetConformance(PPD_CONFORM_RELAXED);
228 break;
229
230 case 'v' : /* Verbose mode */
231 if (verbose < 0)
232 {
233 _cupsLangPuts(stderr,
234 _("cupstestppd: The -v option is incompatible "
235 "with the -q option.\n"));
236 return (1);
237 }
238
239 verbose ++;
240 break;
241
242 default :
243 usage();
244 break;
245 }
246 }
247 else
248 {
249 /*
250 * Open the PPD file...
251 */
252
253 if (files && verbose >= 0)
254 _cupsLangPuts(stdout, "\n");
255
256 files ++;
257
258 if (argv[i][0] == '-')
259 {
260 /*
261 * Read from stdin...
262 */
263
264 ppd = ppdOpen(stdin);
265
266 if (verbose >= 0)
267 printf("%s:", (ppd && ppd->pcfilename) ? ppd->pcfilename : "(stdin)");
268 }
269 else
270 {
271 /*
272 * Read from a file...
273 */
274
275 if (verbose >= 0)
276 printf("%s:", argv[i]);
277
278 ppd = ppdOpenFile(argv[i]);
279 }
280
281 if (ppd == NULL)
282 {
283 error = ppdLastError(&line);
284
285 if (error <= PPD_ALLOC_ERROR)
286 {
287 status = ERROR_FILE_OPEN;
288
289 if (verbose >= 0)
290 _cupsLangPrintf(stdout,
291 _(" FAIL\n"
292 " **FAIL** Unable to open PPD file - %s\n"),
293 strerror(errno));
294 }
295 else
296 {
297 status = ERROR_PPD_FORMAT;
298
299 if (verbose >= 0)
300 {
301 _cupsLangPrintf(stdout,
302 _(" FAIL\n"
303 " **FAIL** Unable to open PPD file - "
304 "%s on line %d.\n"),
305 ppdErrorString(error), line);
306
307 switch (error)
308 {
309 case PPD_MISSING_PPDADOBE4 :
310 _cupsLangPuts(stdout,
311 _(" REF: Page 42, section 5.2.\n"));
312 break;
313 case PPD_MISSING_VALUE :
314 _cupsLangPuts(stdout,
315 _(" REF: Page 20, section 3.4.\n"));
316 break;
317 case PPD_BAD_OPEN_GROUP :
318 case PPD_NESTED_OPEN_GROUP :
319 _cupsLangPuts(stdout,
320 _(" REF: Pages 45-46, section 5.2.\n"));
321 break;
322 case PPD_BAD_OPEN_UI :
323 case PPD_NESTED_OPEN_UI :
324 _cupsLangPuts(stdout,
325 _(" REF: Pages 42-45, section 5.2.\n"));
326 break;
327 case PPD_BAD_ORDER_DEPENDENCY :
328 _cupsLangPuts(stdout,
329 _(" REF: Pages 48-49, section 5.2.\n"));
330 break;
331 case PPD_BAD_UI_CONSTRAINTS :
332 _cupsLangPuts(stdout,
333 _(" REF: Pages 52-54, section 5.2.\n"));
334 break;
335 case PPD_MISSING_ASTERISK :
336 _cupsLangPuts(stdout,
337 _(" REF: Page 15, section 3.2.\n"));
338 break;
339 case PPD_LINE_TOO_LONG :
340 _cupsLangPuts(stdout,
341 _(" REF: Page 15, section 3.1.\n"));
342 break;
343 case PPD_ILLEGAL_CHARACTER :
344 _cupsLangPuts(stdout,
345 _(" REF: Page 15, section 3.1.\n"));
346 break;
347 case PPD_ILLEGAL_MAIN_KEYWORD :
348 _cupsLangPuts(stdout,
349 _(" REF: Pages 16-17, section 3.2.\n"));
350 break;
351 case PPD_ILLEGAL_OPTION_KEYWORD :
352 _cupsLangPuts(stdout,
353 _(" REF: Page 19, section 3.3.\n"));
354 break;
355 case PPD_ILLEGAL_TRANSLATION :
356 _cupsLangPuts(stdout,
357 _(" REF: Page 27, section 3.5.\n"));
358 break;
359 default :
360 break;
361 }
362
363 check_basics(argv[i]);
364 }
365 }
366
367 continue;
368 }
369
370 /*
371 * Show the header and then perform basic conformance tests (limited
372 * only by what the CUPS PPD functions actually load...)
373 */
374
375 errors = 0;
376 ppdversion = 43;
377
378 if (verbose > 0)
379 _cupsLangPuts(stdout,
380 _("\n DETAILED CONFORMANCE TEST RESULTS\n"));
381
382 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
383 attr->value)
384 ppdversion = (int)(10 * _cupsStrScand(attr->value, NULL, loc) + 0.5);
385
386 for (j = 0; j < ppd->num_filters; j ++)
387 if (strstr(ppd->filters[j], "application/vnd.cups-raster"))
388 {
389 if (!test_raster(ppd, verbose))
390 errors ++;
391 break;
392 }
393
394 /*
395 * Look for default keywords with no matching option...
396 */
397
398 if (!(warn & WARN_DEFAULTS))
399 errors = check_defaults(ppd, errors, verbose, 0);
400
401 if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
402 {
403 if (verbose >= 0)
404 {
405 if (!errors && !verbose)
406 _cupsLangPuts(stdout, _(" FAIL\n"));
407
408 _cupsLangPuts(stdout,
409 _(" **FAIL** REQUIRED DefaultImageableArea\n"
410 " REF: Page 102, section 5.15.\n"));
411 }
412
413 errors ++;
414 }
415 else if (ppdPageSize(ppd, attr->value) == NULL &&
416 strcmp(attr->value, "Unknown"))
417 {
418 if (verbose >= 0)
419 {
420 if (!errors && !verbose)
421 _cupsLangPuts(stdout, _(" FAIL\n"));
422
423 _cupsLangPrintf(stdout,
424 _(" **FAIL** BAD DefaultImageableArea %s!\n"
425 " REF: Page 102, section 5.15.\n"),
426 attr->value);
427 }
428
429 errors ++;
430 }
431 else
432 {
433 if (verbose > 0)
434 _cupsLangPuts(stdout, _(" PASS DefaultImageableArea\n"));
435 }
436
437 if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
438 {
439 if (verbose >= 0)
440 {
441 if (!errors && !verbose)
442 _cupsLangPuts(stdout, _(" FAIL\n"));
443
444 _cupsLangPuts(stdout,
445 _(" **FAIL** REQUIRED DefaultPaperDimension\n"
446 " REF: Page 103, section 5.15.\n"));
447 }
448
449 errors ++;
450 }
451 else if (ppdPageSize(ppd, attr->value) == NULL &&
452 strcmp(attr->value, "Unknown"))
453 {
454 if (verbose >= 0)
455 {
456 if (!errors && !verbose)
457 _cupsLangPuts(stdout, _(" FAIL\n"));
458
459 _cupsLangPrintf(stdout,
460 _(" **FAIL** BAD DefaultPaperDimension %s!\n"
461 " REF: Page 103, section 5.15.\n"),
462 attr->value);
463 }
464
465 errors ++;
466 }
467 else if (verbose > 0)
468 _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension\n"));
469
470 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
471 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
472 {
473 /*
474 * Verify that we have a default choice...
475 */
476
477 if (option->defchoice[0])
478 {
479 if (ppdFindChoice(option, option->defchoice) == NULL &&
480 strcmp(option->defchoice, "Unknown"))
481 {
482 if (verbose >= 0)
483 {
484 if (!errors && !verbose)
485 _cupsLangPuts(stdout, _(" FAIL\n"));
486
487 _cupsLangPrintf(stdout,
488 _(" **FAIL** BAD Default%s %s\n"
489 " REF: Page 40, section 4.5.\n"),
490 option->keyword, option->defchoice);
491 }
492
493 errors ++;
494 }
495 else if (verbose > 0)
496 _cupsLangPrintf(stdout,
497 _(" PASS Default%s\n"),
498 option->keyword);
499 }
500 else
501 {
502 if (verbose >= 0)
503 {
504 if (!errors && !verbose)
505 _cupsLangPuts(stdout, _(" FAIL\n"));
506
507 _cupsLangPrintf(stdout,
508 _(" **FAIL** REQUIRED Default%s\n"
509 " REF: Page 40, section 4.5.\n"),
510 option->keyword);
511 }
512
513 errors ++;
514 }
515 }
516
517 if (ppdFindAttr(ppd, "FileVersion", NULL) != NULL)
518 {
519 if (verbose > 0)
520 _cupsLangPuts(stdout, _(" PASS FileVersion\n"));
521 }
522 else
523 {
524 if (verbose >= 0)
525 {
526 if (!errors && !verbose)
527 _cupsLangPuts(stdout, _(" FAIL\n"));
528
529 _cupsLangPuts(stdout,
530 _(" **FAIL** REQUIRED FileVersion\n"
531 " REF: Page 56, section 5.3.\n"));
532 }
533
534 errors ++;
535 }
536
537 if (ppdFindAttr(ppd, "FormatVersion", NULL) != NULL)
538 {
539 if (verbose > 0)
540 _cupsLangPuts(stdout, _(" PASS FormatVersion\n"));
541 }
542 else
543 {
544 if (verbose >= 0)
545 {
546 if (!errors && !verbose)
547 _cupsLangPuts(stdout, _(" FAIL\n"));
548
549 _cupsLangPuts(stdout,
550 _(" **FAIL** REQUIRED FormatVersion\n"
551 " REF: Page 56, section 5.3.\n"));
552 }
553
554 errors ++;
555 }
556
557 if (ppd->lang_encoding != NULL)
558 {
559 if (verbose > 0)
560 _cupsLangPuts(stdout, _(" PASS LanguageEncoding\n"));
561 }
562 else if (ppdversion > 40)
563 {
564 if (verbose >= 0)
565 {
566 if (!errors && !verbose)
567 _cupsLangPuts(stdout, _(" FAIL\n"));
568
569 _cupsLangPuts(stdout,
570 _(" **FAIL** REQUIRED LanguageEncoding\n"
571 " REF: Pages 56-57, section 5.3.\n"));
572 }
573
574 errors ++;
575 }
576
577 if (ppd->lang_version != NULL)
578 {
579 if (verbose > 0)
580 _cupsLangPuts(stdout, _(" PASS LanguageVersion\n"));
581 }
582 else
583 {
584 if (verbose >= 0)
585 {
586 if (!errors && !verbose)
587 _cupsLangPuts(stdout, _(" FAIL\n"));
588
589 _cupsLangPuts(stdout,
590 _(" **FAIL** REQUIRED LanguageVersion\n"
591 " REF: Pages 57-58, section 5.3.\n"));
592 }
593
594 errors ++;
595 }
596
597 if (ppd->manufacturer != NULL)
598 {
599 if (!strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) ||
600 !strncasecmp(ppd->manufacturer, "Hewlett Packard", 15))
601 {
602 if (verbose >= 0)
603 {
604 if (!errors && !verbose)
605 _cupsLangPuts(stdout, _(" FAIL\n"));
606
607 _cupsLangPuts(stdout,
608 _(" **FAIL** BAD Manufacturer (should be "
609 "\"HP\")\n"
610 " REF: Page 211, table D.1.\n"));
611 }
612
613 errors ++;
614 }
615 else if (!strncasecmp(ppd->manufacturer, "OkiData", 7) ||
616 !strncasecmp(ppd->manufacturer, "Oki Data", 8))
617 {
618 if (verbose >= 0)
619 {
620 if (!errors && !verbose)
621 _cupsLangPuts(stdout, _(" FAIL\n"));
622
623 _cupsLangPuts(stdout,
624 _(" **FAIL** BAD Manufacturer (should be "
625 "\"Oki\")\n"
626 " REF: Page 211, table D.1.\n"));
627 }
628
629 errors ++;
630 }
631 else if (verbose > 0)
632 _cupsLangPuts(stdout, _(" PASS Manufacturer\n"));
633 }
634 else if (ppdversion >= 43)
635 {
636 if (verbose >= 0)
637 {
638 if (!errors && !verbose)
639 _cupsLangPuts(stdout, _(" FAIL\n"));
640
641 _cupsLangPuts(stdout,
642 _(" **FAIL** REQUIRED Manufacturer\n"
643 " REF: Pages 58-59, section 5.3.\n"));
644 }
645
646 errors ++;
647 }
648
649 if (ppd->modelname != NULL)
650 {
651 for (ptr = ppd->modelname; *ptr; ptr ++)
652 if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr))
653 break;
654
655 if (*ptr)
656 {
657 if (verbose >= 0)
658 {
659 if (!errors && !verbose)
660 _cupsLangPuts(stdout, _(" FAIL\n"));
661
662 _cupsLangPrintf(stdout,
663 _(" **FAIL** BAD ModelName - \"%c\" not "
664 "allowed in string.\n"
665 " REF: Pages 59-60, section 5.3.\n"),
666 *ptr);
667 }
668
669 errors ++;
670 }
671 else if (verbose > 0)
672 _cupsLangPuts(stdout, _(" PASS ModelName\n"));
673 }
674 else
675 {
676 if (verbose >= 0)
677 {
678 if (!errors && !verbose)
679 _cupsLangPuts(stdout, _(" FAIL\n"));
680
681 _cupsLangPuts(stdout,
682 _(" **FAIL** REQUIRED ModelName\n"
683 " REF: Pages 59-60, section 5.3.\n"));
684 }
685
686 errors ++;
687 }
688
689 if (ppd->nickname != NULL)
690 {
691 if (verbose > 0)
692 _cupsLangPuts(stdout, _(" PASS NickName\n"));
693 }
694 else
695 {
696 if (verbose >= 0)
697 {
698 if (!errors && !verbose)
699 _cupsLangPuts(stdout, _(" FAIL\n"));
700
701 _cupsLangPuts(stdout,
702 _(" **FAIL** REQUIRED NickName\n"
703 " REF: Page 60, section 5.3.\n"));
704 }
705
706 errors ++;
707 }
708
709 if (ppdFindOption(ppd, "PageSize") != NULL)
710 {
711 if (verbose > 0)
712 _cupsLangPuts(stdout, _(" PASS PageSize\n"));
713 }
714 else
715 {
716 if (verbose >= 0)
717 {
718 if (!errors && !verbose)
719 _cupsLangPuts(stdout, _(" FAIL\n"));
720
721 _cupsLangPuts(stdout,
722 _(" **FAIL** REQUIRED PageSize\n"
723 " REF: Pages 99-100, section 5.14.\n"));
724 }
725
726 errors ++;
727 }
728
729 if (ppdFindOption(ppd, "PageRegion") != NULL)
730 {
731 if (verbose > 0)
732 _cupsLangPuts(stdout, _(" PASS PageRegion\n"));
733 }
734 else
735 {
736 if (verbose >= 0)
737 {
738 if (!errors && !verbose)
739 _cupsLangPuts(stdout, _(" FAIL\n"));
740
741 _cupsLangPuts(stdout,
742 _(" **FAIL** REQUIRED PageRegion\n"
743 " REF: Page 100, section 5.14.\n"));
744 }
745
746 errors ++;
747 }
748
749 if (ppd->pcfilename != NULL)
750 {
751 if (verbose > 0)
752 _cupsLangPuts(stdout, _(" PASS PCFileName\n"));
753 }
754 else
755 {
756 if (verbose >= 0)
757 {
758 if (!errors && !verbose)
759 _cupsLangPuts(stdout, _(" FAIL\n"));
760
761 _cupsLangPuts(stdout,
762 _(" **FAIL** REQUIRED PCFileName\n"
763 " REF: Pages 61-62, section 5.3.\n"));
764 }
765
766 errors ++;
767 }
768
769 if (ppd->product != NULL)
770 {
771 if (ppd->product[0] != '(' ||
772 ppd->product[strlen(ppd->product) - 1] != ')')
773 {
774 if (verbose >= 0)
775 {
776 if (!errors && !verbose)
777 _cupsLangPuts(stdout, _(" FAIL\n"));
778
779 _cupsLangPuts(stdout,
780 _(" **FAIL** BAD Product - not \"(string)\".\n"
781 " REF: Page 62, section 5.3.\n"));
782 }
783
784 errors ++;
785 }
786 else if (verbose > 0)
787 _cupsLangPuts(stdout, _(" PASS Product\n"));
788 }
789 else
790 {
791 if (verbose >= 0)
792 {
793 if (!errors && !verbose)
794 _cupsLangPuts(stdout, _(" FAIL\n"));
795
796 _cupsLangPuts(stdout,
797 _(" **FAIL** REQUIRED Product\n"
798 " REF: Page 62, section 5.3.\n"));
799 }
800
801 errors ++;
802 }
803
804 if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
805 attr->value != NULL)
806 {
807 char junkstr[255]; /* Temp string */
808 int junkint; /* Temp integer */
809
810
811 if (sscanf(attr->value, "(%[^)])%d", junkstr, &junkint) != 2)
812 {
813 if (verbose >= 0)
814 {
815 if (!errors && !verbose)
816 _cupsLangPuts(stdout, _(" FAIL\n"));
817
818 _cupsLangPuts(stdout,
819 _(" **FAIL** BAD PSVersion - not \"(string) "
820 "int\".\n"
821 " REF: Pages 62-64, section 5.3.\n"));
822 }
823
824 errors ++;
825 }
826 else if (verbose > 0)
827 _cupsLangPuts(stdout, _(" PASS PSVersion\n"));
828 }
829 else
830 {
831 if (verbose >= 0)
832 {
833 if (!errors && !verbose)
834 _cupsLangPuts(stdout, _(" FAIL\n"));
835
836 _cupsLangPuts(stdout,
837 _(" **FAIL** REQUIRED PSVersion\n"
838 " REF: Pages 62-64, section 5.3.\n"));
839 }
840
841 errors ++;
842 }
843
844 if (ppd->shortnickname != NULL)
845 {
846 if (strlen(ppd->shortnickname) > 31)
847 {
848 if (verbose >= 0)
849 {
850 if (!errors && !verbose)
851 _cupsLangPuts(stdout, _(" FAIL\n"));
852
853 _cupsLangPuts(stdout,
854 _(" **FAIL** BAD ShortNickName - longer "
855 "than 31 chars.\n"
856 " REF: Pages 64-65, section 5.3.\n"));
857 }
858
859 errors ++;
860 }
861 else if (verbose > 0)
862 _cupsLangPuts(stdout, _(" PASS ShortNickName\n"));
863 }
864 else if (ppdversion >= 43)
865 {
866 if (verbose >= 0)
867 {
868 if (!errors && !verbose)
869 _cupsLangPuts(stdout, _(" FAIL\n"));
870
871 _cupsLangPuts(stdout,
872 _(" **FAIL** REQUIRED ShortNickName\n"
873 " REF: Page 64-65, section 5.3.\n"));
874 }
875
876 errors ++;
877 }
878
879 if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
880 strstr(ppd->patches, "*End"))
881 {
882 if (verbose >= 0)
883 {
884 if (!errors && !verbose)
885 _cupsLangPuts(stdout, _(" FAIL\n"));
886
887 _cupsLangPuts(stdout,
888 _(" **FAIL** BAD JobPatchFile attribute in file\n"
889 " REF: Page 24, section 3.4.\n"));
890 }
891
892 errors ++;
893 }
894
895 /*
896 * Check for page sizes without the corresponding ImageableArea or
897 * PaperDimension values...
898 */
899
900 if (ppd->num_sizes == 0)
901 {
902 if (verbose >= 0)
903 {
904 if (!errors && !verbose)
905 _cupsLangPuts(stdout, _(" FAIL\n"));
906
907 _cupsLangPuts(stdout,
908 _(" **FAIL** REQUIRED PageSize\n"
909 " REF: Page 41, section 5.\n"
910 " REF: Page 99, section 5.14.\n"));
911 }
912
913 errors ++;
914 }
915 else
916 {
917 for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++)
918 {
919 /*
920 * Don't check custom size...
921 */
922
923 if (!strcmp(size->name, "Custom"))
924 continue;
925
926 /*
927 * Check for ImageableArea...
928 */
929
930 if (size->left == 0.0 && size->bottom == 0.0 &&
931 size->right == 0.0 && size->top == 0.0)
932 {
933 if (verbose >= 0)
934 {
935 if (!errors && !verbose)
936 _cupsLangPuts(stdout, _(" FAIL\n"));
937
938 _cupsLangPrintf(stdout,
939 _(" **FAIL** REQUIRED ImageableArea for "
940 "PageSize %s\n"
941 " REF: Page 41, section 5.\n"
942 " REF: Page 102, section 5.15.\n"),
943 size->name);
944 }
945
946 errors ++;
947 }
948
949 /*
950 * Check for PaperDimension...
951 */
952
953 if (size->width == 0.0 && size->length == 0.0)
954 {
955 if (verbose >= 0)
956 {
957 if (!errors && !verbose)
958 _cupsLangPuts(stdout, _(" FAIL\n"));
959
960 _cupsLangPrintf(stdout,
961 _(" **FAIL** REQUIRED PaperDimension "
962 "for PageSize %s\n"
963 " REF: Page 41, section 5.\n"
964 " REF: Page 103, section 5.15.\n"),
965 size->name);
966 }
967
968 errors ++;
969 }
970 }
971 }
972
973 /*
974 * Check for valid Resolution, JCLResolution, or SetResolution values...
975 */
976
977 if ((option = ppdFindOption(ppd, "Resolution")) == NULL)
978 if ((option = ppdFindOption(ppd, "JCLResolution")) == NULL)
979 option = ppdFindOption(ppd, "SetResolution");
980
981 if (option != NULL)
982 {
983 for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
984 {
985 /*
986 * Verify that all resolution options are of the form NNNdpi
987 * or NNNxNNNdpi...
988 */
989
990 xdpi = strtol(choice->choice, (char **)&ptr, 10);
991 if (ptr > choice->choice && xdpi > 0)
992 {
993 if (*ptr == 'x')
994 ydpi = strtol(ptr + 1, (char **)&ptr, 10);
995 else
996 ydpi = xdpi;
997 }
998 else
999 ydpi = xdpi;
1000
1001 if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 ||
1002 strcmp(ptr, "dpi"))
1003 {
1004 if (verbose >= 0)
1005 {
1006 if (!errors && !verbose)
1007 _cupsLangPuts(stdout, _(" FAIL\n"));
1008
1009 _cupsLangPrintf(stdout,
1010 _(" **FAIL** Bad %s choice %s!\n"
1011 " REF: Page 84, section 5.9\n"),
1012 option->keyword, choice->choice);
1013 }
1014
1015 errors ++;
1016 }
1017 }
1018 }
1019
1020 if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
1021 strcmp(attr->name, "1284DeviceID"))
1022 {
1023 if (verbose >= 0)
1024 {
1025 if (!errors && !verbose)
1026 _cupsLangPuts(stdout, _(" FAIL\n"));
1027
1028 _cupsLangPrintf(stdout,
1029 _(" **FAIL** %s must be 1284DeviceID!\n"
1030 " REF: Page 72, section 5.5\n"),
1031 attr->name);
1032 }
1033
1034 errors ++;
1035 }
1036
1037 errors = check_case(ppd, errors, verbose);
1038
1039 if (!(warn & WARN_CONSTRAINTS))
1040 errors = check_constraints(ppd, errors, verbose, 0);
1041
1042 if (!(warn & WARN_FILTERS))
1043 errors = check_filters(ppd, root, errors, verbose, 0);
1044
1045 if (!(warn & WARN_PROFILES))
1046 errors = check_profiles(ppd, root, errors, verbose, 0);
1047
1048 if (!(warn & WARN_TRANSLATIONS))
1049 errors = check_translations(ppd, errors, verbose, 0);
1050
1051 if (!(warn & WARN_DUPLEX))
1052 errors = check_duplex(ppd, errors, verbose, 0);
1053
1054 if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
1055 attr->value)
1056 {
1057 /*
1058 * This file contains localizations, check for conformance of the
1059 * base translation...
1060 */
1061
1062 if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL)
1063 {
1064 if (!attr->value || strcmp(attr->value, "ISOLatin1"))
1065 {
1066 if (!errors && !verbose)
1067 _cupsLangPuts(stdout, _(" FAIL\n"));
1068
1069 if (verbose >= 0)
1070 _cupsLangPrintf(stdout,
1071 _(" **FAIL** Bad LanguageEncoding %s - "
1072 "must be ISOLatin1!\n"),
1073 attr->value ? attr->value : "(null)");
1074
1075 errors ++;
1076 }
1077
1078 if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
1079 {
1080 if (!errors && !verbose)
1081 _cupsLangPuts(stdout, _(" FAIL\n"));
1082
1083 if (verbose >= 0)
1084 _cupsLangPrintf(stdout,
1085 _(" **FAIL** Bad LanguageVersion %s - "
1086 "must be English!\n"),
1087 ppd->lang_version ? ppd->lang_version : "(null)");
1088
1089 errors ++;
1090 }
1091
1092 /*
1093 * Loop through all options and choices...
1094 */
1095
1096 for (option = ppdFirstOption(ppd);
1097 option;
1098 option = ppdNextOption(ppd))
1099 {
1100 /*
1101 * Check for special characters outside A0 to BF, F7, or F8
1102 * that are used for languages other than English.
1103 */
1104
1105 for (ptr = option->text; *ptr; ptr ++)
1106 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1107 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1108 break;
1109
1110 if (*ptr)
1111 {
1112 if (!errors && !verbose)
1113 _cupsLangPuts(stdout, _(" FAIL\n"));
1114
1115 if (verbose >= 0)
1116 _cupsLangPrintf(stdout,
1117 _(" **FAIL** Default translation "
1118 "string for option %s contains 8-bit "
1119 "characters!\n"),
1120 option->keyword);
1121
1122 errors ++;
1123 }
1124
1125 for (j = 0; j < option->num_choices; j ++)
1126 {
1127 /*
1128 * Check for special characters outside A0 to BF, F7, or F8
1129 * that are used for languages other than English.
1130 */
1131
1132 for (ptr = option->choices[j].text; *ptr; ptr ++)
1133 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1134 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1135 break;
1136
1137 if (*ptr)
1138 {
1139 if (!errors && !verbose)
1140 _cupsLangPuts(stdout, _(" FAIL\n"));
1141
1142 if (verbose >= 0)
1143 _cupsLangPrintf(stdout,
1144 _(" **FAIL** Default translation "
1145 "string for option %s choice %s contains "
1146 "8-bit characters!\n"),
1147 option->keyword,
1148 option->choices[j].choice);
1149
1150 errors ++;
1151 }
1152 }
1153 }
1154 }
1155 }
1156
1157 /*
1158 * Final pass/fail notification...
1159 */
1160
1161 if (errors)
1162 status = ERROR_CONFORMANCE;
1163 else if (!verbose)
1164 _cupsLangPuts(stdout, _(" PASS\n"));
1165
1166 if (verbose >= 0)
1167 {
1168 check_basics(argv[i]);
1169
1170 if (warn & WARN_CONSTRAINTS)
1171 errors = check_constraints(ppd, errors, verbose, 1);
1172
1173 if (warn & WARN_DEFAULTS)
1174 errors = check_defaults(ppd, errors, verbose, 1);
1175
1176 if (warn & WARN_PROFILES)
1177 errors = check_profiles(ppd, root, errors, verbose, 1);
1178
1179 if (warn & WARN_FILTERS)
1180 errors = check_filters(ppd, root, errors, verbose, 1);
1181
1182 if (warn & WARN_TRANSLATIONS)
1183 errors = check_translations(ppd, errors, verbose, 1);
1184
1185 if (warn & WARN_DUPLEX)
1186 errors = check_duplex(ppd, errors, verbose, 1);
1187
1188 /*
1189 * Look for legacy duplex keywords...
1190 */
1191
1192 if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1193 if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
1194 option = ppdFindOption(ppd, "KD03Duplex");
1195
1196 if (option)
1197 _cupsLangPrintf(stdout,
1198 _(" WARN Duplex option keyword %s may not "
1199 "work as expected and should be named Duplex!\n"
1200 " REF: Page 122, section 5.17\n"),
1201 option->keyword);
1202
1203 /*
1204 * Look for default keywords with no corresponding option...
1205 */
1206
1207 for (j = 0; j < ppd->num_attrs; j ++)
1208 {
1209 attr = ppd->attrs[j];
1210
1211 if (!strcmp(attr->name, "DefaultColorSpace") ||
1212 !strcmp(attr->name, "DefaultColorSep") ||
1213 !strcmp(attr->name, "DefaultFont") ||
1214 !strcmp(attr->name, "DefaultHalftoneType") ||
1215 !strcmp(attr->name, "DefaultImageableArea") ||
1216 !strcmp(attr->name, "DefaultLeadingEdge") ||
1217 !strcmp(attr->name, "DefaultOutputOrder") ||
1218 !strcmp(attr->name, "DefaultPaperDimension") ||
1219 !strcmp(attr->name, "DefaultResolution") ||
1220 !strcmp(attr->name, "DefaultScreenProc") ||
1221 !strcmp(attr->name, "DefaultTransfer"))
1222 continue;
1223
1224 if (!strncmp(attr->name, "Default", 7) &&
1225 !ppdFindOption(ppd, attr->name + 7))
1226 _cupsLangPrintf(stdout,
1227 _(" WARN %s has no corresponding "
1228 "options!\n"),
1229 attr->name);
1230 }
1231
1232 ppdMarkDefaults(ppd);
1233 if (ppdConflicts(ppd))
1234 {
1235 _cupsLangPuts(stdout,
1236 _(" WARN Default choices conflicting!\n"));
1237
1238 show_conflicts(ppd);
1239 }
1240
1241 if (ppdversion < 43)
1242 {
1243 _cupsLangPrintf(stdout,
1244 _(" WARN Obsolete PPD version %.1f!\n"
1245 " REF: Page 42, section 5.2.\n"),
1246 0.1f * ppdversion);
1247 }
1248
1249 if (!ppd->lang_encoding && ppdversion < 41)
1250 {
1251 _cupsLangPuts(stdout,
1252 _(" WARN LanguageEncoding required by PPD "
1253 "4.3 spec.\n"
1254 " REF: Pages 56-57, section 5.3.\n"));
1255 }
1256
1257 if (!ppd->manufacturer && ppdversion < 43)
1258 {
1259 _cupsLangPuts(stdout,
1260 _(" WARN Manufacturer required by PPD "
1261 "4.3 spec.\n"
1262 " REF: Pages 58-59, section 5.3.\n"));
1263 }
1264
1265 /*
1266 * Treat a PCFileName attribute longer than 12 characters as
1267 * a warning and not a hard error...
1268 */
1269
1270 if (ppd->pcfilename && strlen(ppd->pcfilename) > 12)
1271 {
1272 _cupsLangPuts(stdout,
1273 _(" WARN PCFileName longer than 8.3 in "
1274 "violation of PPD spec.\n"
1275 " REF: Pages 61-62, section 5.3.\n"));
1276 }
1277
1278 if (!ppd->shortnickname && ppdversion < 43)
1279 {
1280 _cupsLangPuts(stdout,
1281 _(" WARN ShortNickName required by PPD "
1282 "4.3 spec.\n"
1283 " REF: Pages 64-65, section 5.3.\n"));
1284 }
1285
1286 /*
1287 * Check the Protocols line and flag PJL + BCP since TBCP is
1288 * usually used with PJL...
1289 */
1290
1291 if (ppd->protocols)
1292 {
1293 if (strstr(ppd->protocols, "PJL") &&
1294 strstr(ppd->protocols, "BCP") &&
1295 !strstr(ppd->protocols, "TBCP"))
1296 {
1297 _cupsLangPuts(stdout,
1298 _(" WARN Protocols contains both PJL "
1299 "and BCP; expected TBCP.\n"
1300 " REF: Pages 78-79, section 5.7.\n"));
1301 }
1302
1303 if (strstr(ppd->protocols, "PJL") &&
1304 (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
1305 {
1306 _cupsLangPuts(stdout,
1307 _(" WARN Protocols contains PJL but JCL "
1308 "attributes are not set.\n"
1309 " REF: Pages 78-79, section 5.7.\n"));
1310 }
1311 }
1312
1313 /*
1314 * Check for options with a common prefix, e.g. Duplex and Duplexer,
1315 * which are errors according to the spec but won't cause problems
1316 * with CUPS specifically...
1317 */
1318
1319 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1320 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1321 {
1322 len = strlen(option->keyword);
1323
1324 for (m = 0, group2 = ppd->groups;
1325 m < ppd->num_groups;
1326 m ++, group2 ++)
1327 for (n = 0, option2 = group2->options;
1328 n < group2->num_options;
1329 n ++, option2 ++)
1330 if (option != option2 &&
1331 len < strlen(option2->keyword) &&
1332 !strncmp(option->keyword, option2->keyword, len))
1333 {
1334 _cupsLangPrintf(stdout,
1335 _(" WARN %s shares a common "
1336 "prefix with %s\n"
1337 " REF: Page 15, section "
1338 "3.2.\n"),
1339 option->keyword, option2->keyword);
1340 }
1341 }
1342 }
1343
1344 #ifdef __APPLE__
1345 /*
1346 * APDialogExtension
1347 */
1348
1349 for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL);
1350 attr != NULL;
1351 attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
1352 {
1353 if ((!attr->value || access(attr->value, 0)) && verbose >= 0)
1354 _cupsLangPrintf(stdout, _(" WARN Missing "
1355 "APDialogExtension file \"%s\"\n"),
1356 attr->value ? attr->value : "<NULL>");
1357 }
1358
1359 /*
1360 * APPrinterIconPath
1361 */
1362
1363 for (attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL);
1364 attr != NULL;
1365 attr = ppdFindNextAttr(ppd, "APPrinterIconPath", NULL))
1366 {
1367 if ((!attr->value || access(attr->value, 0)) && verbose >= 0)
1368 _cupsLangPrintf(stdout, _(" WARN Missing "
1369 "APPrinterIconPath file \"%s\"\n"),
1370 attr->value ? attr->value : "<NULL>");
1371 }
1372 #endif /* __APPLE__ */
1373
1374 if (verbose > 0)
1375 {
1376 if (errors)
1377 _cupsLangPrintf(stdout, _(" %d ERRORS FOUND\n"), errors);
1378 else
1379 _cupsLangPuts(stdout, _(" NO ERRORS FOUND\n"));
1380 }
1381
1382 /*
1383 * Then list the options, if "-v" was provided...
1384 */
1385
1386 if (verbose > 1)
1387 {
1388 _cupsLangPrintf(stdout,
1389 "\n"
1390 " language_level = %d\n"
1391 " color_device = %s\n"
1392 " variable_sizes = %s\n"
1393 " landscape = %d\n",
1394 ppd->language_level,
1395 ppd->color_device ? "TRUE" : "FALSE",
1396 ppd->variable_sizes ? "TRUE" : "FALSE",
1397 ppd->landscape);
1398
1399 switch (ppd->colorspace)
1400 {
1401 case PPD_CS_CMYK :
1402 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK\n");
1403 break;
1404 case PPD_CS_CMY :
1405 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY\n");
1406 break;
1407 case PPD_CS_GRAY :
1408 _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY\n");
1409 break;
1410 case PPD_CS_RGB :
1411 _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB\n");
1412 break;
1413 default :
1414 _cupsLangPuts(stdout, " colorspace = <unknown>\n");
1415 break;
1416 }
1417
1418 _cupsLangPrintf(stdout, " num_emulations = %d\n",
1419 ppd->num_emulations);
1420 for (j = 0; j < ppd->num_emulations; j ++)
1421 _cupsLangPrintf(stdout, " emulations[%d] = %s\n",
1422 j, ppd->emulations[j].name);
1423
1424 _cupsLangPrintf(stdout, " lang_encoding = %s\n",
1425 ppd->lang_encoding);
1426 _cupsLangPrintf(stdout, " lang_version = %s\n",
1427 ppd->lang_version);
1428 _cupsLangPrintf(stdout, " modelname = %s\n", ppd->modelname);
1429 _cupsLangPrintf(stdout, " ttrasterizer = %s\n",
1430 ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
1431 _cupsLangPrintf(stdout, " manufacturer = %s\n",
1432 ppd->manufacturer);
1433 _cupsLangPrintf(stdout, " product = %s\n", ppd->product);
1434 _cupsLangPrintf(stdout, " nickname = %s\n", ppd->nickname);
1435 _cupsLangPrintf(stdout, " shortnickname = %s\n",
1436 ppd->shortnickname);
1437 _cupsLangPrintf(stdout, " patches = %d bytes\n",
1438 ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
1439
1440 _cupsLangPrintf(stdout, " num_groups = %d\n", ppd->num_groups);
1441 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1442 {
1443 _cupsLangPrintf(stdout, " group[%d] = %s\n",
1444 j, group->text);
1445
1446 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1447 {
1448 _cupsLangPrintf(stdout,
1449 " options[%d] = %s (%s) %s %s %.0f "
1450 "(%d choices)\n",
1451 k, option->keyword, option->text, uis[option->ui],
1452 sections[option->section], option->order,
1453 option->num_choices);
1454
1455 if (!strcmp(option->keyword, "PageSize") ||
1456 !strcmp(option->keyword, "PageRegion"))
1457 {
1458 for (m = option->num_choices, choice = option->choices;
1459 m > 0;
1460 m --, choice ++)
1461 {
1462 size = ppdPageSize(ppd, choice->choice);
1463
1464 if (size == NULL)
1465 _cupsLangPrintf(stdout,
1466 " %s (%s) = ERROR",
1467 choice->choice, choice->text);
1468 else
1469 _cupsLangPrintf(stdout,
1470 " %s (%s) = %.2fx%.2fin "
1471 "(%.1f,%.1f,%.1f,%.1f)",
1472 choice->choice, choice->text,
1473 size->width / 72.0, size->length / 72.0,
1474 size->left / 72.0, size->bottom / 72.0,
1475 size->right / 72.0, size->top / 72.0);
1476
1477 if (!strcmp(option->defchoice, choice->choice))
1478 _cupsLangPuts(stdout, " *\n");
1479 else
1480 _cupsLangPuts(stdout, "\n");
1481 }
1482 }
1483 else
1484 {
1485 for (m = option->num_choices, choice = option->choices;
1486 m > 0;
1487 m --, choice ++)
1488 {
1489 _cupsLangPrintf(stdout, " %s (%s)",
1490 choice->choice, choice->text);
1491
1492 if (!strcmp(option->defchoice, choice->choice))
1493 _cupsLangPuts(stdout, " *\n");
1494 else
1495 _cupsLangPuts(stdout, "\n");
1496 }
1497 }
1498 }
1499 }
1500
1501 _cupsLangPrintf(stdout, " num_consts = %d\n",
1502 ppd->num_consts);
1503 for (j = 0; j < ppd->num_consts; j ++)
1504 _cupsLangPrintf(stdout,
1505 " consts[%d] = *%s %s *%s %s\n",
1506 j, ppd->consts[j].option1, ppd->consts[j].choice1,
1507 ppd->consts[j].option2, ppd->consts[j].choice2);
1508
1509 _cupsLangPrintf(stdout, " num_profiles = %d\n",
1510 ppd->num_profiles);
1511 for (j = 0; j < ppd->num_profiles; j ++)
1512 _cupsLangPrintf(stdout,
1513 " profiles[%d] = %s/%s %.3f %.3f "
1514 "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
1515 j, ppd->profiles[j].resolution,
1516 ppd->profiles[j].media_type,
1517 ppd->profiles[j].gamma, ppd->profiles[j].density,
1518 ppd->profiles[j].matrix[0][0],
1519 ppd->profiles[j].matrix[0][1],
1520 ppd->profiles[j].matrix[0][2],
1521 ppd->profiles[j].matrix[1][0],
1522 ppd->profiles[j].matrix[1][1],
1523 ppd->profiles[j].matrix[1][2],
1524 ppd->profiles[j].matrix[2][0],
1525 ppd->profiles[j].matrix[2][1],
1526 ppd->profiles[j].matrix[2][2]);
1527
1528 _cupsLangPrintf(stdout, " num_fonts = %d\n", ppd->num_fonts);
1529 for (j = 0; j < ppd->num_fonts; j ++)
1530 _cupsLangPrintf(stdout, " fonts[%d] = %s\n",
1531 j, ppd->fonts[j]);
1532
1533 _cupsLangPrintf(stdout, " num_attrs = %d\n", ppd->num_attrs);
1534 for (j = 0; j < ppd->num_attrs; j ++)
1535 _cupsLangPrintf(stdout,
1536 " attrs[%d] = %s %s%s%s: \"%s\"\n", j,
1537 ppd->attrs[j]->name, ppd->attrs[j]->spec,
1538 ppd->attrs[j]->text[0] ? "/" : "",
1539 ppd->attrs[j]->text,
1540 ppd->attrs[j]->value ?
1541 ppd->attrs[j]->value : "(null)");
1542 }
1543
1544 ppdClose(ppd);
1545 }
1546
1547 if (!files)
1548 usage();
1549
1550 return (status);
1551 }
1552
1553
1554 /*
1555 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1556 */
1557
1558 static void
1559 check_basics(const char *filename) /* I - PPD file to check */
1560 {
1561 cups_file_t *fp; /* File pointer */
1562 int ch; /* Current character */
1563 int col, /* Current column */
1564 whitespace; /* Only seen whitespace? */
1565 int eol; /* Line endings */
1566 int linenum; /* Line number */
1567 int mixed; /* Mixed line endings? */
1568
1569
1570 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1571 return;
1572
1573 linenum = 1;
1574 col = 0;
1575 eol = EOL_NONE;
1576 mixed = 0;
1577 whitespace = 1;
1578
1579 while ((ch = cupsFileGetChar(fp)) != EOF)
1580 {
1581 if (ch == '\r' || ch == '\n')
1582 {
1583 if (ch == '\n')
1584 {
1585 if (eol == EOL_NONE)
1586 eol = EOL_LF;
1587 else if (eol != EOL_LF)
1588 mixed = 1;
1589 }
1590 else if (ch == '\r')
1591 {
1592 if (cupsFilePeekChar(fp) == '\n')
1593 {
1594 cupsFileGetChar(fp);
1595
1596 if (eol == EOL_NONE)
1597 eol = EOL_CRLF;
1598 else if (eol != EOL_CRLF)
1599 mixed = 1;
1600 }
1601 else if (eol == EOL_NONE)
1602 eol = EOL_CR;
1603 else if (eol != EOL_CR)
1604 mixed = 1;
1605 }
1606
1607 if (col > 0 && whitespace)
1608 _cupsLangPrintf(stdout,
1609 _(" WARN Line %d only contains whitespace!\n"),
1610 linenum);
1611
1612 linenum ++;
1613 col = 0;
1614 whitespace = 1;
1615 }
1616 else
1617 {
1618 if (ch != ' ' && ch != '\t')
1619 whitespace = 0;
1620
1621 col ++;
1622 }
1623 }
1624
1625 if (mixed)
1626 _cupsLangPuts(stdout,
1627 _(" WARN File contains a mix of CR, LF, and "
1628 "CR LF line endings!\n"));
1629
1630 if (eol == EOL_CRLF)
1631 _cupsLangPuts(stdout,
1632 _(" WARN Non-Windows PPD files should use lines "
1633 "ending with only LF, not CR LF!\n"));
1634
1635 cupsFileClose(fp);
1636 }
1637
1638
1639 /*
1640 * 'check_constraints()' - Check UIConstraints in the PPD file.
1641 */
1642
1643 static int /* O - Errors found */
1644 check_constraints(ppd_file_t *ppd, /* I - PPD file */
1645 int errors, /* I - Errors found */
1646 int verbose, /* I - Verbosity level */
1647 int warn) /* I - Warnings only? */
1648 {
1649 int i; /* Looping var */
1650 const char *prefix; /* WARN/FAIL prefix */
1651 ppd_const_t *c; /* Current UIConstraints data */
1652 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
1653 const char *vptr; /* Pointer into constraint value */
1654 char option[PPD_MAX_NAME],
1655 /* Option name/MainKeyword */
1656 choice[PPD_MAX_NAME],
1657 /* Choice/OptionKeyword */
1658 *ptr; /* Pointer into option or choice */
1659 int num_options; /* Number of options */
1660 cups_option_t *options; /* Options */
1661 ppd_option_t *o; /* PPD option */
1662
1663
1664 prefix = warn ? " WARN " : "**FAIL**";
1665
1666
1667 /*
1668 * See what kind of constraint data we have in the PPD...
1669 */
1670
1671 if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
1672 {
1673 /*
1674 * Check new-style cupsUIConstraints data...
1675 */
1676
1677 for (; constattr;
1678 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
1679 {
1680 if (!constattr->value)
1681 {
1682 if (!warn && !errors && !verbose)
1683 _cupsLangPuts(stdout, _(" FAIL\n"));
1684
1685 _cupsLangPrintf(stdout,
1686 _(" %s Empty cupsUIConstraints %s!\n"),
1687 prefix, constattr->spec);
1688
1689 if (!warn)
1690 errors ++;
1691
1692 continue;
1693 }
1694
1695 for (i = 0, vptr = strchr(constattr->value, '*');
1696 vptr;
1697 i ++, vptr = strchr(vptr + 1, '*'));
1698
1699 if (i == 0)
1700 {
1701 if (!warn && !errors && !verbose)
1702 _cupsLangPuts(stdout, _(" FAIL\n"));
1703
1704 _cupsLangPrintf(stdout,
1705 _(" %s Bad cupsUIConstraints %s: \"%s\"!\n"),
1706 prefix, constattr->spec, constattr->value);
1707
1708 if (!warn)
1709 errors ++;
1710
1711 continue;
1712 }
1713
1714 cupsArraySave(ppd->sorted_attrs);
1715
1716 if (constattr->spec[0] &&
1717 !ppdFindAttr(ppd, "cupsUIResolver", constattr->spec))
1718 {
1719 if (!warn && !errors && !verbose)
1720 _cupsLangPuts(stdout, _(" FAIL\n"));
1721
1722 _cupsLangPrintf(stdout,
1723 _(" %s Missing cupsUIResolver %s!\n"),
1724 prefix, constattr->spec);
1725
1726 if (!warn)
1727 errors ++;
1728 }
1729
1730 cupsArrayRestore(ppd->sorted_attrs);
1731
1732 num_options = 0;
1733 options = NULL;
1734
1735 for (vptr = strchr(constattr->value, '*');
1736 vptr;
1737 vptr = strchr(vptr, '*'))
1738 {
1739 /*
1740 * Extract "*Option Choice" or just "*Option"...
1741 */
1742
1743 for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
1744 if (ptr < (option + sizeof(option) - 1))
1745 *ptr++ = *vptr;
1746
1747 *ptr = '\0';
1748
1749 while (isspace(*vptr & 255))
1750 vptr ++;
1751
1752 if (*vptr == '*')
1753 choice[0] = '\0';
1754 else
1755 {
1756 for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
1757 if (ptr < (choice + sizeof(choice) - 1))
1758 *ptr++ = *vptr;
1759
1760 *ptr = '\0';
1761 }
1762
1763 if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
1764 {
1765 _cups_strcpy(option, option + 6);
1766 strcpy(choice, "Custom");
1767 }
1768
1769 if ((o = ppdFindOption(ppd, option)) == NULL)
1770 {
1771 if (!warn && !errors && !verbose)
1772 _cupsLangPuts(stdout, _(" FAIL\n"));
1773
1774 _cupsLangPrintf(stdout,
1775 _(" %s Missing option %s in "
1776 "cupsUIConstraints %s: \"%s\"!\n"),
1777 prefix, option, constattr->spec, constattr->value);
1778
1779 if (!warn)
1780 errors ++;
1781
1782 continue;
1783 }
1784
1785 if (choice[0] && !ppdFindChoice(o, choice))
1786 {
1787 if (!warn && !errors && !verbose)
1788 _cupsLangPuts(stdout, _(" FAIL\n"));
1789
1790 _cupsLangPrintf(stdout,
1791 _(" %s Missing choice *%s %s in "
1792 "cupsUIConstraints %s: \"%s\"!\n"),
1793 prefix, option, choice, constattr->spec,
1794 constattr->value);
1795
1796 if (!warn)
1797 errors ++;
1798
1799 continue;
1800 }
1801
1802 if (choice[0])
1803 num_options = cupsAddOption(option, choice, num_options, &options);
1804 else
1805 {
1806 for (i = 0; i < o->num_choices; i ++)
1807 if (strcasecmp(o->choices[i].choice, "None") &&
1808 strcasecmp(o->choices[i].choice, "Off") &&
1809 strcasecmp(o->choices[i].choice, "False"))
1810 {
1811 num_options = cupsAddOption(option, o->choices[i].choice,
1812 num_options, &options);
1813 break;
1814 }
1815 }
1816 }
1817
1818 /*
1819 * Test the resolver...
1820 */
1821
1822 if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options))
1823 {
1824 if (!warn && !errors && !verbose)
1825 _cupsLangPuts(stdout, _(" FAIL\n"));
1826
1827 _cupsLangPrintf(stdout,
1828 _(" %s cupsUIResolver %s causes a loop!\n"),
1829 prefix, constattr->spec);
1830
1831 if (!warn)
1832 errors ++;
1833 }
1834
1835 cupsFreeOptions(num_options, options);
1836 }
1837 }
1838 else
1839 {
1840 /*
1841 * Check old-style [Non]UIConstraints data...
1842 */
1843
1844 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
1845 {
1846 if (!strncasecmp(c->option1, "Custom", 6) &&
1847 !strcasecmp(c->choice1, "True"))
1848 {
1849 strcpy(option, c->option1 + 6);
1850 strcpy(choice, "Custom");
1851 }
1852 else
1853 {
1854 strcpy(option, c->option1);
1855 strcpy(choice, c->choice1);
1856 }
1857
1858 if ((o = ppdFindOption(ppd, option)) == NULL)
1859 {
1860 if (!warn && !errors && !verbose)
1861 _cupsLangPuts(stdout, _(" FAIL\n"));
1862
1863 _cupsLangPrintf(stdout,
1864 _(" %s Missing option %s in "
1865 "UIConstraints \"*%s %s *%s %s\"!\n"),
1866 prefix, c->option1,
1867 c->option1, c->choice1, c->option2, c->choice2);
1868
1869 if (!warn)
1870 errors ++;
1871 }
1872 else if (choice[0] && !ppdFindChoice(o, choice))
1873 {
1874 if (!warn && !errors && !verbose)
1875 _cupsLangPuts(stdout, _(" FAIL\n"));
1876
1877 _cupsLangPrintf(stdout,
1878 _(" %s Missing choice *%s %s in "
1879 "UIConstraints \"*%s %s *%s %s\"!\n"),
1880 prefix, c->option1, c->choice1,
1881 c->option1, c->choice1, c->option2, c->choice2);
1882
1883 if (!warn)
1884 errors ++;
1885 }
1886
1887 if (!strncasecmp(c->option2, "Custom", 6) &&
1888 !strcasecmp(c->choice2, "True"))
1889 {
1890 strcpy(option, c->option2 + 6);
1891 strcpy(choice, "Custom");
1892 }
1893 else
1894 {
1895 strcpy(option, c->option2);
1896 strcpy(choice, c->choice2);
1897 }
1898
1899 if ((o = ppdFindOption(ppd, option)) == NULL)
1900 {
1901 if (!warn && !errors && !verbose)
1902 _cupsLangPuts(stdout, _(" FAIL\n"));
1903
1904 _cupsLangPrintf(stdout,
1905 _(" %s Missing option %s in "
1906 "UIConstraints \"*%s %s *%s %s\"!\n"),
1907 prefix, c->option2,
1908 c->option1, c->choice1, c->option2, c->choice2);
1909
1910 if (!warn)
1911 errors ++;
1912 }
1913 else if (choice[0] && !ppdFindChoice(o, choice))
1914 {
1915 if (!warn && !errors && !verbose)
1916 _cupsLangPuts(stdout, _(" FAIL\n"));
1917
1918 _cupsLangPrintf(stdout,
1919 _(" %s Missing choice *%s %s in "
1920 "UIConstraints \"*%s %s *%s %s\"!\n"),
1921 prefix, c->option2, c->choice2,
1922 c->option1, c->choice1, c->option2, c->choice2);
1923
1924 if (!warn)
1925 errors ++;
1926 }
1927 }
1928 }
1929
1930 return (errors);
1931 }
1932
1933
1934 /*
1935 * 'check_case()' - Check that there are no duplicate groups, options,
1936 * or choices that differ only by case.
1937 */
1938
1939 static int /* O - Errors found */
1940 check_case(ppd_file_t *ppd, /* I - PPD file */
1941 int errors, /* I - Errors found */
1942 int verbose) /* I - Verbosity level */
1943 {
1944 int i, j; /* Looping vars */
1945 ppd_group_t *groupa, /* First group */
1946 *groupb; /* Second group */
1947 ppd_option_t *optiona, /* First option */
1948 *optionb; /* Second option */
1949 ppd_choice_t *choicea, /* First choice */
1950 *choiceb; /* Second choice */
1951
1952
1953 /*
1954 * Check that the groups do not have any duplicate names...
1955 */
1956
1957 for (i = ppd->num_groups, groupa = ppd->groups; i > 1; i --, groupa ++)
1958 for (j = i - 1, groupb = groupa + 1; j > 0; j --, groupb ++)
1959 if (!strcasecmp(groupa->name, groupb->name))
1960 {
1961 if (!errors && !verbose)
1962 _cupsLangPuts(stdout, _(" FAIL\n"));
1963
1964 if (verbose >= 0)
1965 _cupsLangPrintf(stdout,
1966 _(" **FAIL** Group names %s and %s differ only "
1967 "by case!\n"),
1968 groupa->name, groupb->name);
1969
1970 errors ++;
1971 }
1972
1973 /*
1974 * Check that the options do not have any duplicate names...
1975 */
1976
1977 for (optiona = ppdFirstOption(ppd); optiona; optiona = ppdNextOption(ppd))
1978 {
1979 cupsArraySave(ppd->options);
1980 for (optionb = ppdNextOption(ppd); optionb; optionb = ppdNextOption(ppd))
1981 if (!strcasecmp(optiona->keyword, optionb->keyword))
1982 {
1983 if (!errors && !verbose)
1984 _cupsLangPuts(stdout, _(" FAIL\n"));
1985
1986 if (verbose >= 0)
1987 _cupsLangPrintf(stdout,
1988 _(" **FAIL** Option names %s and %s differ only "
1989 "by case!\n"),
1990 optiona->keyword, optionb->keyword);
1991
1992 errors ++;
1993 }
1994 cupsArrayRestore(ppd->options);
1995
1996 /*
1997 * Then the choices...
1998 */
1999
2000 for (i = optiona->num_choices, choicea = optiona->choices;
2001 i > 1;
2002 i --, choicea ++)
2003 for (j = i - 1, choiceb = choicea + 1; j > 0; j --, choiceb ++)
2004 if (!strcmp(choicea->choice, choiceb->choice))
2005 {
2006 if (!errors && !verbose)
2007 _cupsLangPuts(stdout, _(" FAIL\n"));
2008
2009 if (verbose >= 0)
2010 _cupsLangPrintf(stdout,
2011 _(" **FAIL** Multiple occurrences of %s "
2012 "choice name %s!\n"),
2013 optiona->keyword, choicea->choice);
2014
2015 errors ++;
2016
2017 choicea ++;
2018 i --;
2019 break;
2020 }
2021 else if (!strcasecmp(choicea->choice, choiceb->choice))
2022 {
2023 if (!errors && !verbose)
2024 _cupsLangPuts(stdout, _(" FAIL\n"));
2025
2026 if (verbose >= 0)
2027 _cupsLangPrintf(stdout,
2028 _(" **FAIL** %s choice names %s and %s "
2029 "differ only by case!\n"),
2030 optiona->keyword, choicea->choice, choiceb->choice);
2031
2032 errors ++;
2033 }
2034 }
2035
2036 /*
2037 * Return the number of errors found...
2038 */
2039
2040 return (errors);
2041 }
2042
2043
2044 /*
2045 * 'check_defaults()' - Check default option keywords in the PPD file.
2046 */
2047
2048 static int /* O - Errors found */
2049 check_defaults(ppd_file_t *ppd, /* I - PPD file */
2050 int errors, /* I - Errors found */
2051 int verbose, /* I - Verbosity level */
2052 int warn) /* I - Warnings only? */
2053 {
2054 int j, k; /* Looping vars */
2055 ppd_attr_t *attr; /* PPD attribute */
2056 ppd_option_t *option; /* Standard UI option */
2057 const char *prefix; /* WARN/FAIL prefix */
2058
2059
2060 prefix = warn ? " WARN " : "**FAIL**";
2061
2062 for (j = 0; j < ppd->num_attrs; j ++)
2063 {
2064 attr = ppd->attrs[j];
2065
2066 if (!strcmp(attr->name, "DefaultColorSpace") ||
2067 !strcmp(attr->name, "DefaultFont") ||
2068 !strcmp(attr->name, "DefaultHalftoneType") ||
2069 !strcmp(attr->name, "DefaultImageableArea") ||
2070 !strcmp(attr->name, "DefaultLeadingEdge") ||
2071 !strcmp(attr->name, "DefaultOutputOrder") ||
2072 !strcmp(attr->name, "DefaultPaperDimension") ||
2073 !strcmp(attr->name, "DefaultResolution") ||
2074 !strcmp(attr->name, "DefaultTransfer"))
2075 continue;
2076
2077 if (!strncmp(attr->name, "Default", 7))
2078 {
2079 if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
2080 strcmp(attr->value, "Unknown"))
2081 {
2082 /*
2083 * Check that the default option value matches a choice...
2084 */
2085
2086 for (k = 0; k < option->num_choices; k ++)
2087 if (!strcmp(option->choices[k].choice, attr->value))
2088 break;
2089
2090 if (k >= option->num_choices)
2091 {
2092 if (!warn && !errors && !verbose)
2093 _cupsLangPuts(stdout, _(" FAIL\n"));
2094
2095 if (verbose >= 0)
2096 _cupsLangPrintf(stdout,
2097 _(" %s %s %s does not exist!\n"),
2098 prefix, attr->name, attr->value);
2099
2100 if (!warn)
2101 errors ++;
2102 }
2103 }
2104 }
2105 }
2106
2107 return (errors);
2108 }
2109
2110
2111 /*
2112 * 'check_duplex()' - Check duplex keywords in the PPD file.
2113 */
2114
2115 static int /* O - Errors found */
2116 check_duplex(ppd_file_t *ppd, /* I - PPD file */
2117 int errors, /* I - Error found */
2118 int verbose, /* I - Verbosity level */
2119 int warn) /* I - Warnings only? */
2120 {
2121 int i; /* Looping var */
2122 ppd_option_t *option; /* PPD option */
2123 ppd_choice_t *choice; /* Current choice */
2124 const char *prefix; /* Message prefix */
2125
2126
2127 prefix = warn ? " WARN " : "**FAIL**";
2128
2129 /*
2130 * Check for a duplex option, and for standard values...
2131 */
2132
2133 if ((option = ppdFindOption(ppd, "Duplex")) != NULL)
2134 {
2135 if (!ppdFindChoice(option, "None"))
2136 {
2137 if (verbose >= 0)
2138 {
2139 if (!warn && !errors && !verbose)
2140 _cupsLangPuts(stdout, _(" FAIL\n"));
2141
2142 _cupsLangPrintf(stdout,
2143 _(" %s REQUIRED %s does not define "
2144 "choice None!\n"
2145 " REF: Page 122, section 5.17\n"),
2146 prefix, option->keyword);
2147 }
2148
2149 if (!warn)
2150 errors ++;
2151 }
2152
2153 for (i = option->num_choices, choice = option->choices;
2154 i > 0;
2155 i --, choice ++)
2156 if (strcmp(choice->choice, "None") &&
2157 strcmp(choice->choice, "DuplexNoTumble") &&
2158 strcmp(choice->choice, "DuplexTumble") &&
2159 strcmp(choice->choice, "SimplexTumble"))
2160 {
2161 if (verbose >= 0)
2162 {
2163 if (!warn && !errors && !verbose)
2164 _cupsLangPuts(stdout, _(" FAIL\n"));
2165
2166 _cupsLangPrintf(stdout,
2167 _(" %s Bad %s choice %s!\n"
2168 " REF: Page 122, section 5.17\n"),
2169 prefix, option->keyword, choice->choice);
2170 }
2171
2172 if (!warn)
2173 errors ++;
2174 }
2175 }
2176
2177 return (errors);
2178 }
2179
2180
2181 /*
2182 * 'check_filters()' - Check filters in the PPD file.
2183 */
2184
2185 static int /* O - Errors found */
2186 check_filters(ppd_file_t *ppd, /* I - PPD file */
2187 const char *root, /* I - Root directory */
2188 int errors, /* I - Errors found */
2189 int verbose, /* I - Verbosity level */
2190 int warn) /* I - Warnings only? */
2191 {
2192 int i; /* Looping var */
2193 ppd_attr_t *attr; /* PPD attribute */
2194 const char *ptr; /* Pointer into string */
2195 char super[16], /* Super-type for filter */
2196 type[256], /* Type for filter */
2197 program[1024], /* Program/filter name */
2198 pathprog[1024]; /* Complete path to program/filter */
2199 int cost; /* Cost of filter */
2200 const char *prefix; /* WARN/FAIL prefix */
2201
2202
2203 prefix = warn ? " WARN " : "**FAIL**";
2204
2205 for (i = 0; i < ppd->num_filters; i ++)
2206 {
2207 if (sscanf(ppd->filters[i], "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
2208 &cost, program) != 4)
2209 {
2210 if (!warn && !errors && !verbose)
2211 _cupsLangPuts(stdout, _(" FAIL\n"));
2212
2213 if (verbose >= 0)
2214 _cupsLangPrintf(stdout,
2215 _(" %s Bad cupsFilter value \"%s\"!\n"),
2216 prefix, ppd->filters[i]);
2217
2218 if (!warn)
2219 errors ++;
2220 }
2221 else if (strcmp(program, "-"))
2222 {
2223 if (program[0] == '/')
2224 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
2225 else
2226 {
2227 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
2228 ptr = CUPS_SERVERBIN;
2229
2230 if (*ptr == '/' || !*root)
2231 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
2232 program);
2233 else
2234 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
2235 program);
2236 }
2237
2238 if (access(pathprog, X_OK))
2239 {
2240 if (!warn && !errors && !verbose)
2241 _cupsLangPuts(stdout, _(" FAIL\n"));
2242
2243 if (verbose >= 0)
2244 _cupsLangPrintf(stdout, _(" %s Missing cupsFilter "
2245 "file \"%s\"\n"), prefix, program);
2246
2247 if (!warn)
2248 errors ++;
2249 }
2250 }
2251 }
2252
2253 for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
2254 attr;
2255 attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
2256 {
2257 if (!attr->value ||
2258 sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
2259 &cost, program) != 4)
2260 {
2261 if (!warn && !errors && !verbose)
2262 _cupsLangPuts(stdout, _(" FAIL\n"));
2263
2264 if (verbose >= 0)
2265 _cupsLangPrintf(stdout,
2266 _(" %s Bad cupsPreFilter value \"%s\"!\n"),
2267 prefix, attr->value ? attr->value : "");
2268
2269 if (!warn)
2270 errors ++;
2271 }
2272 else if (strcmp(program, "-"))
2273 {
2274 if (program[0] == '/')
2275 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
2276 else
2277 {
2278 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
2279 ptr = CUPS_SERVERBIN;
2280
2281 if (*ptr == '/' || !*root)
2282 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
2283 program);
2284 else
2285 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
2286 program);
2287 }
2288
2289 if (access(pathprog, X_OK))
2290 {
2291 if (!warn && !errors && !verbose)
2292 _cupsLangPuts(stdout, _(" FAIL\n"));
2293
2294 if (verbose >= 0)
2295 _cupsLangPrintf(stdout, _(" %s Missing cupsPreFilter "
2296 "file \"%s\"\n"), prefix, program);
2297
2298 if (!warn)
2299 errors ++;
2300 }
2301 }
2302 }
2303
2304 return (errors);
2305 }
2306
2307
2308 /*
2309 * 'check_profiles()' - Check ICC color profiles in the PPD file.
2310 */
2311
2312 static int /* O - Errors found */
2313 check_profiles(ppd_file_t *ppd, /* I - PPD file */
2314 const char *root, /* I - Root directory */
2315 int errors, /* I - Errors found */
2316 int verbose, /* I - Verbosity level */
2317 int warn) /* I - Warnings only? */
2318 {
2319 int i; /* Looping var */
2320 ppd_attr_t *attr; /* PPD attribute */
2321 const char *ptr; /* Pointer into string */
2322 const char *prefix; /* WARN/FAIL prefix */
2323 char filename[1024]; /* Profile filename */
2324 int num_profiles = 0; /* Number of profiles */
2325 unsigned hash, /* Current hash value */
2326 hashes[1000]; /* Hash values of profile names */
2327 const char *specs[1000]; /* Specifiers for profiles */
2328
2329
2330 prefix = warn ? " WARN " : "**FAIL**";
2331
2332 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
2333 attr;
2334 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
2335 {
2336 /*
2337 * Check for valid selector...
2338 */
2339
2340 for (i = 0, ptr = strchr(attr->spec, '.'); ptr; ptr = strchr(ptr + 1, '.'))
2341 i ++;
2342
2343 if (!attr->value || i < 2)
2344 {
2345 if (!warn && !errors && !verbose)
2346 _cupsLangPuts(stdout, _(" FAIL\n"));
2347
2348 if (verbose >= 0)
2349 _cupsLangPrintf(stdout,
2350 _(" %s Bad cupsICCProfile %s!\n"),
2351 prefix, attr->spec);
2352
2353 if (!warn)
2354 errors ++;
2355
2356 continue;
2357 }
2358
2359 /*
2360 * Check for valid profile filename...
2361 */
2362
2363 if (attr->value[0] == '/')
2364 snprintf(filename, sizeof(filename), "%s%s", root, attr->value);
2365 else
2366 {
2367 if ((ptr = getenv("CUPS_DATADIR")) == NULL)
2368 ptr = CUPS_DATADIR;
2369
2370 if (*ptr == '/' || !*root)
2371 snprintf(filename, sizeof(filename), "%s%s/profiles/%s", root, ptr,
2372 attr->value);
2373 else
2374 snprintf(filename, sizeof(filename), "%s/%s/profiles/%s", root, ptr,
2375 attr->value);
2376 }
2377
2378 if (access(filename, 0))
2379 {
2380 if (!warn && !errors && !verbose)
2381 _cupsLangPuts(stdout, _(" FAIL\n"));
2382
2383 if (verbose >= 0)
2384 _cupsLangPrintf(stdout, _(" %s Missing cupsICCProfile "
2385 "file \"%s\"!\n"), prefix, attr->value);
2386
2387 if (!warn)
2388 errors ++;
2389 }
2390
2391 /*
2392 * Check for hash collisions...
2393 */
2394
2395 hash = _ppdHashName(attr->spec);
2396
2397 if (num_profiles > 0)
2398 {
2399 for (i = 0; i < num_profiles; i ++)
2400 if (hashes[i] == hash)
2401 break;
2402
2403 if (i < num_profiles)
2404 {
2405 if (!warn && !errors && !verbose)
2406 _cupsLangPuts(stdout, _(" FAIL\n"));
2407
2408 if (verbose >= 0)
2409 _cupsLangPrintf(stdout,
2410 _(" %s cupsICCProfile %s hash value "
2411 "collides with %s!\n"), prefix, attr->spec,
2412 specs[i]);
2413
2414 if (!warn)
2415 errors ++;
2416 }
2417 }
2418
2419 /*
2420 * Remember up to 1000 profiles...
2421 */
2422
2423 if (num_profiles < 1000)
2424 {
2425 hashes[num_profiles] = hash;
2426 specs[num_profiles] = attr->spec;
2427 num_profiles ++;
2428 }
2429 }
2430
2431 return (errors);
2432 }
2433
2434
2435 /*
2436 * 'check_translations()' - Check translations in the PPD file.
2437 */
2438
2439 static int /* O - Errors found */
2440 check_translations(ppd_file_t *ppd, /* I - PPD file */
2441 int errors, /* I - Errors found */
2442 int verbose, /* I - Verbosity level */
2443 int warn) /* I - Warnings only? */
2444 {
2445 int j; /* Looping var */
2446 ppd_attr_t *attr; /* PPD attribute */
2447 cups_array_t *languages; /* Array of languages */
2448 int langlen; /* Length of language */
2449 char *language, /* Current language */
2450 keyword[PPD_MAX_NAME], /* Localization keyword (full) */
2451 llkeyword[PPD_MAX_NAME],/* Localization keyword (base) */
2452 ckeyword[PPD_MAX_NAME], /* Custom option keyword (full) */
2453 cllkeyword[PPD_MAX_NAME];
2454 /* Custom option keyword (base) */
2455 ppd_option_t *option; /* Standard UI option */
2456 ppd_coption_t *coption; /* Custom option */
2457 ppd_cparam_t *cparam; /* Custom parameter */
2458 char ll[3]; /* Base language */
2459 const char *prefix; /* WARN/FAIL prefix */
2460 const char *text; /* Pointer into UI text */
2461
2462
2463 prefix = warn ? " WARN " : "**FAIL**";
2464
2465 if ((languages = _ppdGetLanguages(ppd)) != NULL)
2466 {
2467 /*
2468 * This file contains localizations, check them...
2469 */
2470
2471 for (language = (char *)cupsArrayFirst(languages);
2472 language;
2473 language = (char *)cupsArrayNext(languages))
2474 {
2475 langlen = strlen(language);
2476 if (langlen != 2 && langlen != 5)
2477 {
2478 if (!warn && !errors && !verbose)
2479 _cupsLangPuts(stdout, _(" FAIL\n"));
2480
2481 if (verbose >= 0)
2482 _cupsLangPrintf(stdout,
2483 _(" %s Bad language \"%s\"!\n"),
2484 prefix, language);
2485
2486 if (!warn)
2487 errors ++;
2488
2489 continue;
2490 }
2491
2492 if (!strcmp(language, "en"))
2493 continue;
2494
2495 strlcpy(ll, language, sizeof(ll));
2496
2497 /*
2498 * Loop through all options and choices...
2499 */
2500
2501 for (option = ppdFirstOption(ppd);
2502 option;
2503 option = ppdNextOption(ppd))
2504 {
2505 if (!strcmp(option->keyword, "PageRegion"))
2506 continue;
2507
2508 snprintf(keyword, sizeof(keyword), "%s.Translation", language);
2509 snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll);
2510
2511 if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL &&
2512 (attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL)
2513 {
2514 if (!warn && !errors && !verbose)
2515 _cupsLangPuts(stdout, _(" FAIL\n"));
2516
2517 if (verbose >= 0)
2518 _cupsLangPrintf(stdout,
2519 _(" %s Missing \"%s\" translation "
2520 "string for option %s!\n"),
2521 prefix, language, option->keyword);
2522
2523 if (!warn)
2524 errors ++;
2525 }
2526 else if (!valid_utf8(attr->text))
2527 {
2528 if (!warn && !errors && !verbose)
2529 _cupsLangPuts(stdout, _(" FAIL\n"));
2530
2531 if (verbose >= 0)
2532 _cupsLangPrintf(stdout,
2533 _(" %s Bad UTF-8 \"%s\" translation "
2534 "string for option %s!\n"),
2535 prefix, language, option->keyword);
2536
2537 if (!warn)
2538 errors ++;
2539 }
2540
2541 snprintf(keyword, sizeof(keyword), "%s.%s", language,
2542 option->keyword);
2543 snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll,
2544 option->keyword);
2545
2546 for (j = 0; j < option->num_choices; j ++)
2547 {
2548 /*
2549 * First see if this choice is a number; if so, don't require
2550 * translation...
2551 */
2552
2553 for (text = option->choices[j].text; *text; text ++)
2554 if (!strchr("0123456789-+.", *text))
2555 break;
2556
2557 if (!*text)
2558 continue;
2559
2560 /*
2561 * Check custom choices differently...
2562 */
2563
2564 if (!strcasecmp(option->choices[j].choice, "Custom") &&
2565 (coption = ppdFindCustomOption(ppd,
2566 option->keyword)) != NULL)
2567 {
2568 snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
2569 language, option->keyword);
2570
2571 if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL &&
2572 !valid_utf8(attr->text))
2573 {
2574 if (!warn && !errors && !verbose)
2575 _cupsLangPuts(stdout, _(" FAIL\n"));
2576
2577 if (verbose >= 0)
2578 _cupsLangPrintf(stdout,
2579 _(" %s Bad UTF-8 \"%s\" "
2580 "translation string for option %s, "
2581 "choice %s!\n"),
2582 prefix, language,
2583 ckeyword + 1 + strlen(language),
2584 "True");
2585
2586 if (!warn)
2587 errors ++;
2588 }
2589
2590 if (strcasecmp(option->keyword, "PageSize"))
2591 {
2592 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
2593 cparam;
2594 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
2595 {
2596 snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
2597 language, option->keyword);
2598 snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s",
2599 ll, option->keyword);
2600
2601 if ((attr = ppdFindAttr(ppd, ckeyword,
2602 cparam->name)) == NULL &&
2603 (attr = ppdFindAttr(ppd, cllkeyword,
2604 cparam->name)) == NULL)
2605 {
2606 if (!warn && !errors && !verbose)
2607 _cupsLangPuts(stdout, _(" FAIL\n"));
2608
2609 if (verbose >= 0)
2610 _cupsLangPrintf(stdout,
2611 _(" %s Missing \"%s\" "
2612 "translation string for option %s, "
2613 "choice %s!\n"),
2614 prefix, language,
2615 ckeyword + 1 + strlen(language),
2616 cparam->name);
2617
2618 if (!warn)
2619 errors ++;
2620 }
2621 else if (!valid_utf8(attr->text))
2622 {
2623 if (!warn && !errors && !verbose)
2624 _cupsLangPuts(stdout, _(" FAIL\n"));
2625
2626 if (verbose >= 0)
2627 _cupsLangPrintf(stdout,
2628 _(" %s Bad UTF-8 \"%s\" "
2629 "translation string for option %s, "
2630 "choice %s!\n"),
2631 prefix, language,
2632 ckeyword + 1 + strlen(language),
2633 cparam->name);
2634
2635 if (!warn)
2636 errors ++;
2637 }
2638 }
2639 }
2640 }
2641 else if ((attr = ppdFindAttr(ppd, keyword,
2642 option->choices[j].choice)) == NULL &&
2643 (attr = ppdFindAttr(ppd, llkeyword,
2644 option->choices[j].choice)) == NULL)
2645 {
2646 if (!warn && !errors && !verbose)
2647 _cupsLangPuts(stdout, _(" FAIL\n"));
2648
2649 if (verbose >= 0)
2650 _cupsLangPrintf(stdout,
2651 _(" %s Missing \"%s\" "
2652 "translation string for option %s, "
2653 "choice %s!\n"),
2654 prefix, language, option->keyword,
2655 option->choices[j].choice);
2656
2657 if (!warn)
2658 errors ++;
2659 }
2660 else if (!valid_utf8(attr->text))
2661 {
2662 if (!warn && !errors && !verbose)
2663 _cupsLangPuts(stdout, _(" FAIL\n"));
2664
2665 if (verbose >= 0)
2666 _cupsLangPrintf(stdout,
2667 _(" %s Bad UTF-8 \"%s\" "
2668 "translation string for option %s, "
2669 "choice %s!\n"),
2670 prefix, language, option->keyword,
2671 option->choices[j].choice);
2672
2673 if (!warn)
2674 errors ++;
2675 }
2676 }
2677 }
2678 }
2679
2680 /*
2681 * Verify that we have the base language for each localized one...
2682 */
2683
2684 for (language = (char *)cupsArrayFirst(languages);
2685 language;
2686 language = (char *)cupsArrayNext(languages))
2687 if (language[2])
2688 {
2689 /*
2690 * Lookup the base language...
2691 */
2692
2693 cupsArraySave(languages);
2694
2695 strlcpy(ll, language, sizeof(ll));
2696
2697 if (!cupsArrayFind(languages, ll) &&
2698 strcmp(ll, "zh") && strcmp(ll, "en"))
2699 {
2700 if (!warn && !errors && !verbose)
2701 _cupsLangPuts(stdout, _(" FAIL\n"));
2702
2703 if (verbose >= 0)
2704 _cupsLangPrintf(stdout,
2705 _(" %s No base translation \"%s\" "
2706 "is included in file!\n"), prefix, ll);
2707
2708 if (!warn)
2709 errors ++;
2710 }
2711
2712 cupsArrayRestore(languages);
2713 }
2714
2715 /*
2716 * Free memory used for the languages...
2717 */
2718
2719 _ppdFreeLanguages(languages);
2720 }
2721
2722 return (errors);
2723 }
2724
2725
2726 /*
2727 * 'show_conflicts()' - Show option conflicts in a PPD file.
2728 */
2729
2730 static void
2731 show_conflicts(ppd_file_t *ppd) /* I - PPD to check */
2732 {
2733 int i, j; /* Looping variables */
2734 ppd_const_t *c; /* Current constraint */
2735 ppd_option_t *o1, *o2; /* Options */
2736 ppd_choice_t *c1, *c2; /* Choices */
2737
2738
2739 /*
2740 * Loop through all of the UI constraints and report any options
2741 * that conflict...
2742 */
2743
2744 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
2745 {
2746 /*
2747 * Grab pointers to the first option...
2748 */
2749
2750 o1 = ppdFindOption(ppd, c->option1);
2751
2752 if (o1 == NULL)
2753 continue;
2754 else if (c->choice1[0] != '\0')
2755 {
2756 /*
2757 * This constraint maps to a specific choice.
2758 */
2759
2760 c1 = ppdFindChoice(o1, c->choice1);
2761 }
2762 else
2763 {
2764 /*
2765 * This constraint applies to any choice for this option.
2766 */
2767
2768 for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
2769 if (c1->marked)
2770 break;
2771
2772 if (j == 0 ||
2773 !strcasecmp(c1->choice, "None") ||
2774 !strcasecmp(c1->choice, "Off") ||
2775 !strcasecmp(c1->choice, "False"))
2776 c1 = NULL;
2777 }
2778
2779 /*
2780 * Grab pointers to the second option...
2781 */
2782
2783 o2 = ppdFindOption(ppd, c->option2);
2784
2785 if (o2 == NULL)
2786 continue;
2787 else if (c->choice2[0] != '\0')
2788 {
2789 /*
2790 * This constraint maps to a specific choice.
2791 */
2792
2793 c2 = ppdFindChoice(o2, c->choice2);
2794 }
2795 else
2796 {
2797 /*
2798 * This constraint applies to any choice for this option.
2799 */
2800
2801 for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
2802 if (c2->marked)
2803 break;
2804
2805 if (j == 0 ||
2806 !strcasecmp(c2->choice, "None") ||
2807 !strcasecmp(c2->choice, "Off") ||
2808 !strcasecmp(c2->choice, "False"))
2809 c2 = NULL;
2810 }
2811
2812 /*
2813 * If both options are marked then there is a conflict...
2814 */
2815
2816 if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
2817 _cupsLangPrintf(stdout,
2818 _(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
2819 " (constraint=\"%s %s %s %s\")\n"),
2820 o1->keyword, c1->choice, o2->keyword, c2->choice,
2821 c->option1, c->choice1, c->option2, c->choice2);
2822 }
2823 }
2824
2825
2826 /*
2827 * 'test_raster()' - Test PostScript commands for raster printers.
2828 */
2829
2830 static int /* O - 1 on success, 0 on failure */
2831 test_raster(ppd_file_t *ppd, /* I - PPD file */
2832 int verbose) /* I - Verbosity */
2833 {
2834 cups_page_header2_t header; /* Page header */
2835
2836
2837 ppdMarkDefaults(ppd);
2838 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
2839 {
2840 if (!verbose)
2841 _cupsLangPuts(stdout, _(" FAIL\n"));
2842
2843 if (verbose >= 0)
2844 _cupsLangPrintf(stdout,
2845 _(" **FAIL** Default option code cannot be "
2846 "interpreted: %s\n"), cupsRasterErrorString());
2847
2848 return (0);
2849 }
2850
2851 /*
2852 * Try a test of custom page size code, if available...
2853 */
2854
2855 if (!ppdPageSize(ppd, "Custom.612x792"))
2856 return (1);
2857
2858 ppdMarkOption(ppd, "PageSize", "Custom.612x792");
2859
2860 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
2861 {
2862 if (!verbose)
2863 _cupsLangPuts(stdout, _(" FAIL\n"));
2864
2865 if (verbose >= 0)
2866 _cupsLangPrintf(stdout,
2867 _(" **FAIL** Default option code cannot be "
2868 "interpreted: %s\n"), cupsRasterErrorString());
2869
2870 return (0);
2871 }
2872
2873 return (1);
2874 }
2875
2876
2877 /*
2878 * 'usage()' - Show program usage...
2879 */
2880
2881 static void
2882 usage(void)
2883 {
2884 _cupsLangPuts(stdout,
2885 _("Usage: cupstestppd [options] filename1.ppd[.gz] "
2886 "[... filenameN.ppd[.gz]]\n"
2887 " program | cupstestppd [options] -\n"
2888 "\n"
2889 "Options:\n"
2890 "\n"
2891 " -R root-directory Set alternate root\n"
2892 " -W {all,none,constraints,defaults,duplex,filters,"
2893 "translations}\n"
2894 " Issue warnings instead of errors\n"
2895 " -q Run silently\n"
2896 " -r Use 'relaxed' open mode\n"
2897 " -v Be slightly verbose\n"
2898 " -vv Be very verbose\n"));
2899
2900 exit(ERROR_USAGE);
2901 }
2902
2903
2904 /*
2905 * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
2906 */
2907
2908 static int /* O - 1 if valid, 0 if not */
2909 valid_utf8(const char *s) /* I - String to check */
2910 {
2911 while (*s)
2912 {
2913 if (*s & 0x80)
2914 {
2915 /*
2916 * Check for valid UTF-8 sequence...
2917 */
2918
2919 if ((*s & 0xc0) == 0x80)
2920 return (0); /* Illegal suffix byte */
2921 else if ((*s & 0xe0) == 0xc0)
2922 {
2923 /*
2924 * 2-byte sequence...
2925 */
2926
2927 s ++;
2928
2929 if ((*s & 0xc0) != 0x80)
2930 return (0); /* Missing suffix byte */
2931 }
2932 else if ((*s & 0xf0) == 0xe0)
2933 {
2934 /*
2935 * 3-byte sequence...
2936 */
2937
2938 s ++;
2939
2940 if ((*s & 0xc0) != 0x80)
2941 return (0); /* Missing suffix byte */
2942
2943 s ++;
2944
2945 if ((*s & 0xc0) != 0x80)
2946 return (0); /* Missing suffix byte */
2947 }
2948 else if ((*s & 0xf8) == 0xf0)
2949 {
2950 /*
2951 * 4-byte sequence...
2952 */
2953
2954 s ++;
2955
2956 if ((*s & 0xc0) != 0x80)
2957 return (0); /* Missing suffix byte */
2958
2959 s ++;
2960
2961 if ((*s & 0xc0) != 0x80)
2962 return (0); /* Missing suffix byte */
2963
2964 s ++;
2965
2966 if ((*s & 0xc0) != 0x80)
2967 return (0); /* Missing suffix byte */
2968 }
2969 else
2970 return (0); /* Bad sequence */
2971 }
2972
2973 s ++;
2974 }
2975
2976 return (1);
2977 }
2978
2979
2980 /*
2981 * End of "$Id: cupstestppd.c 7637 2008-06-11 17:25:36Z mike $".
2982 */