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