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