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