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