]> git.ipfire.org Git - thirdparty/cups.git/blob - systemv/cupstestppd.c
Import CUPS 1.4svn-r7226.
[thirdparty/cups.git] / systemv / cupstestppd.c
1 /*
2 * "$Id: cupstestppd.c 6927 2007-09-07 16:51:00Z mike $"
3 *
4 * PPD test program for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 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 lines.
23 * check_constraints() - Check UIConstraints in the PPD file.
24 * check_defaults() - Check default option keywords in the PPD file.
25 * check_filters() - Check filters in the PPD file.
26 * check_translations() - Check translations in the PPD file.
27 * show_conflicts() - Show option conflicts in a PPD file.
28 * test_raster() - Test PostScript commands for raster printers.
29 * usage() - Show program usage...
30 * valid_utf8() - Check whether a string contains valid UTF-8 text.
31 */
32
33 /*
34 * Include necessary headers...
35 */
36
37 #include <cups/string.h>
38 #include <cups/cups.h>
39 #include <cups/i18n.h>
40 #include <filter/raster.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <sys/stat.h>
44
45
46 /*
47 * Error warning overrides...
48 */
49
50 enum
51 {
52 WARN_NONE = 0,
53 WARN_CONSTRAINTS = 1,
54 WARN_DEFAULTS = 2,
55 WARN_FILTERS = 4,
56 WARN_TRANSLATIONS = 8,
57 WARN_ALL = 15
58 };
59
60
61 /*
62 * Error codes...
63 */
64
65 enum
66 {
67 ERROR_NONE = 0,
68 ERROR_USAGE,
69 ERROR_FILE_OPEN,
70 ERROR_PPD_FORMAT,
71 ERROR_CONFORMANCE
72 };
73
74
75 /*
76 * Line endings...
77 */
78
79 enum
80 {
81 EOL_NONE = 0,
82 EOL_CR,
83 EOL_LF,
84 EOL_CRLF
85 };
86
87
88 /*
89 * Local functions...
90 */
91
92 static void check_basics(const char *filename);
93 static int check_constraints(ppd_file_t *ppd, int errors, int verbose,
94 int warn);
95 static int check_defaults(ppd_file_t *ppd, int errors, int verbose,
96 int warn);
97 static int check_filters(ppd_file_t *ppd, const char *root, int errors,
98 int verbose, int warn);
99 static int check_translations(ppd_file_t *ppd, int errors, int verbose,\
100 int warn);
101 static void show_conflicts(ppd_file_t *ppd);
102 static int test_raster(ppd_file_t *ppd, int verbose);
103 static void usage(void);
104 static int valid_utf8(const char *s);
105
106
107 /*
108 * 'main()' - Main entry for test program.
109 */
110
111 int /* O - Exit status */
112 main(int argc, /* I - Number of command-line args */
113 char *argv[]) /* I - Command-line arguments */
114 {
115 int i, j, k, m, n; /* Looping vars */
116 int len; /* Length of option name */
117 char *opt; /* Option character */
118 const char *ptr; /* Pointer into string */
119 int files; /* Number of files */
120 int verbose; /* Want verbose output? */
121 int warn; /* Which errors to just warn about */
122 int status; /* Exit status */
123 int errors; /* Number of conformance errors */
124 int ppdversion; /* PPD spec version in PPD file */
125 ppd_status_t error; /* Status of ppdOpen*() */
126 int line; /* Line number for error */
127 struct stat statbuf; /* File information */
128 char pathprog[1024], /* Complete path to program/filter */
129 *root; /* Root directory */
130 int xdpi, /* X resolution */
131 ydpi; /* Y resolution */
132 ppd_file_t *ppd; /* PPD file record */
133 ppd_attr_t *attr; /* PPD attribute */
134 ppd_size_t *size; /* Size record */
135 ppd_group_t *group; /* UI group */
136 ppd_option_t *option; /* Standard UI option */
137 ppd_group_t *group2; /* UI group */
138 ppd_option_t *option2; /* Standard UI option */
139 ppd_choice_t *choice; /* Standard UI option choice */
140 static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
141 static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
142 "JCL", "PAGE", "PROLOG" };
143
144
145 _cupsSetLocale(argv);
146
147 /*
148 * Display PPD files for each file listed on the command-line...
149 */
150
151 ppdSetConformance(PPD_CONFORM_STRICT);
152
153 verbose = 0;
154 ppd = NULL;
155 files = 0;
156 status = ERROR_NONE;
157 root = "";
158 warn = WARN_NONE;
159
160 for (i = 1; i < argc; i ++)
161 if (argv[i][0] == '-' && argv[i][1])
162 {
163 for (opt = argv[i] + 1; *opt; opt ++)
164 switch (*opt)
165 {
166 case 'R' : /* Alternate root directory */
167 i ++;
168
169 if (i >= argc)
170 usage();
171
172 root = argv[i];
173 break;
174
175 case 'W' : /* Turn errors into warnings */
176 i ++;
177
178 if (i >= argc)
179 usage();
180
181 if (!strcmp(argv[i], "none"))
182 warn = WARN_NONE;
183 else if (!strcmp(argv[i], "constraints"))
184 warn |= WARN_CONSTRAINTS;
185 else if (!strcmp(argv[i], "defaults"))
186 warn |= WARN_DEFAULTS;
187 else if (!strcmp(argv[i], "filters"))
188 warn |= WARN_FILTERS;
189 else if (!strcmp(argv[i], "translations"))
190 warn |= WARN_TRANSLATIONS;
191 else if (!strcmp(argv[i], "all"))
192 warn = WARN_ALL;
193 else
194 usage();
195 break;
196
197 case 'q' : /* Quiet mode */
198 if (verbose > 0)
199 {
200 _cupsLangPuts(stderr,
201 _("cupstestppd: The -q option is incompatible "
202 "with the -v option.\n"));
203 return (1);
204 }
205
206 verbose --;
207 break;
208
209 case 'r' : /* Relaxed mode */
210 ppdSetConformance(PPD_CONFORM_RELAXED);
211 break;
212
213 case 'v' : /* Verbose mode */
214 if (verbose < 0)
215 {
216 _cupsLangPuts(stderr,
217 _("cupstestppd: The -v option is incompatible "
218 "with the -q option.\n"));
219 return (1);
220 }
221
222 verbose ++;
223 break;
224
225 default :
226 usage();
227 break;
228 }
229 }
230 else
231 {
232 /*
233 * Open the PPD file...
234 */
235
236 if (files && verbose >= 0)
237 _cupsLangPuts(stdout, "\n");
238
239 files ++;
240
241 if (argv[i][0] == '-')
242 {
243 /*
244 * Read from stdin...
245 */
246
247 if (verbose >= 0)
248 printf("(stdin):");
249
250 ppd = ppdOpen(stdin);
251 }
252 else
253 {
254 /*
255 * Read from a file...
256 */
257
258 if (verbose >= 0)
259 printf("%s:", argv[i]);
260
261 ppd = ppdOpenFile(argv[i]);
262 }
263
264 if (ppd == NULL)
265 {
266 error = ppdLastError(&line);
267
268 if (error <= PPD_ALLOC_ERROR)
269 {
270 status = ERROR_FILE_OPEN;
271
272 if (verbose >= 0)
273 _cupsLangPrintf(stdout,
274 _(" FAIL\n"
275 " **FAIL** Unable to open PPD file - %s\n"),
276 strerror(errno));
277 }
278 else
279 {
280 status = ERROR_PPD_FORMAT;
281
282 if (verbose >= 0)
283 {
284 _cupsLangPrintf(stdout,
285 _(" FAIL\n"
286 " **FAIL** Unable to open PPD file - "
287 "%s on line %d.\n"),
288 ppdErrorString(error), line);
289
290 switch (error)
291 {
292 case PPD_MISSING_PPDADOBE4 :
293 _cupsLangPuts(stdout,
294 _(" REF: Page 42, section 5.2.\n"));
295 break;
296 case PPD_MISSING_VALUE :
297 _cupsLangPuts(stdout,
298 _(" REF: Page 20, section 3.4.\n"));
299 break;
300 case PPD_BAD_OPEN_GROUP :
301 case PPD_NESTED_OPEN_GROUP :
302 _cupsLangPuts(stdout,
303 _(" REF: Pages 45-46, section 5.2.\n"));
304 break;
305 case PPD_BAD_OPEN_UI :
306 case PPD_NESTED_OPEN_UI :
307 _cupsLangPuts(stdout,
308 _(" REF: Pages 42-45, section 5.2.\n"));
309 break;
310 case PPD_BAD_ORDER_DEPENDENCY :
311 _cupsLangPuts(stdout,
312 _(" REF: Pages 48-49, section 5.2.\n"));
313 break;
314 case PPD_BAD_UI_CONSTRAINTS :
315 _cupsLangPuts(stdout,
316 _(" REF: Pages 52-54, section 5.2.\n"));
317 break;
318 case PPD_MISSING_ASTERISK :
319 _cupsLangPuts(stdout,
320 _(" REF: Page 15, section 3.2.\n"));
321 break;
322 case PPD_LINE_TOO_LONG :
323 _cupsLangPuts(stdout,
324 _(" REF: Page 15, section 3.1.\n"));
325 break;
326 case PPD_ILLEGAL_CHARACTER :
327 _cupsLangPuts(stdout,
328 _(" REF: Page 15, section 3.1.\n"));
329 break;
330 case PPD_ILLEGAL_MAIN_KEYWORD :
331 _cupsLangPuts(stdout,
332 _(" REF: Pages 16-17, section 3.2.\n"));
333 break;
334 case PPD_ILLEGAL_OPTION_KEYWORD :
335 _cupsLangPuts(stdout,
336 _(" REF: Page 19, section 3.3.\n"));
337 break;
338 case PPD_ILLEGAL_TRANSLATION :
339 _cupsLangPuts(stdout,
340 _(" REF: Page 27, section 3.5.\n"));
341 break;
342 default :
343 break;
344 }
345
346 check_basics(argv[i]);
347 }
348 }
349
350 continue;
351 }
352
353 /*
354 * Show the header and then perform basic conformance tests (limited
355 * only by what the CUPS PPD functions actually load...)
356 */
357
358 errors = 0;
359 ppdversion = 43;
360
361 if (verbose > 0)
362 _cupsLangPuts(stdout,
363 _("\n DETAILED CONFORMANCE TEST RESULTS\n"));
364
365 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
366 attr->value)
367 ppdversion = (int)(10 * atof(attr->value) + 0.5);
368
369 for (j = 0; j < ppd->num_filters; j ++)
370 if (strstr(ppd->filters[j], "application/vnd.cups-raster"))
371 {
372 if (!test_raster(ppd, verbose))
373 errors ++;
374 break;
375 }
376
377 /*
378 * Look for default keywords with no matching option...
379 */
380
381 if (!(warn & WARN_DEFAULTS))
382 errors = check_defaults(ppd, errors, verbose, 0);
383
384 if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
385 {
386 if (verbose >= 0)
387 {
388 if (!errors && !verbose)
389 _cupsLangPuts(stdout, _(" FAIL\n"));
390
391 _cupsLangPuts(stdout,
392 _(" **FAIL** REQUIRED DefaultImageableArea\n"
393 " REF: Page 102, section 5.15.\n"));
394 }
395
396 errors ++;
397 }
398 else if (ppdPageSize(ppd, attr->value) == NULL &&
399 strcmp(attr->value, "Unknown"))
400 {
401 if (verbose >= 0)
402 {
403 if (!errors && !verbose)
404 _cupsLangPuts(stdout, _(" FAIL\n"));
405
406 _cupsLangPrintf(stdout,
407 _(" **FAIL** BAD DefaultImageableArea %s!\n"
408 " REF: Page 102, section 5.15.\n"),
409 attr->value);
410 }
411
412 errors ++;
413 }
414 else
415 {
416 if (verbose > 0)
417 _cupsLangPuts(stdout, _(" PASS DefaultImageableArea\n"));
418 }
419
420 if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
421 {
422 if (verbose >= 0)
423 {
424 if (!errors && !verbose)
425 _cupsLangPuts(stdout, _(" FAIL\n"));
426
427 _cupsLangPuts(stdout,
428 _(" **FAIL** REQUIRED DefaultPaperDimension\n"
429 " REF: Page 103, section 5.15.\n"));
430 }
431
432 errors ++;
433 }
434 else if (ppdPageSize(ppd, attr->value) == NULL &&
435 strcmp(attr->value, "Unknown"))
436 {
437 if (verbose >= 0)
438 {
439 if (!errors && !verbose)
440 _cupsLangPuts(stdout, _(" FAIL\n"));
441
442 _cupsLangPrintf(stdout,
443 _(" **FAIL** BAD DefaultPaperDimension %s!\n"
444 " REF: Page 103, section 5.15.\n"),
445 attr->value);
446 }
447
448 errors ++;
449 }
450 else if (verbose > 0)
451 _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension\n"));
452
453 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
454 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
455 {
456 /*
457 * Verify that we have a default choice...
458 */
459
460 if (option->defchoice[0])
461 {
462 if (ppdFindChoice(option, option->defchoice) == NULL &&
463 strcmp(option->defchoice, "Unknown"))
464 {
465 if (verbose >= 0)
466 {
467 if (!errors && !verbose)
468 _cupsLangPuts(stdout, _(" FAIL\n"));
469
470 _cupsLangPrintf(stdout,
471 _(" **FAIL** BAD Default%s %s\n"
472 " REF: Page 40, section 4.5.\n"),
473 option->keyword, option->defchoice);
474 }
475
476 errors ++;
477 }
478 else if (verbose > 0)
479 _cupsLangPrintf(stdout,
480 _(" PASS Default%s\n"),
481 option->keyword);
482 }
483 else
484 {
485 if (verbose >= 0)
486 {
487 if (!errors && !verbose)
488 _cupsLangPuts(stdout, _(" FAIL\n"));
489
490 _cupsLangPrintf(stdout,
491 _(" **FAIL** REQUIRED Default%s\n"
492 " REF: Page 40, section 4.5.\n"),
493 option->keyword);
494 }
495
496 errors ++;
497 }
498 }
499
500 if (ppdFindAttr(ppd, "FileVersion", NULL) != NULL)
501 {
502 if (verbose > 0)
503 _cupsLangPuts(stdout, _(" PASS FileVersion\n"));
504 }
505 else
506 {
507 if (verbose >= 0)
508 {
509 if (!errors && !verbose)
510 _cupsLangPuts(stdout, _(" FAIL\n"));
511
512 _cupsLangPuts(stdout,
513 _(" **FAIL** REQUIRED FileVersion\n"
514 " REF: Page 56, section 5.3.\n"));
515 }
516
517 errors ++;
518 }
519
520 if (ppdFindAttr(ppd, "FormatVersion", NULL) != NULL)
521 {
522 if (verbose > 0)
523 _cupsLangPuts(stdout, _(" PASS FormatVersion\n"));
524 }
525 else
526 {
527 if (verbose >= 0)
528 {
529 if (!errors && !verbose)
530 _cupsLangPuts(stdout, _(" FAIL\n"));
531
532 _cupsLangPuts(stdout,
533 _(" **FAIL** REQUIRED FormatVersion\n"
534 " REF: Page 56, section 5.3.\n"));
535 }
536
537 errors ++;
538 }
539
540 if (ppd->lang_encoding != NULL)
541 {
542 if (verbose > 0)
543 _cupsLangPuts(stdout, _(" PASS LanguageEncoding\n"));
544 }
545 else if (ppdversion > 40)
546 {
547 if (verbose >= 0)
548 {
549 if (!errors && !verbose)
550 _cupsLangPuts(stdout, _(" FAIL\n"));
551
552 _cupsLangPuts(stdout,
553 _(" **FAIL** REQUIRED LanguageEncoding\n"
554 " REF: Pages 56-57, section 5.3.\n"));
555 }
556
557 errors ++;
558 }
559
560 if (ppd->lang_version != NULL)
561 {
562 if (verbose > 0)
563 _cupsLangPuts(stdout, _(" PASS LanguageVersion\n"));
564 }
565 else
566 {
567 if (verbose >= 0)
568 {
569 if (!errors && !verbose)
570 _cupsLangPuts(stdout, _(" FAIL\n"));
571
572 _cupsLangPuts(stdout,
573 _(" **FAIL** REQUIRED LanguageVersion\n"
574 " REF: Pages 57-58, section 5.3.\n"));
575 }
576
577 errors ++;
578 }
579
580 if (ppd->manufacturer != NULL)
581 {
582 if (!strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) ||
583 !strncasecmp(ppd->manufacturer, "Hewlett Packard", 15))
584 {
585 if (verbose >= 0)
586 {
587 if (!errors && !verbose)
588 _cupsLangPuts(stdout, _(" FAIL\n"));
589
590 _cupsLangPuts(stdout,
591 _(" **FAIL** BAD Manufacturer (should be "
592 "\"HP\")\n"
593 " REF: Page 211, table D.1.\n"));
594 }
595
596 errors ++;
597 }
598 else if (!strncasecmp(ppd->manufacturer, "OkiData", 7) ||
599 !strncasecmp(ppd->manufacturer, "Oki Data", 8))
600 {
601 if (verbose >= 0)
602 {
603 if (!errors && !verbose)
604 _cupsLangPuts(stdout, _(" FAIL\n"));
605
606 _cupsLangPuts(stdout,
607 _(" **FAIL** BAD Manufacturer (should be "
608 "\"Oki\")\n"
609 " REF: Page 211, table D.1.\n"));
610 }
611
612 errors ++;
613 }
614 else if (verbose > 0)
615 _cupsLangPuts(stdout, _(" PASS Manufacturer\n"));
616 }
617 else if (ppdversion >= 43)
618 {
619 if (verbose >= 0)
620 {
621 if (!errors && !verbose)
622 _cupsLangPuts(stdout, _(" FAIL\n"));
623
624 _cupsLangPuts(stdout,
625 _(" **FAIL** REQUIRED Manufacturer\n"
626 " REF: Pages 58-59, section 5.3.\n"));
627 }
628
629 errors ++;
630 }
631
632 if (ppd->modelname != NULL)
633 {
634 for (ptr = ppd->modelname; *ptr; ptr ++)
635 if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr))
636 break;
637
638 if (*ptr)
639 {
640 if (verbose >= 0)
641 {
642 if (!errors && !verbose)
643 _cupsLangPuts(stdout, _(" FAIL\n"));
644
645 _cupsLangPrintf(stdout,
646 _(" **FAIL** BAD ModelName - \"%c\" not "
647 "allowed in string.\n"
648 " REF: Pages 59-60, section 5.3.\n"),
649 *ptr);
650 }
651
652 errors ++;
653 }
654 else if (verbose > 0)
655 _cupsLangPuts(stdout, _(" PASS ModelName\n"));
656 }
657 else
658 {
659 if (verbose >= 0)
660 {
661 if (!errors && !verbose)
662 _cupsLangPuts(stdout, _(" FAIL\n"));
663
664 _cupsLangPuts(stdout,
665 _(" **FAIL** REQUIRED ModelName\n"
666 " REF: Pages 59-60, section 5.3.\n"));
667 }
668
669 errors ++;
670 }
671
672 if (ppd->nickname != NULL)
673 {
674 if (verbose > 0)
675 _cupsLangPuts(stdout, _(" PASS NickName\n"));
676 }
677 else
678 {
679 if (verbose >= 0)
680 {
681 if (!errors && !verbose)
682 _cupsLangPuts(stdout, _(" FAIL\n"));
683
684 _cupsLangPuts(stdout,
685 _(" **FAIL** REQUIRED NickName\n"
686 " REF: Page 60, section 5.3.\n"));
687 }
688
689 errors ++;
690 }
691
692 if (ppdFindOption(ppd, "PageSize") != NULL)
693 {
694 if (verbose > 0)
695 _cupsLangPuts(stdout, _(" PASS PageSize\n"));
696 }
697 else
698 {
699 if (verbose >= 0)
700 {
701 if (!errors && !verbose)
702 _cupsLangPuts(stdout, _(" FAIL\n"));
703
704 _cupsLangPuts(stdout,
705 _(" **FAIL** REQUIRED PageSize\n"
706 " REF: Pages 99-100, section 5.14.\n"));
707 }
708
709 errors ++;
710 }
711
712 if (ppdFindOption(ppd, "PageRegion") != NULL)
713 {
714 if (verbose > 0)
715 _cupsLangPuts(stdout, _(" PASS PageRegion\n"));
716 }
717 else
718 {
719 if (verbose >= 0)
720 {
721 if (!errors && !verbose)
722 _cupsLangPuts(stdout, _(" FAIL\n"));
723
724 _cupsLangPuts(stdout,
725 _(" **FAIL** REQUIRED PageRegion\n"
726 " REF: Page 100, section 5.14.\n"));
727 }
728
729 errors ++;
730 }
731
732 if (ppd->pcfilename != NULL)
733 {
734 if (verbose > 0)
735 _cupsLangPuts(stdout, _(" PASS PCFileName\n"));
736 }
737 else
738 {
739 if (verbose >= 0)
740 {
741 if (!errors && !verbose)
742 _cupsLangPuts(stdout, _(" FAIL\n"));
743
744 _cupsLangPuts(stdout,
745 _(" **FAIL** REQUIRED PCFileName\n"
746 " REF: Pages 61-62, section 5.3.\n"));
747 }
748
749 errors ++;
750 }
751
752 if (ppd->product != NULL)
753 {
754 if (ppd->product[0] != '(' ||
755 ppd->product[strlen(ppd->product) - 1] != ')')
756 {
757 if (verbose >= 0)
758 {
759 if (!errors && !verbose)
760 _cupsLangPuts(stdout, _(" FAIL\n"));
761
762 _cupsLangPuts(stdout,
763 _(" **FAIL** BAD Product - not \"(string)\".\n"
764 " REF: Page 62, section 5.3.\n"));
765 }
766
767 errors ++;
768 }
769 else if (verbose > 0)
770 _cupsLangPuts(stdout, _(" PASS Product\n"));
771 }
772 else
773 {
774 if (verbose >= 0)
775 {
776 if (!errors && !verbose)
777 _cupsLangPuts(stdout, _(" FAIL\n"));
778
779 _cupsLangPuts(stdout,
780 _(" **FAIL** REQUIRED Product\n"
781 " REF: Page 62, section 5.3.\n"));
782 }
783
784 errors ++;
785 }
786
787 if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
788 attr->value != NULL)
789 {
790 char junkstr[255]; /* Temp string */
791 int junkint; /* Temp integer */
792
793
794 if (sscanf(attr->value, "(%[^)])%d", junkstr, &junkint) != 2)
795 {
796 if (verbose >= 0)
797 {
798 if (!errors && !verbose)
799 _cupsLangPuts(stdout, _(" FAIL\n"));
800
801 _cupsLangPuts(stdout,
802 _(" **FAIL** BAD PSVersion - not \"(string) "
803 "int\".\n"
804 " REF: Pages 62-64, section 5.3.\n"));
805 }
806
807 errors ++;
808 }
809 else if (verbose > 0)
810 _cupsLangPuts(stdout, _(" PASS PSVersion\n"));
811 }
812 else
813 {
814 if (verbose >= 0)
815 {
816 if (!errors && !verbose)
817 _cupsLangPuts(stdout, _(" FAIL\n"));
818
819 _cupsLangPuts(stdout,
820 _(" **FAIL** REQUIRED PSVersion\n"
821 " REF: Pages 62-64, section 5.3.\n"));
822 }
823
824 errors ++;
825 }
826
827 if (ppd->shortnickname != NULL)
828 {
829 if (strlen(ppd->shortnickname) > 31)
830 {
831 if (verbose >= 0)
832 {
833 if (!errors && !verbose)
834 _cupsLangPuts(stdout, _(" FAIL\n"));
835
836 _cupsLangPuts(stdout,
837 _(" **FAIL** BAD ShortNickName - longer "
838 "than 31 chars.\n"
839 " REF: Pages 64-65, section 5.3.\n"));
840 }
841
842 errors ++;
843 }
844 else if (verbose > 0)
845 _cupsLangPuts(stdout, _(" PASS ShortNickName\n"));
846 }
847 else if (ppdversion >= 43)
848 {
849 if (verbose >= 0)
850 {
851 if (!errors && !verbose)
852 _cupsLangPuts(stdout, _(" FAIL\n"));
853
854 _cupsLangPuts(stdout,
855 _(" **FAIL** REQUIRED ShortNickName\n"
856 " REF: Page 64-65, section 5.3.\n"));
857 }
858
859 errors ++;
860 }
861
862 if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
863 strstr(ppd->patches, "*End"))
864 {
865 if (verbose >= 0)
866 {
867 if (!errors && !verbose)
868 _cupsLangPuts(stdout, _(" FAIL\n"));
869
870 _cupsLangPuts(stdout,
871 _(" **FAIL** BAD JobPatchFile attribute in file\n"
872 " REF: Page 24, section 3.4.\n"));
873 }
874
875 errors ++;
876 }
877
878 /*
879 * Check for page sizes without the corresponding ImageableArea or
880 * PaperDimension values...
881 */
882
883 if (ppd->num_sizes == 0)
884 {
885 if (verbose >= 0)
886 {
887 if (!errors && !verbose)
888 _cupsLangPuts(stdout, _(" FAIL\n"));
889
890 _cupsLangPuts(stdout,
891 _(" **FAIL** REQUIRED PageSize\n"
892 " REF: Page 41, section 5.\n"
893 " REF: Page 99, section 5.14.\n"));
894 }
895
896 errors ++;
897 }
898 else
899 {
900 for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++)
901 {
902 /*
903 * Don't check custom size...
904 */
905
906 if (!strcmp(size->name, "Custom"))
907 continue;
908
909 /*
910 * Check for ImageableArea...
911 */
912
913 if (size->left == 0.0 && size->bottom == 0.0 &&
914 size->right == 0.0 && size->top == 0.0)
915 {
916 if (verbose >= 0)
917 {
918 if (!errors && !verbose)
919 _cupsLangPuts(stdout, _(" FAIL\n"));
920
921 _cupsLangPrintf(stdout,
922 _(" **FAIL** REQUIRED ImageableArea for "
923 "PageSize %s\n"
924 " REF: Page 41, section 5.\n"
925 " REF: Page 102, section 5.15.\n"),
926 size->name);
927 }
928
929 errors ++;
930 }
931
932 /*
933 * Check for PaperDimension...
934 */
935
936 if (size->width == 0.0 && size->length == 0.0)
937 {
938 if (verbose >= 0)
939 {
940 if (!errors && !verbose)
941 _cupsLangPuts(stdout, _(" FAIL\n"));
942
943 _cupsLangPrintf(stdout,
944 _(" **FAIL** REQUIRED PaperDimension "
945 "for PageSize %s\n"
946 " REF: Page 41, section 5.\n"
947 " REF: Page 103, section 5.15.\n"),
948 size->name);
949 }
950
951 errors ++;
952 }
953 }
954 }
955
956 /*
957 * Check for valid Resolution, JCLResolution, or SetResolution values...
958 */
959
960 if ((option = ppdFindOption(ppd, "Resolution")) == NULL)
961 if ((option = ppdFindOption(ppd, "JCLResolution")) == NULL)
962 option = ppdFindOption(ppd, "SetResolution");
963
964 if (option != NULL)
965 {
966 for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
967 {
968 /*
969 * Verify that all resolution options are of the form NNNdpi
970 * or NNNxNNNdpi...
971 */
972
973 xdpi = strtol(choice->choice, (char **)&ptr, 10);
974 if (ptr > choice->choice && xdpi > 0)
975 {
976 if (*ptr == 'x')
977 ydpi = strtol(ptr + 1, (char **)&ptr, 10);
978 else
979 ydpi = xdpi;
980 }
981 else
982 ydpi = xdpi;
983
984 if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 ||
985 strcmp(ptr, "dpi"))
986 {
987 if (verbose >= 0)
988 {
989 if (!errors && !verbose)
990 _cupsLangPuts(stdout, _(" FAIL\n"));
991
992 _cupsLangPrintf(stdout,
993 _(" **FAIL** Bad %s choice %s!\n"
994 " REF: Page 84, section 5.9\n"),
995 option->keyword, choice->choice);
996 }
997
998 errors ++;
999 }
1000 }
1001 }
1002
1003 /*
1004 * Check for a duplex option, and for standard values...
1005 */
1006
1007 if ((option = ppdFindOption(ppd, "Duplex")) == NULL)
1008 if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1009 if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
1010 option = ppdFindOption(ppd, "KD03Duplex");
1011
1012 if (option != NULL)
1013 {
1014 if (ppdFindChoice(option, "None") == NULL)
1015 {
1016 if (verbose >= 0)
1017 {
1018 if (!errors && !verbose)
1019 _cupsLangPuts(stdout, _(" FAIL\n"));
1020
1021 _cupsLangPrintf(stdout,
1022 _(" **FAIL** REQUIRED %s does not define "
1023 "choice None!\n"
1024 " REF: Page 122, section 5.17\n"),
1025 option->keyword);
1026 }
1027
1028 errors ++;
1029 }
1030
1031 for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
1032 if (strcmp(choice->choice, "None") &&
1033 strcmp(choice->choice, "DuplexNoTumble") &&
1034 strcmp(choice->choice, "DuplexTumble") &&
1035 strcmp(choice->choice, "SimplexTumble"))
1036 {
1037 if (verbose >= 0)
1038 {
1039 if (!errors && !verbose)
1040 _cupsLangPuts(stdout, _(" FAIL\n"));
1041
1042 _cupsLangPrintf(stdout,
1043 _(" **FAIL** Bad %s choice %s!\n"
1044 " REF: Page 122, section 5.17\n"),
1045 option->keyword, choice->choice);
1046 }
1047
1048 errors ++;
1049 }
1050 }
1051
1052 if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
1053 strcmp(attr->name, "1284DeviceID"))
1054 {
1055 if (verbose >= 0)
1056 {
1057 if (!errors && !verbose)
1058 _cupsLangPuts(stdout, _(" FAIL\n"));
1059
1060 _cupsLangPrintf(stdout,
1061 _(" **FAIL** %s must be 1284DeviceID!\n"
1062 " REF: Page 72, section 5.5\n"),
1063 attr->name);
1064 }
1065
1066 errors ++;
1067 }
1068
1069 if (!(warn & WARN_CONSTRAINTS))
1070 errors = check_constraints(ppd, errors, verbose, 0);
1071
1072 if (!(warn & WARN_FILTERS))
1073 errors = check_filters(ppd, root, errors, verbose, 0);
1074
1075 if (!(warn & WARN_TRANSLATIONS))
1076 errors = check_translations(ppd, errors, verbose, 0);
1077
1078 if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
1079 attr->value)
1080 {
1081 /*
1082 * This file contains localizations, check for conformance of the
1083 * base translation...
1084 */
1085
1086 if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL)
1087 {
1088 if (!attr->value || strcmp(attr->value, "ISOLatin1"))
1089 {
1090 if (!errors && !verbose)
1091 _cupsLangPuts(stdout, _(" FAIL\n"));
1092
1093 if (verbose >= 0)
1094 _cupsLangPrintf(stdout,
1095 _(" **FAIL** Bad LanguageEncoding %s - "
1096 "must be ISOLatin1!\n"),
1097 attr->value ? attr->value : "(null)");
1098
1099 errors ++;
1100 }
1101
1102 if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
1103 {
1104 if (!errors && !verbose)
1105 _cupsLangPuts(stdout, _(" FAIL\n"));
1106
1107 if (verbose >= 0)
1108 _cupsLangPrintf(stdout,
1109 _(" **FAIL** Bad LanguageVersion %s - "
1110 "must be English!\n"),
1111 ppd->lang_version ? ppd->lang_version : "(null)");
1112
1113 errors ++;
1114 }
1115
1116 /*
1117 * Loop through all options and choices...
1118 */
1119
1120 for (option = ppdFirstOption(ppd);
1121 option;
1122 option = ppdNextOption(ppd))
1123 {
1124 /*
1125 * Check for special characters outside A0 to BF, F7, or F8
1126 * that are used for languages other than English.
1127 */
1128
1129 for (ptr = option->text; *ptr; ptr ++)
1130 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1131 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1132 break;
1133
1134 if (*ptr)
1135 {
1136 if (!errors && !verbose)
1137 _cupsLangPuts(stdout, _(" FAIL\n"));
1138
1139 if (verbose >= 0)
1140 _cupsLangPrintf(stdout,
1141 _(" **FAIL** Default translation "
1142 "string for option %s contains 8-bit "
1143 "characters!\n"),
1144 option->keyword);
1145
1146 errors ++;
1147 }
1148
1149 for (j = 0; j < option->num_choices; j ++)
1150 {
1151 /*
1152 * Check for special characters outside A0 to BF, F7, or F8
1153 * that are used for languages other than English.
1154 */
1155
1156 for (ptr = option->choices[j].text; *ptr; ptr ++)
1157 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1158 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1159 break;
1160
1161 if (*ptr)
1162 {
1163 if (!errors && !verbose)
1164 _cupsLangPuts(stdout, _(" FAIL\n"));
1165
1166 if (verbose >= 0)
1167 _cupsLangPrintf(stdout,
1168 _(" **FAIL** Default translation "
1169 "string for option %s choice %s contains "
1170 "8-bit characters!\n"),
1171 option->keyword,
1172 option->choices[j].choice);
1173
1174 errors ++;
1175 }
1176 }
1177 }
1178 }
1179 }
1180
1181 /*
1182 * Final pass/fail notification...
1183 */
1184
1185 if (errors)
1186 status = ERROR_CONFORMANCE;
1187 else if (!verbose)
1188 _cupsLangPuts(stdout, _(" PASS\n"));
1189
1190 if (verbose >= 0)
1191 {
1192 check_basics(argv[i]);
1193
1194 if (warn & WARN_CONSTRAINTS)
1195 errors = check_constraints(ppd, errors, verbose, 1);
1196
1197 if (warn & WARN_DEFAULTS)
1198 errors = check_defaults(ppd, errors, verbose, 1);
1199
1200 if (warn & WARN_FILTERS)
1201 errors = check_filters(ppd, root, errors, verbose, 1);
1202
1203 if (warn & WARN_TRANSLATIONS)
1204 errors = check_translations(ppd, errors, verbose, 1);
1205
1206 /*
1207 * Look for default keywords with no corresponding option...
1208 */
1209
1210 for (j = 0; j < ppd->num_attrs; j ++)
1211 {
1212 attr = ppd->attrs[j];
1213
1214 if (!strcmp(attr->name, "DefaultColorSpace") ||
1215 !strcmp(attr->name, "DefaultColorSep") ||
1216 !strcmp(attr->name, "DefaultFont") ||
1217 !strcmp(attr->name, "DefaultHalftoneType") ||
1218 !strcmp(attr->name, "DefaultImageableArea") ||
1219 !strcmp(attr->name, "DefaultLeadingEdge") ||
1220 !strcmp(attr->name, "DefaultOutputOrder") ||
1221 !strcmp(attr->name, "DefaultPaperDimension") ||
1222 !strcmp(attr->name, "DefaultResolution") ||
1223 !strcmp(attr->name, "DefaultScreenProc") ||
1224 !strcmp(attr->name, "DefaultTransfer"))
1225 continue;
1226
1227 if (!strncmp(attr->name, "Default", 7) &&
1228 !ppdFindOption(ppd, attr->name + 7))
1229 _cupsLangPrintf(stdout,
1230 _(" WARN %s has no corresponding "
1231 "options!\n"),
1232 attr->name);
1233 }
1234
1235 /*
1236 * Check for old Duplex option names...
1237 */
1238
1239 if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
1240 option = ppdFindOption(ppd, "KD03Duplex");
1241
1242 if (option)
1243 {
1244 _cupsLangPrintf(stdout,
1245 _(" WARN Duplex option keyword %s "
1246 "should be named Duplex or JCLDuplex!\n"
1247 " REF: Page 122, section 5.17\n"),
1248 option->keyword);
1249 }
1250
1251 ppdMarkDefaults(ppd);
1252 if (ppdConflicts(ppd))
1253 {
1254 _cupsLangPuts(stdout,
1255 _(" WARN Default choices conflicting!\n"));
1256
1257 show_conflicts(ppd);
1258 }
1259
1260 if (ppdversion < 43)
1261 {
1262 _cupsLangPrintf(stdout,
1263 _(" WARN Obsolete PPD version %.1f!\n"
1264 " REF: Page 42, section 5.2.\n"),
1265 0.1f * ppdversion);
1266 }
1267
1268 if (!ppd->lang_encoding && ppdversion < 41)
1269 {
1270 _cupsLangPuts(stdout,
1271 _(" WARN LanguageEncoding required by PPD "
1272 "4.3 spec.\n"
1273 " REF: Pages 56-57, section 5.3.\n"));
1274 }
1275
1276 if (!ppd->manufacturer && ppdversion < 43)
1277 {
1278 _cupsLangPuts(stdout,
1279 _(" WARN Manufacturer required by PPD "
1280 "4.3 spec.\n"
1281 " REF: Pages 58-59, section 5.3.\n"));
1282 }
1283
1284 /*
1285 * Treat a PCFileName attribute longer than 12 characters as
1286 * a warning and not a hard error...
1287 */
1288
1289 if (ppd->pcfilename && strlen(ppd->pcfilename) > 12)
1290 {
1291 _cupsLangPuts(stdout,
1292 _(" WARN PCFileName longer than 8.3 in "
1293 "violation of PPD spec.\n"
1294 " REF: Pages 61-62, section 5.3.\n"));
1295 }
1296
1297 if (!ppd->shortnickname && ppdversion < 43)
1298 {
1299 _cupsLangPuts(stdout,
1300 _(" WARN ShortNickName required by PPD "
1301 "4.3 spec.\n"
1302 " REF: Pages 64-65, section 5.3.\n"));
1303 }
1304
1305 /*
1306 * Check the Protocols line and flag PJL + BCP since TBCP is
1307 * usually used with PJL...
1308 */
1309
1310 if (ppd->protocols)
1311 {
1312 if (strstr(ppd->protocols, "PJL") &&
1313 strstr(ppd->protocols, "BCP") &&
1314 !strstr(ppd->protocols, "TBCP"))
1315 {
1316 _cupsLangPuts(stdout,
1317 _(" WARN Protocols contains both PJL "
1318 "and BCP; expected TBCP.\n"
1319 " REF: Pages 78-79, section 5.7.\n"));
1320 }
1321
1322 if (strstr(ppd->protocols, "PJL") &&
1323 (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
1324 {
1325 _cupsLangPuts(stdout,
1326 _(" WARN Protocols contains PJL but JCL "
1327 "attributes are not set.\n"
1328 " REF: Pages 78-79, section 5.7.\n"));
1329 }
1330 }
1331
1332 /*
1333 * Check for options with a common prefix, e.g. Duplex and Duplexer,
1334 * which are errors according to the spec but won't cause problems
1335 * with CUPS specifically...
1336 */
1337
1338 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1339 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1340 {
1341 len = strlen(option->keyword);
1342
1343 for (m = 0, group2 = ppd->groups;
1344 m < ppd->num_groups;
1345 m ++, group2 ++)
1346 for (n = 0, option2 = group2->options;
1347 n < group2->num_options;
1348 n ++, option2 ++)
1349 if (option != option2 &&
1350 len < strlen(option2->keyword) &&
1351 !strncmp(option->keyword, option2->keyword, len))
1352 {
1353 _cupsLangPrintf(stdout,
1354 _(" WARN %s shares a common "
1355 "prefix with %s\n"
1356 " REF: Page 15, section "
1357 "3.2.\n"),
1358 option->keyword, option2->keyword);
1359 }
1360 }
1361 }
1362
1363 /*
1364 * cupsICCProfile
1365 */
1366
1367 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
1368 attr != NULL;
1369 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
1370 {
1371 if (attr->value)
1372 {
1373 if (attr->value[0] == '/')
1374 snprintf(pathprog, sizeof(pathprog), "%s%s", root, attr->value);
1375 else
1376 {
1377 if ((ptr = getenv("CUPS_DATADIR")) == NULL)
1378 ptr = CUPS_DATADIR;
1379
1380 if (*ptr == '/' || !*root)
1381 snprintf(pathprog, sizeof(pathprog), "%s%s/profiles/%s", root,
1382 ptr, attr->value);
1383 else
1384 snprintf(pathprog, sizeof(pathprog), "%s/%s/profiles/%s", root,
1385 ptr, attr->value);
1386 }
1387 }
1388
1389 if (!attr->value || !attr->value[0] || stat(pathprog, &statbuf))
1390 {
1391 if (verbose >= 0)
1392 _cupsLangPrintf(stdout,
1393 _(" WARN Missing cupsICCProfile "
1394 "file \"%s\"\n"),
1395 !attr->value || !attr->value[0] ? "<NULL>" :
1396 attr->value);
1397 }
1398 }
1399
1400 #ifdef __APPLE__
1401 /*
1402 * APDialogExtension
1403 */
1404
1405 for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL);
1406 attr != NULL;
1407 attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
1408 {
1409 if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
1410 _cupsLangPrintf(stdout, _(" WARN Missing "
1411 "APDialogExtension file \"%s\"\n"),
1412 attr->value ? attr->value : "<NULL>");
1413 }
1414
1415 /*
1416 * APPrinterIconPath
1417 */
1418
1419 for (attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL);
1420 attr != NULL;
1421 attr = ppdFindNextAttr(ppd, "APPrinterIconPath", NULL))
1422 {
1423 if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
1424 _cupsLangPrintf(stdout, _(" WARN Missing "
1425 "APPrinterIconPath file \"%s\"\n"),
1426 attr->value ? attr->value : "<NULL>");
1427 }
1428 #endif /* __APPLE__ */
1429
1430 if (verbose > 0)
1431 {
1432 if (errors)
1433 _cupsLangPrintf(stdout, _(" %d ERRORS FOUND\n"), errors);
1434 else
1435 _cupsLangPuts(stdout, _(" NO ERRORS FOUND\n"));
1436 }
1437
1438 /*
1439 * Then list the options, if "-v" was provided...
1440 */
1441
1442 if (verbose > 1)
1443 {
1444 _cupsLangPrintf(stdout,
1445 "\n"
1446 " language_level = %d\n"
1447 " color_device = %s\n"
1448 " variable_sizes = %s\n"
1449 " landscape = %d\n",
1450 ppd->language_level,
1451 ppd->color_device ? "TRUE" : "FALSE",
1452 ppd->variable_sizes ? "TRUE" : "FALSE",
1453 ppd->landscape);
1454
1455 switch (ppd->colorspace)
1456 {
1457 case PPD_CS_CMYK :
1458 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK\n");
1459 break;
1460 case PPD_CS_CMY :
1461 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY\n");
1462 break;
1463 case PPD_CS_GRAY :
1464 _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY\n");
1465 break;
1466 case PPD_CS_RGB :
1467 _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB\n");
1468 break;
1469 default :
1470 _cupsLangPuts(stdout, " colorspace = <unknown>\n");
1471 break;
1472 }
1473
1474 _cupsLangPrintf(stdout, " num_emulations = %d\n",
1475 ppd->num_emulations);
1476 for (j = 0; j < ppd->num_emulations; j ++)
1477 _cupsLangPrintf(stdout, " emulations[%d] = %s\n",
1478 j, ppd->emulations[j].name);
1479
1480 _cupsLangPrintf(stdout, " lang_encoding = %s\n",
1481 ppd->lang_encoding);
1482 _cupsLangPrintf(stdout, " lang_version = %s\n",
1483 ppd->lang_version);
1484 _cupsLangPrintf(stdout, " modelname = %s\n", ppd->modelname);
1485 _cupsLangPrintf(stdout, " ttrasterizer = %s\n",
1486 ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
1487 _cupsLangPrintf(stdout, " manufacturer = %s\n",
1488 ppd->manufacturer);
1489 _cupsLangPrintf(stdout, " product = %s\n", ppd->product);
1490 _cupsLangPrintf(stdout, " nickname = %s\n", ppd->nickname);
1491 _cupsLangPrintf(stdout, " shortnickname = %s\n",
1492 ppd->shortnickname);
1493 _cupsLangPrintf(stdout, " patches = %d bytes\n",
1494 ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
1495
1496 _cupsLangPrintf(stdout, " num_groups = %d\n", ppd->num_groups);
1497 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1498 {
1499 _cupsLangPrintf(stdout, " group[%d] = %s\n",
1500 j, group->text);
1501
1502 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1503 {
1504 _cupsLangPrintf(stdout,
1505 " options[%d] = %s (%s) %s %s %.0f "
1506 "(%d choices)\n",
1507 k, option->keyword, option->text, uis[option->ui],
1508 sections[option->section], option->order,
1509 option->num_choices);
1510
1511 if (!strcmp(option->keyword, "PageSize") ||
1512 !strcmp(option->keyword, "PageRegion"))
1513 {
1514 for (m = option->num_choices, choice = option->choices;
1515 m > 0;
1516 m --, choice ++)
1517 {
1518 size = ppdPageSize(ppd, choice->choice);
1519
1520 if (size == NULL)
1521 _cupsLangPrintf(stdout,
1522 " %s (%s) = ERROR",
1523 choice->choice, choice->text);
1524 else
1525 _cupsLangPrintf(stdout,
1526 " %s (%s) = %.2fx%.2fin "
1527 "(%.1f,%.1f,%.1f,%.1f)",
1528 choice->choice, choice->text,
1529 size->width / 72.0, size->length / 72.0,
1530 size->left / 72.0, size->bottom / 72.0,
1531 size->right / 72.0, size->top / 72.0);
1532
1533 if (!strcmp(option->defchoice, choice->choice))
1534 _cupsLangPuts(stdout, " *\n");
1535 else
1536 _cupsLangPuts(stdout, "\n");
1537 }
1538 }
1539 else
1540 {
1541 for (m = option->num_choices, choice = option->choices;
1542 m > 0;
1543 m --, choice ++)
1544 {
1545 _cupsLangPrintf(stdout, " %s (%s)",
1546 choice->choice, choice->text);
1547
1548 if (!strcmp(option->defchoice, choice->choice))
1549 _cupsLangPuts(stdout, " *\n");
1550 else
1551 _cupsLangPuts(stdout, "\n");
1552 }
1553 }
1554 }
1555 }
1556
1557 _cupsLangPrintf(stdout, " num_consts = %d\n",
1558 ppd->num_consts);
1559 for (j = 0; j < ppd->num_consts; j ++)
1560 _cupsLangPrintf(stdout,
1561 " consts[%d] = *%s %s *%s %s\n",
1562 j, ppd->consts[j].option1, ppd->consts[j].choice1,
1563 ppd->consts[j].option2, ppd->consts[j].choice2);
1564
1565 _cupsLangPrintf(stdout, " num_profiles = %d\n",
1566 ppd->num_profiles);
1567 for (j = 0; j < ppd->num_profiles; j ++)
1568 _cupsLangPrintf(stdout,
1569 " profiles[%d] = %s/%s %.3f %.3f "
1570 "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
1571 j, ppd->profiles[j].resolution,
1572 ppd->profiles[j].media_type,
1573 ppd->profiles[j].gamma, ppd->profiles[j].density,
1574 ppd->profiles[j].matrix[0][0],
1575 ppd->profiles[j].matrix[0][1],
1576 ppd->profiles[j].matrix[0][2],
1577 ppd->profiles[j].matrix[1][0],
1578 ppd->profiles[j].matrix[1][1],
1579 ppd->profiles[j].matrix[1][2],
1580 ppd->profiles[j].matrix[2][0],
1581 ppd->profiles[j].matrix[2][1],
1582 ppd->profiles[j].matrix[2][2]);
1583
1584 _cupsLangPrintf(stdout, " num_fonts = %d\n", ppd->num_fonts);
1585 for (j = 0; j < ppd->num_fonts; j ++)
1586 _cupsLangPrintf(stdout, " fonts[%d] = %s\n",
1587 j, ppd->fonts[j]);
1588
1589 _cupsLangPrintf(stdout, " num_attrs = %d\n", ppd->num_attrs);
1590 for (j = 0; j < ppd->num_attrs; j ++)
1591 _cupsLangPrintf(stdout,
1592 " attrs[%d] = %s %s%s%s: \"%s\"\n", j,
1593 ppd->attrs[j]->name, ppd->attrs[j]->spec,
1594 ppd->attrs[j]->text[0] ? "/" : "",
1595 ppd->attrs[j]->text,
1596 ppd->attrs[j]->value ?
1597 ppd->attrs[j]->value : "(null)");
1598 }
1599
1600 ppdClose(ppd);
1601 }
1602
1603 if (!files)
1604 usage();
1605
1606 return (status);
1607 }
1608
1609
1610 /*
1611 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1612 */
1613
1614 static void
1615 check_basics(const char *filename) /* I - PPD file to check */
1616 {
1617 cups_file_t *fp; /* File pointer */
1618 int ch; /* Current character */
1619 int col, /* Current column */
1620 whitespace; /* Only seen whitespace? */
1621 int eol; /* Line endings */
1622 int linenum; /* Line number */
1623 int mixed; /* Mixed line endings? */
1624
1625
1626 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1627 return;
1628
1629 linenum = 1;
1630 col = 0;
1631 eol = EOL_NONE;
1632 mixed = 0;
1633 whitespace = 1;
1634
1635 while ((ch = cupsFileGetChar(fp)) != EOF)
1636 {
1637 if (ch == '\r' || ch == '\n')
1638 {
1639 if (ch == '\n')
1640 {
1641 if (eol == EOL_NONE)
1642 eol = EOL_LF;
1643 else if (eol != EOL_LF)
1644 mixed = 1;
1645 }
1646 else if (ch == '\r')
1647 {
1648 if (cupsFilePeekChar(fp) == '\n')
1649 {
1650 cupsFileGetChar(fp);
1651
1652 if (eol == EOL_NONE)
1653 eol = EOL_CRLF;
1654 else
1655 mixed = 1;
1656 }
1657 else if (eol == EOL_NONE)
1658 eol = EOL_CR;
1659 else
1660 mixed = 1;
1661 }
1662
1663 if (col > 0 && whitespace)
1664 _cupsLangPrintf(stdout,
1665 _(" WARN Line %d only contains whitespace!\n"),
1666 linenum);
1667
1668 linenum ++;
1669 col = 0;
1670 whitespace = 1;
1671 }
1672 else
1673 {
1674 if (ch != ' ' && ch != '\t')
1675 whitespace = 0;
1676
1677 col ++;
1678 }
1679 }
1680
1681 if (mixed)
1682 _cupsLangPuts(stdout,
1683 _(" WARN File contains a mix of CR, LF, and "
1684 "CR LF line endings!\n"));
1685
1686 if (eol == EOL_CRLF)
1687 _cupsLangPuts(stdout,
1688 _(" WARN Non-Windows PPD files should use lines "
1689 "ending with only LF, not CR LF!\n"));
1690
1691 cupsFileClose(fp);
1692 }
1693
1694
1695 /*
1696 * 'check_constraints()' - Check UIConstraints in the PPD file.
1697 */
1698
1699 static int /* O - Errors found */
1700 check_constraints(ppd_file_t *ppd, /* I - PPD file */
1701 int errors, /* I - Errors found */
1702 int verbose, /* I - Verbosity level */
1703 int warn) /* I - Warnings only? */
1704 {
1705 int j; /* Looping var */
1706 ppd_const_t *c; /* Current constraint */
1707 ppd_option_t *option; /* Standard UI option */
1708 ppd_option_t *option2; /* Standard UI option */
1709 const char *prefix; /* WARN/FAIL prefix */
1710
1711
1712 prefix = warn ? " WARN " : "**FAIL**";
1713
1714 for (j = ppd->num_consts, c = ppd->consts; j > 0; j --, c ++)
1715 {
1716 option = ppdFindOption(ppd, c->option1);
1717 option2 = ppdFindOption(ppd, c->option2);
1718
1719 if (!option || !option2)
1720 {
1721 if (!warn && !errors && !verbose)
1722 _cupsLangPuts(stdout, _(" FAIL\n"));
1723
1724 if (!option)
1725 _cupsLangPrintf(stdout,
1726 _(" %s Missing option %s in "
1727 "UIConstraint \"*%s %s *%s %s\"!\n"),
1728 prefix, c->option1,
1729 c->option1, c->choice1, c->option2, c->choice2);
1730
1731 if (!option2)
1732 _cupsLangPrintf(stdout,
1733 _(" %s Missing option %s in "
1734 "UIConstraint \"*%s %s *%s %s\"!\n"),
1735 prefix, c->option2,
1736 c->option1, c->choice1, c->option2, c->choice2);
1737
1738 if (!warn)
1739 errors ++;
1740
1741 continue;
1742 }
1743
1744 if (c->choice1[0] && !ppdFindChoice(option, c->choice1))
1745 {
1746 if (!warn && !errors && !verbose)
1747 _cupsLangPuts(stdout, _(" FAIL\n"));
1748
1749 _cupsLangPrintf(stdout,
1750 _(" %s Missing choice *%s %s in "
1751 "UIConstraint \"*%s %s *%s %s\"!\n"),
1752 prefix, c->option1, c->choice1,
1753 c->option1, c->choice1, c->option2, c->choice2);
1754
1755 if (!warn)
1756 errors ++;
1757 }
1758
1759 if (c->choice2[0] && !ppdFindChoice(option2, c->choice2))
1760 {
1761 if (!warn && !errors && !verbose)
1762 _cupsLangPuts(stdout, _(" FAIL\n"));
1763
1764 _cupsLangPrintf(stdout,
1765 _(" %s Missing choice *%s %s in "
1766 "UIConstraint \"*%s %s *%s %s\"!\n"),
1767 prefix, c->option2, c->choice2,
1768 c->option1, c->choice1, c->option2, c->choice2);
1769
1770 if (!warn)
1771 errors ++;
1772 }
1773 }
1774
1775 return (errors);
1776 }
1777
1778
1779 /*
1780 * 'check_defaults()' - Check default option keywords in the PPD file.
1781 */
1782
1783 static int /* O - Errors found */
1784 check_defaults(ppd_file_t *ppd, /* I - PPD file */
1785 int errors, /* I - Errors found */
1786 int verbose, /* I - Verbosity level */
1787 int warn) /* I - Warnings only? */
1788 {
1789 int j, k; /* Looping vars */
1790 ppd_attr_t *attr; /* PPD attribute */
1791 ppd_option_t *option; /* Standard UI option */
1792 const char *prefix; /* WARN/FAIL prefix */
1793
1794
1795 prefix = warn ? " WARN " : "**FAIL**";
1796
1797 for (j = 0; j < ppd->num_attrs; j ++)
1798 {
1799 attr = ppd->attrs[j];
1800
1801 if (!strcmp(attr->name, "DefaultColorSpace") ||
1802 !strcmp(attr->name, "DefaultFont") ||
1803 !strcmp(attr->name, "DefaultHalftoneType") ||
1804 !strcmp(attr->name, "DefaultImageableArea") ||
1805 !strcmp(attr->name, "DefaultLeadingEdge") ||
1806 !strcmp(attr->name, "DefaultOutputOrder") ||
1807 !strcmp(attr->name, "DefaultPaperDimension") ||
1808 !strcmp(attr->name, "DefaultResolution") ||
1809 !strcmp(attr->name, "DefaultTransfer"))
1810 continue;
1811
1812 if (!strncmp(attr->name, "Default", 7))
1813 {
1814 if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
1815 strcmp(attr->value, "Unknown"))
1816 {
1817 /*
1818 * Check that the default option value matches a choice...
1819 */
1820
1821 for (k = 0; k < option->num_choices; k ++)
1822 if (!strcmp(option->choices[k].choice, attr->value))
1823 break;
1824
1825 if (k >= option->num_choices)
1826 {
1827 if (!warn && !errors && !verbose)
1828 _cupsLangPuts(stdout, _(" FAIL\n"));
1829
1830 if (verbose >= 0)
1831 _cupsLangPrintf(stdout,
1832 _(" %s %s %s does not exist!\n"),
1833 prefix, attr->name, attr->value);
1834
1835 if (!warn)
1836 errors ++;
1837 }
1838 }
1839 }
1840 }
1841
1842 return (errors);
1843 }
1844
1845
1846 /*
1847 * 'check_filters()' - Check filters in the PPD file.
1848 */
1849
1850 static int /* O - Errors found */
1851 check_filters(ppd_file_t *ppd, /* I - PPD file */
1852 const char *root, /* I - Root directory */
1853 int errors, /* I - Errors found */
1854 int verbose, /* I - Verbosity level */
1855 int warn) /* I - Warnings only? */
1856 {
1857 ppd_attr_t *attr; /* PPD attribute */
1858 const char *ptr; /* Pointer into string */
1859 struct stat statbuf; /* File information */
1860 char super[16], /* Super-type for filter */
1861 type[256], /* Type for filter */
1862 program[256], /* Program/filter name */
1863 pathprog[1024]; /* Complete path to program/filter */
1864 int cost; /* Cost of filter */
1865 const char *prefix; /* WARN/FAIL prefix */
1866
1867
1868 prefix = warn ? " WARN " : "**FAIL**";
1869
1870 for (attr = ppdFindAttr(ppd, "cupsFilter", NULL);
1871 attr;
1872 attr = ppdFindNextAttr(ppd, "cupsFilter", NULL))
1873 {
1874 if (!attr->value ||
1875 sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
1876 program) != 4)
1877 {
1878 if (!warn && !errors && !verbose)
1879 _cupsLangPuts(stdout, _(" FAIL\n"));
1880
1881 if (verbose >= 0)
1882 _cupsLangPrintf(stdout,
1883 _(" %s Bad cupsFilter value \"%s\"!\n"),
1884 prefix, attr->value ? attr->value : "");
1885
1886 if (!warn)
1887 errors ++;
1888 }
1889 else
1890 {
1891 if (program[0] == '/')
1892 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
1893 else
1894 {
1895 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
1896 ptr = CUPS_SERVERBIN;
1897
1898 if (*ptr == '/' || !*root)
1899 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
1900 program);
1901 else
1902 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
1903 program);
1904 }
1905
1906 if (stat(pathprog, &statbuf))
1907 {
1908 if (!warn && !errors && !verbose)
1909 _cupsLangPuts(stdout, _(" FAIL\n"));
1910
1911 if (verbose >= 0)
1912 _cupsLangPrintf(stdout, _(" %s Missing cupsFilter "
1913 "file \"%s\"\n"), prefix, program);
1914
1915 if (!warn)
1916 errors ++;
1917 }
1918 }
1919 }
1920
1921 for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
1922 attr;
1923 attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
1924 {
1925 if (!attr->value ||
1926 sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
1927 program) != 4)
1928 {
1929 if (!warn && !errors && !verbose)
1930 _cupsLangPuts(stdout, _(" FAIL\n"));
1931
1932 if (verbose >= 0)
1933 _cupsLangPrintf(stdout,
1934 _(" %s Bad cupsPreFilter value \"%s\"!\n"),
1935 prefix, attr->value ? attr->value : "");
1936
1937 if (!warn)
1938 errors ++;
1939 }
1940 else
1941 {
1942 if (program[0] == '/')
1943 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
1944 else
1945 {
1946 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
1947 ptr = CUPS_SERVERBIN;
1948
1949 if (*ptr == '/' || !*root)
1950 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
1951 program);
1952 else
1953 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
1954 program);
1955 }
1956
1957 if (stat(pathprog, &statbuf))
1958 {
1959 if (!warn && !errors && !verbose)
1960 _cupsLangPuts(stdout, _(" FAIL\n"));
1961
1962 if (verbose >= 0)
1963 _cupsLangPrintf(stdout, _(" %s Missing cupsPreFilter "
1964 "file \"%s\"\n"), prefix, program);
1965
1966 if (!warn)
1967 errors ++;
1968 }
1969 }
1970 }
1971
1972 return (errors);
1973 }
1974
1975
1976 /*
1977 * 'check_translations()' - Check translations in the PPD file.
1978 */
1979
1980 static int /* O - Errors found */
1981 check_translations(ppd_file_t *ppd, /* I - PPD file */
1982 int errors, /* I - Errors found */
1983 int verbose, /* I - Verbosity level */
1984 int warn) /* I - Warnings only? */
1985 {
1986 int j; /* Looping var */
1987 ppd_attr_t *attr; /* PPD attribute */
1988 char *languages, /* Copy of attribute value */
1989 *langstart, /* Start of current language */
1990 *langptr, /* Pointer into languages */
1991 keyword[PPD_MAX_NAME], /* Localization keyword (full) */
1992 llkeyword[PPD_MAX_NAME],/* Localization keyword (base) */
1993 ckeyword[PPD_MAX_NAME], /* Custom option keyword (full) */
1994 cllkeyword[PPD_MAX_NAME];
1995 /* Custom option keyword (base) */
1996 ppd_option_t *option; /* Standard UI option */
1997 ppd_coption_t *coption; /* Custom option */
1998 ppd_cparam_t *cparam; /* Custom parameter */
1999 cups_array_t *langlist; /* List of languages so far */
2000 char ll[3]; /* Base language */
2001 const char *prefix; /* WARN/FAIL prefix */
2002
2003
2004 prefix = warn ? " WARN " : "**FAIL**";
2005
2006 if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
2007 attr->value)
2008 {
2009 /*
2010 * This file contains localizations, check them...
2011 */
2012
2013 if ((languages = strdup(attr->value)) == NULL)
2014 return (1);
2015
2016 langlist = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2017
2018 for (langptr = languages; *langptr;)
2019 {
2020 /*
2021 * Skip leading whitespace...
2022 */
2023
2024 while (isspace(*langptr & 255))
2025 langptr ++;
2026
2027 if (!*langptr)
2028 break;
2029
2030 /*
2031 * Find the end of this language name...
2032 */
2033
2034 for (langstart = langptr;
2035 *langptr && !isspace(*langptr & 255);
2036 langptr ++);
2037
2038 if (*langptr)
2039 *langptr++ = '\0';
2040
2041 j = strlen(langstart);
2042 if (j != 2 && j != 5)
2043 {
2044 if (!warn && !errors && !verbose)
2045 _cupsLangPuts(stdout, _(" FAIL\n"));
2046
2047 if (verbose >= 0)
2048 _cupsLangPrintf(stdout,
2049 _(" %s Bad language \"%s\"!\n"),
2050 prefix, langstart);
2051
2052 if (!warn)
2053 errors ++;
2054
2055 continue;
2056 }
2057
2058 if (!strcmp(langstart, "en"))
2059 continue;
2060
2061 cupsArrayAdd(langlist, langstart);
2062
2063 strlcpy(ll, langstart, sizeof(ll));
2064
2065 /*
2066 * Loop through all options and choices...
2067 */
2068
2069 for (option = ppdFirstOption(ppd);
2070 option;
2071 option = ppdNextOption(ppd))
2072 {
2073 if (!strcmp(option->keyword, "PageRegion"))
2074 continue;
2075
2076 snprintf(keyword, sizeof(keyword), "%s.Translation", langstart);
2077 snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll);
2078
2079 if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL &&
2080 (attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL)
2081 {
2082 if (!warn && !errors && !verbose)
2083 _cupsLangPuts(stdout, _(" FAIL\n"));
2084
2085 if (verbose >= 0)
2086 _cupsLangPrintf(stdout,
2087 _(" %s Missing \"%s\" translation "
2088 "string for option %s!\n"),
2089 prefix, langstart, option->keyword);
2090
2091 if (!warn)
2092 errors ++;
2093 }
2094 else if (!valid_utf8(attr->text))
2095 {
2096 if (!warn && !errors && !verbose)
2097 _cupsLangPuts(stdout, _(" FAIL\n"));
2098
2099 if (verbose >= 0)
2100 _cupsLangPrintf(stdout,
2101 _(" %s Bad UTF-8 \"%s\" translation "
2102 "string for option %s!\n"),
2103 prefix, langstart, option->keyword);
2104
2105 if (!warn)
2106 errors ++;
2107 }
2108
2109 snprintf(keyword, sizeof(keyword), "%s.%s", langstart,
2110 option->keyword);
2111 snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll,
2112 option->keyword);
2113
2114 for (j = 0; j < option->num_choices; j ++)
2115 {
2116 if (!strcasecmp(option->choices[j].choice, "Custom") &&
2117 (coption = ppdFindCustomOption(ppd,
2118 option->keyword)) != NULL)
2119 {
2120 snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
2121 langstart, option->keyword);
2122
2123 if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL &&
2124 !valid_utf8(attr->text))
2125 {
2126 if (!warn && !errors && !verbose)
2127 _cupsLangPuts(stdout, _(" FAIL\n"));
2128
2129 if (verbose >= 0)
2130 _cupsLangPrintf(stdout,
2131 _(" %s Bad UTF-8 \"%s\" "
2132 "translation string for option %s, "
2133 "choice %s!\n"),
2134 prefix, langstart,
2135 ckeyword + 1 + strlen(langstart),
2136 "True");
2137
2138 if (!warn)
2139 errors ++;
2140 }
2141
2142 if (strcasecmp(option->keyword, "PageSize"))
2143 {
2144 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
2145 cparam;
2146 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
2147 {
2148 snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
2149 langstart, option->keyword);
2150 snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s",
2151 ll, option->keyword);
2152
2153 if ((attr = ppdFindAttr(ppd, ckeyword,
2154 cparam->name)) == NULL &&
2155 (attr = ppdFindAttr(ppd, cllkeyword,
2156 cparam->name)) == NULL)
2157 {
2158 if (!warn && !errors && !verbose)
2159 _cupsLangPuts(stdout, _(" FAIL\n"));
2160
2161 if (verbose >= 0)
2162 _cupsLangPrintf(stdout,
2163 _(" %s Missing \"%s\" "
2164 "translation string for option %s, "
2165 "choice %s!\n"),
2166 prefix, langstart,
2167 ckeyword + 1 + strlen(langstart),
2168 cparam->name);
2169
2170 if (!warn)
2171 errors ++;
2172 }
2173 else if (!valid_utf8(attr->text))
2174 {
2175 if (!warn && !errors && !verbose)
2176 _cupsLangPuts(stdout, _(" FAIL\n"));
2177
2178 if (verbose >= 0)
2179 _cupsLangPrintf(stdout,
2180 _(" %s Bad UTF-8 \"%s\" "
2181 "translation string for option %s, "
2182 "choice %s!\n"),
2183 prefix, langstart,
2184 ckeyword + 1 + strlen(langstart),
2185 cparam->name);
2186
2187 if (!warn)
2188 errors ++;
2189 }
2190 }
2191 }
2192 }
2193 else if ((attr = ppdFindAttr(ppd, keyword,
2194 option->choices[j].choice)) == NULL &&
2195 (attr = ppdFindAttr(ppd, llkeyword,
2196 option->choices[j].choice)) == NULL)
2197 {
2198 if (!warn && !errors && !verbose)
2199 _cupsLangPuts(stdout, _(" FAIL\n"));
2200
2201 if (verbose >= 0)
2202 _cupsLangPrintf(stdout,
2203 _(" %s Missing \"%s\" "
2204 "translation string for option %s, "
2205 "choice %s!\n"),
2206 prefix, langstart, option->keyword,
2207 option->choices[j].choice);
2208
2209 if (!warn)
2210 errors ++;
2211 }
2212 else if (!valid_utf8(attr->text))
2213 {
2214 if (!warn && !errors && !verbose)
2215 _cupsLangPuts(stdout, _(" FAIL\n"));
2216
2217 if (verbose >= 0)
2218 _cupsLangPrintf(stdout,
2219 _(" %s Bad UTF-8 \"%s\" "
2220 "translation string for option %s, "
2221 "choice %s!\n"),
2222 prefix, langstart, option->keyword,
2223 option->choices[j].choice);
2224
2225 if (!warn)
2226 errors ++;
2227 }
2228 }
2229 }
2230 }
2231
2232 /*
2233 * Verify that we have the base language for each localized one...
2234 */
2235
2236 for (langptr = (char *)cupsArrayFirst(langlist);
2237 langptr;
2238 langptr = (char *)cupsArrayNext(langlist))
2239 if (langptr[2])
2240 {
2241 /*
2242 * Lookup the base language...
2243 */
2244
2245 cupsArraySave(langlist);
2246
2247 strlcpy(ll, langptr, sizeof(ll));
2248
2249 if (!cupsArrayFind(langlist, ll) && strcmp(ll, "zh"))
2250 {
2251 if (!warn && !errors && !verbose)
2252 _cupsLangPuts(stdout, _(" FAIL\n"));
2253
2254 if (verbose >= 0)
2255 _cupsLangPrintf(stdout,
2256 _(" %s No base translation \"%s\" "
2257 "is included in file!\n"), prefix, ll);
2258
2259 if (!warn)
2260 errors ++;
2261 }
2262
2263 cupsArrayRestore(langlist);
2264 }
2265
2266 /*
2267 * Free memory used for the languages...
2268 */
2269
2270 cupsArrayDelete(langlist);
2271 free(languages);
2272 }
2273
2274 return (errors);
2275 }
2276
2277
2278 /*
2279 * 'show_conflicts()' - Show option conflicts in a PPD file.
2280 */
2281
2282 static void
2283 show_conflicts(ppd_file_t *ppd) /* I - PPD to check */
2284 {
2285 int i, j; /* Looping variables */
2286 ppd_const_t *c; /* Current constraint */
2287 ppd_option_t *o1, *o2; /* Options */
2288 ppd_choice_t *c1, *c2; /* Choices */
2289
2290
2291 /*
2292 * Loop through all of the UI constraints and report any options
2293 * that conflict...
2294 */
2295
2296 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
2297 {
2298 /*
2299 * Grab pointers to the first option...
2300 */
2301
2302 o1 = ppdFindOption(ppd, c->option1);
2303
2304 if (o1 == NULL)
2305 continue;
2306 else if (c->choice1[0] != '\0')
2307 {
2308 /*
2309 * This constraint maps to a specific choice.
2310 */
2311
2312 c1 = ppdFindChoice(o1, c->choice1);
2313 }
2314 else
2315 {
2316 /*
2317 * This constraint applies to any choice for this option.
2318 */
2319
2320 for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
2321 if (c1->marked)
2322 break;
2323
2324 if (j == 0 ||
2325 !strcasecmp(c1->choice, "None") ||
2326 !strcasecmp(c1->choice, "Off") ||
2327 !strcasecmp(c1->choice, "False"))
2328 c1 = NULL;
2329 }
2330
2331 /*
2332 * Grab pointers to the second option...
2333 */
2334
2335 o2 = ppdFindOption(ppd, c->option2);
2336
2337 if (o2 == NULL)
2338 continue;
2339 else if (c->choice2[0] != '\0')
2340 {
2341 /*
2342 * This constraint maps to a specific choice.
2343 */
2344
2345 c2 = ppdFindChoice(o2, c->choice2);
2346 }
2347 else
2348 {
2349 /*
2350 * This constraint applies to any choice for this option.
2351 */
2352
2353 for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
2354 if (c2->marked)
2355 break;
2356
2357 if (j == 0 ||
2358 !strcasecmp(c2->choice, "None") ||
2359 !strcasecmp(c2->choice, "Off") ||
2360 !strcasecmp(c2->choice, "False"))
2361 c2 = NULL;
2362 }
2363
2364 /*
2365 * If both options are marked then there is a conflict...
2366 */
2367
2368 if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
2369 _cupsLangPrintf(stdout,
2370 _(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
2371 " (constraint=\"%s %s %s %s\")\n"),
2372 o1->keyword, c1->choice, o2->keyword, c2->choice,
2373 c->option1, c->choice1, c->option2, c->choice2);
2374 }
2375 }
2376
2377
2378 /*
2379 * 'test_raster()' - Test PostScript commands for raster printers.
2380 */
2381
2382 static int /* O - 1 on success, 0 on failure */
2383 test_raster(ppd_file_t *ppd, /* I - PPD file */
2384 int verbose) /* I - Verbosity */
2385 {
2386 cups_page_header2_t header; /* Page header */
2387
2388
2389 ppdMarkDefaults(ppd);
2390 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
2391 {
2392 if (!verbose)
2393 _cupsLangPuts(stdout, _(" FAIL\n"));
2394
2395 if (verbose >= 0)
2396 _cupsLangPrintf(stdout,
2397 _(" **FAIL** Default option code cannot be "
2398 "interpreted: %s\n"), cupsRasterErrorString());
2399
2400 return (0);
2401 }
2402
2403 /*
2404 * Try a test of custom page size code, if available...
2405 */
2406
2407 if (!ppdPageSize(ppd, "Custom.612x792"))
2408 return (1);
2409
2410 ppdMarkOption(ppd, "PageSize", "Custom.612x792");
2411
2412 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
2413 {
2414 if (!verbose)
2415 _cupsLangPuts(stdout, _(" FAIL\n"));
2416
2417 if (verbose >= 0)
2418 _cupsLangPrintf(stdout,
2419 _(" **FAIL** Default option code cannot be "
2420 "interpreted: %s\n"), cupsRasterErrorString());
2421
2422 return (0);
2423 }
2424
2425 return (1);
2426 }
2427
2428
2429 /*
2430 * 'usage()' - Show program usage...
2431 */
2432
2433 static void
2434 usage(void)
2435 {
2436 _cupsLangPuts(stdout,
2437 _("Usage: cupstestppd [options] filename1.ppd[.gz] "
2438 "[... filenameN.ppd[.gz]]\n"
2439 " program | cupstestppd [options] -\n"
2440 "\n"
2441 "Options:\n"
2442 "\n"
2443 " -R root-directory Set alternate root\n"
2444 " -W {all,none,constraints,defaults,filters,translations}\n"
2445 " Issue warnings instead of errors\n"
2446 " -q Run silently\n"
2447 " -r Use 'relaxed' open mode\n"
2448 " -v Be slightly verbose\n"
2449 " -vv Be very verbose\n"));
2450
2451 exit(ERROR_USAGE);
2452 }
2453
2454
2455 /*
2456 * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
2457 */
2458
2459 static int /* O - 1 if valid, 0 if not */
2460 valid_utf8(const char *s) /* I - String to check */
2461 {
2462 while (*s)
2463 {
2464 if (*s & 0x80)
2465 {
2466 /*
2467 * Check for valid UTF-8 sequence...
2468 */
2469
2470 if ((*s & 0xc0) == 0x80)
2471 return (0); /* Illegal suffix byte */
2472 else if ((*s & 0xe0) == 0xc0)
2473 {
2474 /*
2475 * 2-byte sequence...
2476 */
2477
2478 s ++;
2479
2480 if ((*s & 0xc0) != 0x80)
2481 return (0); /* Missing suffix byte */
2482 }
2483 else if ((*s & 0xf0) == 0xe0)
2484 {
2485 /*
2486 * 3-byte sequence...
2487 */
2488
2489 s ++;
2490
2491 if ((*s & 0xc0) != 0x80)
2492 return (0); /* Missing suffix byte */
2493
2494 s ++;
2495
2496 if ((*s & 0xc0) != 0x80)
2497 return (0); /* Missing suffix byte */
2498 }
2499 else if ((*s & 0xf8) == 0xf0)
2500 {
2501 /*
2502 * 4-byte sequence...
2503 */
2504
2505 s ++;
2506
2507 if ((*s & 0xc0) != 0x80)
2508 return (0); /* Missing suffix byte */
2509
2510 s ++;
2511
2512 if ((*s & 0xc0) != 0x80)
2513 return (0); /* Missing suffix byte */
2514
2515 s ++;
2516
2517 if ((*s & 0xc0) != 0x80)
2518 return (0); /* Missing suffix byte */
2519 }
2520 else
2521 return (0); /* Bad sequence */
2522 }
2523
2524 s ++;
2525 }
2526
2527 return (1);
2528 }
2529
2530
2531 /*
2532 * End of "$Id: cupstestppd.c 6927 2007-09-07 16:51:00Z mike $".
2533 */