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