]> 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 4990 2006-01-26 02:21:45Z mike $"
3 *
4 * PPD test program for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 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 */
34
35 /*
36 * Include necessary headers...
37 */
38
39 #include <cups/string.h>
40 #include <cups/cups.h>
41 #include <cups/i18n.h>
42 #include <errno.h>
43 #include <stdlib.h>
44
45
46 /*
47 * Error codes...
48 */
49
50 enum
51 {
52 ERROR_NONE = 0,
53 ERROR_USAGE,
54 ERROR_FILE_OPEN,
55 ERROR_PPD_FORMAT,
56 ERROR_CONFORMANCE
57 };
58
59
60 /*
61 * Line endings...
62 */
63
64 enum
65 {
66 EOL_NONE = 0,
67 EOL_CR,
68 EOL_LF,
69 EOL_CRLF
70 };
71
72
73 /*
74 * Local functions...
75 */
76
77 void check_basics(const char *filename);
78 void show_conflicts(ppd_file_t *ppd);
79 void usage(void);
80
81
82 /*
83 * 'main()' - Main entry for test program.
84 */
85
86 int /* O - Exit status */
87 main(int argc, /* I - Number of command-line arguments */
88 char *argv[]) /* I - Command-line arguments */
89 {
90 int i, j, k, m, n; /* Looping vars */
91 int len; /* Length of option name */
92 char *opt; /* Option character */
93 const char *ptr; /* Pointer into string */
94 int files; /* Number of files */
95 int verbose; /* Want verbose output? */
96 int status; /* Exit status */
97 int errors; /* Number of conformance errors */
98 int ppdversion; /* PPD spec version in PPD file */
99 ppd_status_t error; /* Status of ppdOpen*() */
100 int line; /* Line number for error */
101 int xdpi, /* X resolution */
102 ydpi; /* Y resolution */
103 ppd_file_t *ppd; /* PPD file record */
104 ppd_attr_t *attr; /* PPD attribute */
105 ppd_size_t *size; /* Size record */
106 ppd_group_t *group; /* UI group */
107 ppd_option_t *option; /* Standard UI option */
108 ppd_group_t *group2; /* UI group */
109 ppd_option_t *option2; /* Standard UI option */
110 ppd_choice_t *choice; /* Standard UI option choice */
111 static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
112 static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
113 "JCL", "PAGE", "PROLOG" };
114
115
116 /*
117 * Display PPD files for each file listed on the command-line...
118 */
119
120 ppdSetConformance(PPD_CONFORM_STRICT);
121
122 verbose = 0;
123 ppd = NULL;
124 files = 0;
125 status = ERROR_NONE;
126
127 for (i = 1; i < argc; i ++)
128 if (argv[i][0] == '-' && argv[i][1])
129 {
130 for (opt = argv[i] + 1; *opt; opt ++)
131 switch (*opt)
132 {
133 case 'q' : /* Quiet mode */
134 if (verbose > 0)
135 {
136 _cupsLangPuts(stderr,
137 _("cupstestppd: The -q option is incompatible "
138 "with the -v option.\n"));
139 return (1);
140 }
141
142 verbose --;
143 break;
144
145 case 'r' : /* Relaxed mode */
146 ppdSetConformance(PPD_CONFORM_RELAXED);
147 break;
148
149 case 'v' : /* Verbose mode */
150 if (verbose < 0)
151 {
152 _cupsLangPuts(stderr,
153 _("cupstestppd: The -v option is incompatible "
154 "with the -q option.\n"));
155 return (1);
156 }
157
158 verbose ++;
159 break;
160
161 default :
162 usage();
163 break;
164 }
165 }
166 else
167 {
168 /*
169 * Open the PPD file...
170 */
171
172 if (files && verbose >= 0)
173 _cupsLangPuts(stdout, "\n");
174
175 files ++;
176
177 if (argv[i][0] == '-')
178 {
179 /*
180 * Read from stdin...
181 */
182
183 if (verbose >= 0)
184 printf("(stdin):");
185
186 ppd = ppdOpen(stdin);
187 }
188 else
189 {
190 /*
191 * Read from a file...
192 */
193
194 if (verbose >= 0)
195 printf("%s:", argv[i]);
196
197 ppd = ppdOpenFile(argv[i]);
198 }
199
200 if (ppd == NULL)
201 {
202 error = ppdLastError(&line);
203
204 if (error <= PPD_ALLOC_ERROR)
205 {
206 status = ERROR_FILE_OPEN;
207
208 if (verbose >= 0)
209 _cupsLangPrintf(stdout,
210 _(" FAIL\n"
211 " **FAIL** Unable to open PPD file - %s\n"),
212 strerror(errno));
213 }
214 else
215 {
216 status = ERROR_PPD_FORMAT;
217
218 if (verbose >= 0)
219 {
220 _cupsLangPrintf(stdout,
221 _(" FAIL\n"
222 " **FAIL** Unable to open PPD file - "
223 "%s on line %d.\n"),
224 ppdErrorString(error), line);
225
226 switch (error)
227 {
228 case PPD_MISSING_PPDADOBE4 :
229 _cupsLangPuts(stdout,
230 _(" REF: Page 42, section 5.2.\n"));
231 break;
232 case PPD_MISSING_VALUE :
233 _cupsLangPuts(stdout,
234 _(" REF: Page 20, section 3.4.\n"));
235 break;
236 case PPD_BAD_OPEN_GROUP :
237 case PPD_NESTED_OPEN_GROUP :
238 _cupsLangPuts(stdout,
239 _(" REF: Pages 45-46, section 5.2.\n"));
240 break;
241 case PPD_BAD_OPEN_UI :
242 case PPD_NESTED_OPEN_UI :
243 _cupsLangPuts(stdout,
244 _(" REF: Pages 42-45, section 5.2.\n"));
245 break;
246 case PPD_BAD_ORDER_DEPENDENCY :
247 _cupsLangPuts(stdout,
248 _(" REF: Pages 48-49, section 5.2.\n"));
249 break;
250 case PPD_BAD_UI_CONSTRAINTS :
251 _cupsLangPuts(stdout,
252 _(" REF: Pages 52-54, section 5.2.\n"));
253 break;
254 case PPD_MISSING_ASTERISK :
255 _cupsLangPuts(stdout,
256 _(" REF: Page 15, section 3.2.\n"));
257 break;
258 case PPD_LINE_TOO_LONG :
259 _cupsLangPuts(stdout,
260 _(" REF: Page 15, section 3.1.\n"));
261 break;
262 case PPD_ILLEGAL_CHARACTER :
263 _cupsLangPuts(stdout,
264 _(" REF: Page 15, section 3.1.\n"));
265 break;
266 case PPD_ILLEGAL_MAIN_KEYWORD :
267 _cupsLangPuts(stdout,
268 _(" REF: Pages 16-17, section 3.2.\n"));
269 break;
270 case PPD_ILLEGAL_OPTION_KEYWORD :
271 _cupsLangPuts(stdout,
272 _(" REF: Page 19, section 3.3.\n"));
273 break;
274 case PPD_ILLEGAL_TRANSLATION :
275 _cupsLangPuts(stdout,
276 _(" REF: Page 27, section 3.5.\n"));
277 break;
278 default :
279 break;
280 }
281
282 check_basics(argv[i]);
283 }
284 }
285
286 continue;
287 }
288
289 /*
290 * Show the header and then perform basic conformance tests (limited
291 * only by what the CUPS PPD functions actually load...)
292 */
293
294 errors = 0;
295 ppdversion = 43;
296
297 if (verbose > 0)
298 _cupsLangPuts(stdout,
299 _("\n DETAILED CONFORMANCE TEST RESULTS\n"));
300
301 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
302 attr->value)
303 ppdversion = (int)(10 * atof(attr->value) + 0.5);
304
305 if (verbose > 0)
306 {
307 /*
308 * Look for default keywords with no matching option...
309 */
310
311 for (i = 0; i < ppd->num_attrs; i ++)
312 {
313 attr = ppd->attrs[i];
314
315 if (!strcmp(attr->name, "DefaultColorSpace") ||
316 !strcmp(attr->name, "DefaultFont") ||
317 !strcmp(attr->name, "DefaultImageableArea") ||
318 !strcmp(attr->name, "DefaultOutputOrder") ||
319 !strcmp(attr->name, "DefaultPaperDimension") ||
320 !strcmp(attr->name, "DefaultTransfer"))
321 continue;
322
323 if (!strncmp(attr->name, "Default", 7) &&
324 !ppdFindOption(ppd, attr->name + 7))
325 _cupsLangPrintf(stdout,
326 _(" WARN %s has no corresponding "
327 "options!\n"),
328 attr->name);
329 }
330 }
331
332 if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
333 {
334 if (verbose >= 0)
335 {
336 if (!errors && !verbose)
337 _cupsLangPuts(stdout, _(" FAIL\n"));
338
339 _cupsLangPuts(stdout,
340 _(" **FAIL** REQUIRED DefaultImageableArea\n"
341 " REF: Page 102, section 5.15.\n"));
342 }
343
344 errors ++;
345 }
346 else if (ppdPageSize(ppd, attr->value) == NULL &&
347 strcmp(attr->value, "Unknown"))
348 {
349 if (verbose >= 0)
350 {
351 if (!errors && !verbose)
352 _cupsLangPuts(stdout, _(" FAIL\n"));
353
354 _cupsLangPrintf(stdout,
355 _(" **FAIL** BAD DefaultImageableArea %s!\n"
356 " REF: Page 102, section 5.15.\n"),
357 attr->value);
358 }
359
360 errors ++;
361 }
362 else
363 {
364 if (verbose > 0)
365 _cupsLangPuts(stdout, _(" PASS DefaultImageableArea\n"));
366 }
367
368 if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
369 {
370 if (verbose >= 0)
371 {
372 if (!errors && !verbose)
373 _cupsLangPuts(stdout, _(" FAIL\n"));
374
375 _cupsLangPuts(stdout,
376 _(" **FAIL** REQUIRED DefaultPaperDimension\n"
377 " REF: Page 103, section 5.15.\n"));
378 }
379
380 errors ++;
381 }
382 else if (ppdPageSize(ppd, attr->value) == NULL &&
383 strcmp(attr->value, "Unknown"))
384 {
385 if (verbose >= 0)
386 {
387 if (!errors && !verbose)
388 _cupsLangPuts(stdout, _(" FAIL\n"));
389
390 _cupsLangPrintf(stdout,
391 _(" **FAIL** BAD DefaultPaperDimension %s!\n"
392 " REF: Page 103, section 5.15.\n"),
393 attr->value);
394 }
395
396 errors ++;
397 }
398 else if (verbose > 0)
399 _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension\n"));
400
401 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
402 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
403 {
404 /*
405 * Verify that we have a default choice...
406 */
407
408 if (option->defchoice[0])
409 {
410 if (ppdFindChoice(option, option->defchoice) == NULL &&
411 strcmp(option->defchoice, "Unknown"))
412 {
413 if (verbose >= 0)
414 {
415 if (!errors && !verbose)
416 _cupsLangPuts(stdout, _(" FAIL\n"));
417
418 _cupsLangPrintf(stdout,
419 _(" **FAIL** BAD Default%s %s\n"
420 " REF: Page 40, section 4.5.\n"),
421 option->keyword, option->defchoice);
422 }
423
424 errors ++;
425 }
426 else if (verbose > 0)
427 _cupsLangPrintf(stdout,
428 _(" PASS Default%s\n"),
429 option->keyword);
430 }
431 else
432 {
433 if (verbose >= 0)
434 {
435 if (!errors && !verbose)
436 _cupsLangPuts(stdout, _(" FAIL\n"));
437
438 _cupsLangPrintf(stdout,
439 _(" **FAIL** REQUIRED Default%s\n"
440 " REF: Page 40, section 4.5.\n"),
441 option->keyword);
442 }
443
444 errors ++;
445 }
446 }
447
448 if (ppdFindAttr(ppd, "FileVersion", NULL) != NULL)
449 {
450 if (verbose > 0)
451 _cupsLangPuts(stdout, _(" PASS FileVersion\n"));
452 }
453 else
454 {
455 if (verbose >= 0)
456 {
457 if (!errors && !verbose)
458 _cupsLangPuts(stdout, _(" FAIL\n"));
459
460 _cupsLangPuts(stdout,
461 _(" **FAIL** REQUIRED FileVersion\n"
462 " REF: Page 56, section 5.3.\n"));
463 }
464
465 errors ++;
466 }
467
468 if (ppdFindAttr(ppd, "FormatVersion", NULL) != NULL)
469 {
470 if (verbose > 0)
471 _cupsLangPuts(stdout, _(" PASS FormatVersion\n"));
472 }
473 else
474 {
475 if (verbose >= 0)
476 {
477 if (!errors && !verbose)
478 _cupsLangPuts(stdout, _(" FAIL\n"));
479
480 _cupsLangPuts(stdout,
481 _(" **FAIL** REQUIRED FormatVersion\n"
482 " REF: Page 56, section 5.3.\n"));
483 }
484
485 errors ++;
486 }
487
488 if (ppd->lang_encoding != NULL)
489 {
490 if (verbose > 0)
491 _cupsLangPuts(stdout, _(" PASS LanguageEncoding\n"));
492 }
493 else if (ppdversion > 40)
494 {
495 if (verbose >= 0)
496 {
497 if (!errors && !verbose)
498 _cupsLangPuts(stdout, _(" FAIL\n"));
499
500 _cupsLangPuts(stdout,
501 _(" **FAIL** REQUIRED LanguageEncoding\n"
502 " REF: Pages 56-57, section 5.3.\n"));
503 }
504
505 errors ++;
506 }
507
508 if (ppd->lang_version != NULL)
509 {
510 if (verbose > 0)
511 _cupsLangPuts(stdout, _(" PASS LanguageVersion\n"));
512 }
513 else
514 {
515 if (verbose >= 0)
516 {
517 if (!errors && !verbose)
518 _cupsLangPuts(stdout, _(" FAIL\n"));
519
520 _cupsLangPuts(stdout,
521 _(" **FAIL** REQUIRED LanguageVersion\n"
522 " REF: Pages 57-58, section 5.3.\n"));
523 }
524
525 errors ++;
526 }
527
528 if (ppd->manufacturer != NULL)
529 {
530 if (!strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) ||
531 !strncasecmp(ppd->manufacturer, "Hewlett Packard", 15))
532 {
533 if (verbose >= 0)
534 {
535 if (!errors && !verbose)
536 _cupsLangPuts(stdout, _(" FAIL\n"));
537
538 _cupsLangPuts(stdout,
539 _(" **FAIL** BAD Manufacturer (should be "
540 "\"HP\")\n"
541 " REF: Page 211, table D.1.\n"));
542 }
543
544 errors ++;
545 }
546 else if (verbose > 0)
547 _cupsLangPuts(stdout, _(" PASS Manufacturer\n"));
548 }
549 else if (ppdversion >= 43)
550 {
551 if (verbose >= 0)
552 {
553 if (!errors && !verbose)
554 _cupsLangPuts(stdout, _(" FAIL\n"));
555
556 _cupsLangPuts(stdout,
557 _(" **FAIL** REQUIRED Manufacturer\n"
558 " REF: Pages 58-59, section 5.3.\n"));
559 }
560
561 errors ++;
562 }
563
564 if (ppd->modelname != NULL)
565 {
566 for (ptr = ppd->modelname; *ptr; ptr ++)
567 if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr))
568 break;
569
570 if (*ptr)
571 {
572 if (verbose >= 0)
573 {
574 if (!errors && !verbose)
575 _cupsLangPuts(stdout, _(" FAIL\n"));
576
577 _cupsLangPrintf(stdout,
578 _(" **FAIL** BAD ModelName - \"%c\" not "
579 "allowed in string.\n"
580 " REF: Pages 59-60, section 5.3.\n"),
581 *ptr);
582 }
583
584 errors ++;
585 }
586 else if (verbose > 0)
587 _cupsLangPuts(stdout, _(" PASS ModelName\n"));
588 }
589 else
590 {
591 if (verbose >= 0)
592 {
593 if (!errors && !verbose)
594 _cupsLangPuts(stdout, _(" FAIL\n"));
595
596 _cupsLangPuts(stdout,
597 _(" **FAIL** REQUIRED ModelName\n"
598 " REF: Pages 59-60, section 5.3.\n"));
599 }
600
601 errors ++;
602 }
603
604 if (ppd->nickname != NULL)
605 {
606 if (verbose > 0)
607 _cupsLangPuts(stdout, _(" PASS NickName\n"));
608 }
609 else
610 {
611 if (verbose >= 0)
612 {
613 if (!errors && !verbose)
614 _cupsLangPuts(stdout, _(" FAIL\n"));
615
616 _cupsLangPuts(stdout,
617 _(" **FAIL** REQUIRED NickName\n"
618 " REF: Page 60, section 5.3.\n"));
619 }
620
621 errors ++;
622 }
623
624 if (ppdFindOption(ppd, "PageSize") != NULL)
625 {
626 if (verbose > 0)
627 _cupsLangPuts(stdout, _(" PASS PageSize\n"));
628 }
629 else
630 {
631 if (verbose >= 0)
632 {
633 if (!errors && !verbose)
634 _cupsLangPuts(stdout, _(" FAIL\n"));
635
636 _cupsLangPuts(stdout,
637 _(" **FAIL** REQUIRED PageSize\n"
638 " REF: Pages 99-100, section 5.14.\n"));
639 }
640
641 errors ++;
642 }
643
644 if (ppdFindOption(ppd, "PageRegion") != NULL)
645 {
646 if (verbose > 0)
647 _cupsLangPuts(stdout, _(" PASS PageRegion\n"));
648 }
649 else
650 {
651 if (verbose >= 0)
652 {
653 if (!errors && !verbose)
654 _cupsLangPuts(stdout, _(" FAIL\n"));
655
656 _cupsLangPuts(stdout,
657 _(" **FAIL** REQUIRED PageRegion\n"
658 " REF: Page 100, section 5.14.\n"));
659 }
660
661 errors ++;
662 }
663
664 if (ppd->pcfilename != NULL)
665 {
666 if (verbose > 0)
667 _cupsLangPuts(stdout, _(" PASS PCFileName\n"));
668 }
669 else
670 {
671 if (verbose >= 0)
672 {
673 if (!errors && !verbose)
674 _cupsLangPuts(stdout, _(" FAIL\n"));
675
676 _cupsLangPuts(stdout,
677 _(" **FAIL** REQUIRED PCFileName\n"
678 " REF: Pages 61-62, section 5.3.\n"));
679 }
680
681 errors ++;
682 }
683
684 if (ppd->product != NULL)
685 {
686 if (ppd->product[0] != '(' ||
687 ppd->product[strlen(ppd->product) - 1] != ')')
688 {
689 if (verbose >= 0)
690 {
691 if (!errors && !verbose)
692 _cupsLangPuts(stdout, _(" FAIL\n"));
693
694 _cupsLangPuts(stdout,
695 _(" **FAIL** BAD Product - not \"(string)\".\n"
696 " REF: Page 62, section 5.3.\n"));
697 }
698
699 errors ++;
700 }
701 else if (verbose > 0)
702 _cupsLangPuts(stdout, _(" PASS Product\n"));
703 }
704 else
705 {
706 if (verbose >= 0)
707 {
708 if (!errors && !verbose)
709 _cupsLangPuts(stdout, _(" FAIL\n"));
710
711 _cupsLangPuts(stdout,
712 _(" **FAIL** REQUIRED Product\n"
713 " REF: Page 62, section 5.3.\n"));
714 }
715
716 errors ++;
717 }
718
719 if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
720 attr->value != NULL)
721 {
722 char junkstr[255]; /* Temp string */
723 int junkint; /* Temp integer */
724
725
726 if (sscanf(attr->value, "(%[^)])%d", junkstr, &junkint) != 2)
727 {
728 if (verbose >= 0)
729 {
730 if (!errors && !verbose)
731 _cupsLangPuts(stdout, _(" FAIL\n"));
732
733 _cupsLangPuts(stdout,
734 _(" **FAIL** BAD PSVersion - not \"(string) "
735 "int\".\n"
736 " REF: Pages 62-64, section 5.3.\n"));
737 }
738
739 errors ++;
740 }
741 else if (verbose > 0)
742 _cupsLangPuts(stdout, _(" PASS PSVersion\n"));
743 }
744 else
745 {
746 if (verbose >= 0)
747 {
748 if (!errors && !verbose)
749 _cupsLangPuts(stdout, _(" FAIL\n"));
750
751 _cupsLangPuts(stdout,
752 _(" **FAIL** REQUIRED PSVersion\n"
753 " REF: Pages 62-64, section 5.3.\n"));
754 }
755
756 errors ++;
757 }
758
759 if (ppd->shortnickname != NULL)
760 {
761 if (strlen(ppd->shortnickname) > 31)
762 {
763 if (verbose >= 0)
764 {
765 if (!errors && !verbose)
766 _cupsLangPuts(stdout, _(" FAIL\n"));
767
768 _cupsLangPuts(stdout,
769 _(" **FAIL** BAD ShortNickName - longer "
770 "than 31 chars.\n"
771 " REF: Pages 64-65, section 5.3.\n"));
772 }
773
774 errors ++;
775 }
776 else if (verbose > 0)
777 _cupsLangPuts(stdout, _(" PASS ShortNickName\n"));
778 }
779 else if (ppdversion >= 43)
780 {
781 if (verbose >= 0)
782 {
783 if (!errors && !verbose)
784 _cupsLangPuts(stdout, _(" FAIL\n"));
785
786 _cupsLangPuts(stdout,
787 _(" **FAIL** REQUIRED ShortNickName\n"
788 " REF: Page 64-65, section 5.3.\n"));
789 }
790
791 errors ++;
792 }
793
794 if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
795 strstr(ppd->patches, "*End"))
796 {
797 if (verbose >= 0)
798 {
799 if (!errors && !verbose)
800 _cupsLangPuts(stdout, _(" FAIL\n"));
801
802 _cupsLangPuts(stdout,
803 _(" **FAIL** BAD JobPatchFile attribute in file\n"
804 " REF: Page 24, section 3.4.\n"));
805 }
806
807 errors ++;
808 }
809
810 /*
811 * Check for page sizes without the corresponding ImageableArea or
812 * PaperDimension values...
813 */
814
815 if (ppd->num_sizes == 0)
816 {
817 if (verbose >= 0)
818 {
819 if (!errors && !verbose)
820 _cupsLangPuts(stdout, _(" FAIL\n"));
821
822 _cupsLangPuts(stdout,
823 _(" **FAIL** REQUIRED PageSize\n"
824 " REF: Page 41, section 5.\n"
825 " REF: Page 99, section 5.14.\n"));
826 }
827
828 errors ++;
829 }
830 else
831 {
832 for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++)
833 {
834 /*
835 * Don't check custom size...
836 */
837
838 if (!strcmp(size->name, "Custom"))
839 continue;
840
841 /*
842 * Check for ImageableArea...
843 */
844
845 if (size->left == 0.0 && size->bottom == 0.0 &&
846 size->right == 0.0 && size->top == 0.0)
847 {
848 if (verbose >= 0)
849 {
850 if (!errors && !verbose)
851 _cupsLangPuts(stdout, _(" FAIL\n"));
852
853 _cupsLangPrintf(stdout,
854 _(" **FAIL** REQUIRED ImageableArea for "
855 "PageSize %s\n"
856 " REF: Page 41, section 5.\n"
857 " REF: Page 102, section 5.15.\n"),
858 size->name);
859 }
860
861 errors ++;
862 }
863
864 /*
865 * Check for PaperDimension...
866 */
867
868 if (size->width == 0.0 && size->length == 0.0)
869 {
870 if (verbose >= 0)
871 {
872 if (!errors && !verbose)
873 _cupsLangPuts(stdout, _(" FAIL\n"));
874
875 _cupsLangPrintf(stdout,
876 _(" **FAIL** REQUIRED PaperDimension "
877 "for PageSize %s\n"
878 " REF: Page 41, section 5.\n"
879 " REF: Page 103, section 5.15.\n"),
880 size->name);
881 }
882
883 errors ++;
884 }
885 }
886 }
887
888 /*
889 * Check for valid Resolution, JCLResolution, or SetResolution values...
890 */
891
892 if ((option = ppdFindOption(ppd, "Resolution")) == NULL)
893 if ((option = ppdFindOption(ppd, "JCLResolution")) == NULL)
894 option = ppdFindOption(ppd, "SetResolution");
895
896 if (option != NULL)
897 {
898 for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
899 {
900 /*
901 * Verify that all resolution options are of the form NNNdpi
902 * or NNNxNNNdpi...
903 */
904
905 xdpi = strtol(choice->choice, (char **)&ptr, 10);
906 if (ptr > choice->choice && xdpi > 0)
907 {
908 if (*ptr == 'x')
909 ydpi = strtol(ptr + 1, (char **)&ptr, 10);
910 else
911 ydpi = xdpi;
912 }
913 else
914 ydpi = xdpi;
915
916 if (xdpi <= 0 || ydpi <= 0 || strcmp(ptr, "dpi"))
917 {
918 if (verbose >= 0)
919 {
920 if (!errors && !verbose)
921 _cupsLangPuts(stdout, _(" FAIL\n"));
922
923 _cupsLangPrintf(stdout,
924 _(" **FAIL** Bad %s choice %s!\n"
925 " REF: Page 84, section 5.9\n"),
926 option->keyword, choice->choice);
927 }
928
929 errors ++;
930 }
931 }
932 }
933
934 /*
935 * Check for a duplex option, and for standard values...
936 */
937
938 if ((option = ppdFindOption(ppd, "Duplex")) == NULL)
939 if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
940 if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
941 option = ppdFindOption(ppd, "KD03Duplex");
942
943 if (option != NULL)
944 {
945 if (ppdFindChoice(option, "None") == NULL)
946 {
947 if (verbose >= 0)
948 {
949 if (!errors && !verbose)
950 _cupsLangPuts(stdout, _(" FAIL\n"));
951
952 _cupsLangPrintf(stdout,
953 _(" **FAIL** REQUIRED %s does not define "
954 "choice None!\n"
955 " REF: Page 122, section 5.17\n"),
956 option->keyword);
957 }
958
959 errors ++;
960 }
961
962 for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
963 if (strcmp(choice->choice, "None") &&
964 strcmp(choice->choice, "DuplexNoTumble") &&
965 strcmp(choice->choice, "DuplexTumble") &&
966 strcmp(choice->choice, "SimplexTumble"))
967 {
968 if (verbose >= 0)
969 {
970 if (!errors && !verbose)
971 _cupsLangPuts(stdout, _(" FAIL\n"));
972
973 _cupsLangPrintf(stdout,
974 _(" **FAIL** Bad %s choice %s!\n"
975 " REF: Page 122, section 5.17\n"),
976 option->keyword, choice->choice);
977 }
978
979 errors ++;
980 }
981 }
982
983 if (errors)
984 status = ERROR_CONFORMANCE;
985 else if (!verbose)
986 _cupsLangPuts(stdout, _(" PASS\n"));
987
988 if (verbose >= 0)
989 {
990 check_basics(argv[i]);
991
992 if (option &&
993 strcmp(option->keyword, "Duplex") &&
994 strcmp(option->keyword, "JCLDuplex"))
995 {
996 _cupsLangPrintf(stdout,
997 _(" WARN Duplex option keyword %s "
998 "should be named Duplex or JCLDuplex!\n"
999 " REF: Page 122, section 5.17\n"),
1000 option->keyword);
1001 }
1002
1003 ppdMarkDefaults(ppd);
1004 if (ppdConflicts(ppd))
1005 {
1006 _cupsLangPuts(stdout,
1007 _(" WARN Default choices conflicting!\n"));
1008
1009 show_conflicts(ppd);
1010 }
1011
1012 if (ppdversion < 43)
1013 {
1014 _cupsLangPrintf(stdout,
1015 _(" WARN Obsolete PPD version %.1f!\n"
1016 " REF: Page 42, section 5.2.\n"),
1017 0.1f * ppdversion);
1018 }
1019
1020 if (!ppd->lang_encoding && ppdversion < 41)
1021 {
1022 _cupsLangPuts(stdout,
1023 _(" WARN LanguageEncoding required by PPD "
1024 "4.3 spec.\n"
1025 " REF: Pages 56-57, section 5.3.\n"));
1026 }
1027
1028 if (!ppd->manufacturer && ppdversion < 43)
1029 {
1030 _cupsLangPuts(stdout,
1031 _(" WARN Manufacturer required by PPD "
1032 "4.3 spec.\n"
1033 " REF: Pages 58-59, section 5.3.\n"));
1034 }
1035
1036 /*
1037 * Treat a PCFileName attribute longer than 12 characters as
1038 * a warning and not a hard error...
1039 */
1040
1041 if (ppd->pcfilename && strlen(ppd->pcfilename) > 12)
1042 {
1043 _cupsLangPuts(stdout,
1044 _(" WARN PCFileName longer than 8.3 in "
1045 "violation of PPD spec.\n"
1046 " REF: Pages 61-62, section 5.3.\n"));
1047 }
1048
1049 if (!ppd->shortnickname && ppdversion < 43)
1050 {
1051 _cupsLangPuts(stdout,
1052 _(" WARN ShortNickName required by PPD "
1053 "4.3 spec.\n"
1054 " REF: Pages 64-65, section 5.3.\n"));
1055 }
1056
1057 /*
1058 * Check the Protocols line and flag PJL + BCP since TBCP is
1059 * usually used with PJL...
1060 */
1061
1062 if (ppd->protocols)
1063 {
1064 if (strstr(ppd->protocols, "PJL") &&
1065 strstr(ppd->protocols, "BCP") &&
1066 !strstr(ppd->protocols, "TBCP"))
1067 {
1068 _cupsLangPuts(stdout,
1069 _(" WARN Protocols contains both PJL "
1070 "and BCP; expected TBCP.\n"
1071 " REF: Pages 78-79, section 5.7.\n"));
1072 }
1073
1074 if (strstr(ppd->protocols, "PJL") &&
1075 (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
1076 {
1077 _cupsLangPuts(stdout,
1078 _(" WARN Protocols contains PJL but JCL "
1079 "attributes are not set.\n"
1080 " REF: Pages 78-79, section 5.7.\n"));
1081 }
1082 }
1083
1084 /*
1085 * Check for options with a common prefix, e.g. Duplex and Duplexer,
1086 * which are errors according to the spec but won't cause problems
1087 * with CUPS specifically...
1088 */
1089
1090 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1091 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1092 {
1093 len = strlen(option->keyword);
1094
1095 for (m = 0, group2 = ppd->groups;
1096 m < ppd->num_groups;
1097 m ++, group2 ++)
1098 for (n = 0, option2 = group2->options;
1099 n < group2->num_options;
1100 n ++, option2 ++)
1101 if (option != option2 &&
1102 len < strlen(option2->keyword) &&
1103 !strncmp(option->keyword, option2->keyword, len))
1104 {
1105 _cupsLangPrintf(stdout,
1106 _(" WARN %s shares a common "
1107 "prefix with %s\n"
1108 " REF: Page 15, section "
1109 "3.2.\n"),
1110 option->keyword, option2->keyword);
1111 }
1112 }
1113 }
1114
1115 if (verbose > 0)
1116 {
1117 if (errors)
1118 _cupsLangPrintf(stdout, _(" %d ERROR%s FOUND\n"),
1119 errors, errors == 1 ? "" : "S");
1120 else
1121 _cupsLangPuts(stdout, _(" NO ERRORS FOUND\n"));
1122 }
1123
1124 /*
1125 * Then list the options, if "-v" was provided...
1126 */
1127
1128 if (verbose > 1)
1129 {
1130 _cupsLangPrintf(stdout,
1131 "\n"
1132 " language_level = %d\n"
1133 " color_device = %s\n"
1134 " variable_sizes = %s\n"
1135 " landscape = %d\n",
1136 ppd->language_level,
1137 ppd->color_device ? "TRUE" : "FALSE",
1138 ppd->variable_sizes ? "TRUE" : "FALSE",
1139 ppd->landscape);
1140
1141 switch (ppd->colorspace)
1142 {
1143 case PPD_CS_CMYK :
1144 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK\n");
1145 break;
1146 case PPD_CS_CMY :
1147 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY\n");
1148 break;
1149 case PPD_CS_GRAY :
1150 _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY\n");
1151 break;
1152 case PPD_CS_RGB :
1153 _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB\n");
1154 break;
1155 default :
1156 _cupsLangPuts(stdout, " colorspace = <unknown>\n");
1157 break;
1158 }
1159
1160 _cupsLangPrintf(stdout, " num_emulations = %d\n",
1161 ppd->num_emulations);
1162 for (j = 0; j < ppd->num_emulations; j ++)
1163 _cupsLangPrintf(stdout, " emulations[%d] = %s\n",
1164 j, ppd->emulations[j].name);
1165
1166 _cupsLangPrintf(stdout, " lang_encoding = %s\n",
1167 ppd->lang_encoding);
1168 _cupsLangPrintf(stdout, " lang_version = %s\n",
1169 ppd->lang_version);
1170 _cupsLangPrintf(stdout, " modelname = %s\n", ppd->modelname);
1171 _cupsLangPrintf(stdout, " ttrasterizer = %s\n",
1172 ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
1173 _cupsLangPrintf(stdout, " manufacturer = %s\n",
1174 ppd->manufacturer);
1175 _cupsLangPrintf(stdout, " product = %s\n", ppd->product);
1176 _cupsLangPrintf(stdout, " nickname = %s\n", ppd->nickname);
1177 _cupsLangPrintf(stdout, " shortnickname = %s\n",
1178 ppd->shortnickname);
1179 _cupsLangPrintf(stdout, " patches = %d bytes\n",
1180 ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
1181
1182 _cupsLangPrintf(stdout, " num_groups = %d\n", ppd->num_groups);
1183 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1184 {
1185 _cupsLangPrintf(stdout, " group[%d] = %s\n",
1186 j, group->text);
1187
1188 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1189 {
1190 _cupsLangPrintf(stdout,
1191 " options[%d] = %s (%s) %s %s %.0f "
1192 "(%d choices)\n",
1193 k, option->keyword, option->text, uis[option->ui],
1194 sections[option->section], option->order,
1195 option->num_choices);
1196
1197 if (!strcmp(option->keyword, "PageSize") ||
1198 !strcmp(option->keyword, "PageRegion"))
1199 {
1200 for (m = option->num_choices, choice = option->choices;
1201 m > 0;
1202 m --, choice ++)
1203 {
1204 size = ppdPageSize(ppd, choice->choice);
1205
1206 if (size == NULL)
1207 _cupsLangPrintf(stdout,
1208 " %s (%s) = ERROR",
1209 choice->choice, choice->text);
1210 else
1211 _cupsLangPrintf(stdout,
1212 " %s (%s) = %.2fx%.2fin "
1213 "(%.1f,%.1f,%.1f,%.1f)",
1214 choice->choice, choice->text,
1215 size->width / 72.0, size->length / 72.0,
1216 size->left / 72.0, size->bottom / 72.0,
1217 size->right / 72.0, size->top / 72.0);
1218
1219 if (!strcmp(option->defchoice, choice->choice))
1220 _cupsLangPuts(stdout, " *\n");
1221 else
1222 _cupsLangPuts(stdout, "\n");
1223 }
1224 }
1225 else
1226 {
1227 for (m = option->num_choices, choice = option->choices;
1228 m > 0;
1229 m --, choice ++)
1230 {
1231 _cupsLangPrintf(stdout, " %s (%s)",
1232 choice->choice, choice->text);
1233
1234 if (!strcmp(option->defchoice, choice->choice))
1235 _cupsLangPuts(stdout, " *\n");
1236 else
1237 _cupsLangPuts(stdout, "\n");
1238 }
1239 }
1240 }
1241 }
1242
1243 _cupsLangPrintf(stdout, " num_profiles = %d\n",
1244 ppd->num_profiles);
1245 for (j = 0; j < ppd->num_profiles; j ++)
1246 _cupsLangPrintf(stdout,
1247 " profiles[%d] = %s/%s %.3f %.3f "
1248 "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
1249 j, ppd->profiles[j].resolution,
1250 ppd->profiles[j].media_type,
1251 ppd->profiles[j].gamma, ppd->profiles[j].density,
1252 ppd->profiles[j].matrix[0][0],
1253 ppd->profiles[j].matrix[0][1],
1254 ppd->profiles[j].matrix[0][2],
1255 ppd->profiles[j].matrix[1][0],
1256 ppd->profiles[j].matrix[1][1],
1257 ppd->profiles[j].matrix[1][2],
1258 ppd->profiles[j].matrix[2][0],
1259 ppd->profiles[j].matrix[2][1],
1260 ppd->profiles[j].matrix[2][2]);
1261
1262 _cupsLangPrintf(stdout, " num_fonts = %d\n", ppd->num_fonts);
1263 for (j = 0; j < ppd->num_fonts; j ++)
1264 _cupsLangPrintf(stdout, " fonts[%d] = %s\n",
1265 j, ppd->fonts[j]);
1266
1267 _cupsLangPrintf(stdout, " num_attrs = %d\n", ppd->num_attrs);
1268 for (j = 0; j < ppd->num_attrs; j ++)
1269 _cupsLangPrintf(stdout,
1270 " attrs[%d] = %s %s%s%s: \"%s\"\n", j,
1271 ppd->attrs[j]->name, ppd->attrs[j]->spec,
1272 ppd->attrs[j]->text[0] ? "/" : "",
1273 ppd->attrs[j]->text,
1274 ppd->attrs[j]->value ?
1275 ppd->attrs[j]->value : "(null)");
1276 }
1277
1278 ppdClose(ppd);
1279 }
1280
1281 if (!files)
1282 usage();
1283
1284 return (status);
1285 }
1286
1287
1288 /*
1289 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1290 */
1291
1292 void
1293 check_basics(const char *filename) /* I - PPD file to check */
1294 {
1295 cups_file_t *fp; /* File pointer */
1296 int ch; /* Current character */
1297 int col, /* Current column */
1298 whitespace; /* Only seen whitespace? */
1299 int eol; /* Line endings */
1300 int linenum; /* Line number */
1301 int mixed; /* Mixed line endings? */
1302
1303
1304 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1305 return;
1306
1307 linenum = 1;
1308 col = 0;
1309 eol = EOL_NONE;
1310 mixed = 0;
1311 whitespace = 1;
1312
1313 while ((ch = cupsFileGetChar(fp)) != EOF)
1314 {
1315 if (ch == '\r' || ch == '\n')
1316 {
1317 if (ch == '\n')
1318 {
1319 if (eol == EOL_NONE)
1320 eol = EOL_LF;
1321 else if (eol != EOL_LF)
1322 mixed = 1;
1323 }
1324 else if (ch == '\r')
1325 {
1326 if (cupsFilePeekChar(fp) == '\n')
1327 {
1328 cupsFileGetChar(fp);
1329
1330 if (eol == EOL_NONE)
1331 eol = EOL_CRLF;
1332 else
1333 mixed = 1;
1334 }
1335 else if (eol == EOL_NONE)
1336 eol = EOL_CR;
1337 else
1338 mixed = 1;
1339 }
1340
1341 if (col > 0 && whitespace)
1342 _cupsLangPrintf(stdout,
1343 _(" WARN Line %d only contains whitespace!\n"),
1344 linenum);
1345
1346 linenum ++;
1347 col = 0;
1348 whitespace = 1;
1349 }
1350 else
1351 {
1352 if (ch != ' ' && ch != '\t')
1353 whitespace = 0;
1354
1355 col ++;
1356 }
1357 }
1358
1359 if (mixed)
1360 _cupsLangPuts(stdout,
1361 _(" WARN File contains a mix of CR, LF, and "
1362 "CR LF line endings!\n"));
1363
1364 if (eol == EOL_CRLF)
1365 _cupsLangPuts(stdout,
1366 _(" WARN Non-Windows PPD files should use lines "
1367 "ending with only LF, not CR LF!\n"));
1368
1369 cupsFileClose(fp);
1370 }
1371
1372
1373 /*
1374 * 'show_conflicts()' - Show option conflicts in a PPD file.
1375 */
1376
1377 void
1378 show_conflicts(ppd_file_t *ppd) /* I - PPD to check */
1379 {
1380 int i, j; /* Looping variables */
1381 ppd_const_t *c; /* Current constraint */
1382 ppd_option_t *o1, *o2; /* Options */
1383 ppd_choice_t *c1, *c2; /* Choices */
1384
1385
1386 /*
1387 * Loop through all of the UI constraints and report any options
1388 * that conflict...
1389 */
1390
1391 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
1392 {
1393 /*
1394 * Grab pointers to the first option...
1395 */
1396
1397 o1 = ppdFindOption(ppd, c->option1);
1398
1399 if (o1 == NULL)
1400 continue;
1401 else if (c->choice1[0] != '\0')
1402 {
1403 /*
1404 * This constraint maps to a specific choice.
1405 */
1406
1407 c1 = ppdFindChoice(o1, c->choice1);
1408 }
1409 else
1410 {
1411 /*
1412 * This constraint applies to any choice for this option.
1413 */
1414
1415 for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
1416 if (c1->marked)
1417 break;
1418
1419 if (j == 0 ||
1420 !strcasecmp(c1->choice, "None") ||
1421 !strcasecmp(c1->choice, "Off") ||
1422 !strcasecmp(c1->choice, "False"))
1423 c1 = NULL;
1424 }
1425
1426 /*
1427 * Grab pointers to the second option...
1428 */
1429
1430 o2 = ppdFindOption(ppd, c->option2);
1431
1432 if (o2 == NULL)
1433 continue;
1434 else if (c->choice2[0] != '\0')
1435 {
1436 /*
1437 * This constraint maps to a specific choice.
1438 */
1439
1440 c2 = ppdFindChoice(o2, c->choice2);
1441 }
1442 else
1443 {
1444 /*
1445 * This constraint applies to any choice for this option.
1446 */
1447
1448 for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
1449 if (c2->marked)
1450 break;
1451
1452 if (j == 0 ||
1453 !strcasecmp(c2->choice, "None") ||
1454 !strcasecmp(c2->choice, "Off") ||
1455 !strcasecmp(c2->choice, "False"))
1456 c2 = NULL;
1457 }
1458
1459 /*
1460 * If both options are marked then there is a conflict...
1461 */
1462
1463 if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
1464 _cupsLangPrintf(stdout,
1465 _(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
1466 " (constraint=\"%s %s %s %s\")\n"),
1467 o1->keyword, c1->choice, o2->keyword, c2->choice,
1468 c->option1, c->choice1, c->option2, c->choice2);
1469 }
1470 }
1471
1472
1473 /*
1474 * 'usage()' - Show program usage...
1475 */
1476
1477 void
1478 usage(void)
1479 {
1480 _cupsLangPuts(stdout,
1481 _("Usage: cupstestppd [-q] [-r] [-v[v]] filename1.ppd[.gz] "
1482 "[... filenameN.ppd[.gz]]\n"
1483 " program | cupstestppd [-q] [-r] [-v[v]] -\n"));
1484
1485 exit(ERROR_USAGE);
1486 }
1487
1488
1489 /*
1490 * End of "$Id: cupstestppd.c 4990 2006-01-26 02:21:45Z mike $".
1491 */