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