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