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