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