]> git.ipfire.org Git - thirdparty/cups.git/blame - systemv/cupstestppd.c
Load cups into easysw/current.
[thirdparty/cups.git] / systemv / cupstestppd.c
CommitLineData
ef416fc2 1/*
f7deaa1a 2 * "$Id: cupstestppd.c 6322 2007-03-08 19:25:26Z mike $"
ef416fc2 3 *
4 * PPD test program for the Common UNIX Printing System (CUPS).
5 *
f7deaa1a 6 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 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...
f7deaa1a 33 * valid_utf8() - Check whether a string contains valid UTF-8 text.
ef416fc2 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
fa73b229 51enum
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
65enum
66{
67 EOL_NONE = 0,
68 EOL_CR,
69 EOL_LF,
70 EOL_CRLF
71};
ef416fc2 72
73
74/*
75 * Local functions...
76 */
77
fa73b229 78void check_basics(const char *filename);
ef416fc2 79void show_conflicts(ppd_file_t *ppd);
80void usage(void);
f7deaa1a 81int valid_utf8(const char *s);
ef416fc2 82
83/*
84 * 'main()' - Main entry for test program.
85 */
86
87int /* O - Exit status */
88main(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
07725fee 117 _cupsSetLocale(argv);
d09495fa 118
ef416fc2 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 {
fa73b229 139 _cupsLangPuts(stderr,
ef416fc2 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 {
fa73b229 155 _cupsLangPuts(stderr,
ef416fc2 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)
fa73b229 176 _cupsLangPuts(stdout, "\n");
ef416fc2 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)
fa73b229 212 _cupsLangPrintf(stdout,
ef416fc2 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 {
fa73b229 223 _cupsLangPrintf(stdout,
ef416fc2 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 :
fa73b229 232 _cupsLangPuts(stdout,
ef416fc2 233 _(" REF: Page 42, section 5.2.\n"));
234 break;
235 case PPD_MISSING_VALUE :
fa73b229 236 _cupsLangPuts(stdout,
ef416fc2 237 _(" REF: Page 20, section 3.4.\n"));
238 break;
239 case PPD_BAD_OPEN_GROUP :
240 case PPD_NESTED_OPEN_GROUP :
fa73b229 241 _cupsLangPuts(stdout,
ef416fc2 242 _(" REF: Pages 45-46, section 5.2.\n"));
243 break;
244 case PPD_BAD_OPEN_UI :
245 case PPD_NESTED_OPEN_UI :
fa73b229 246 _cupsLangPuts(stdout,
ef416fc2 247 _(" REF: Pages 42-45, section 5.2.\n"));
248 break;
249 case PPD_BAD_ORDER_DEPENDENCY :
fa73b229 250 _cupsLangPuts(stdout,
ef416fc2 251 _(" REF: Pages 48-49, section 5.2.\n"));
252 break;
253 case PPD_BAD_UI_CONSTRAINTS :
fa73b229 254 _cupsLangPuts(stdout,
ef416fc2 255 _(" REF: Pages 52-54, section 5.2.\n"));
256 break;
257 case PPD_MISSING_ASTERISK :
fa73b229 258 _cupsLangPuts(stdout,
ef416fc2 259 _(" REF: Page 15, section 3.2.\n"));
260 break;
261 case PPD_LINE_TOO_LONG :
fa73b229 262 _cupsLangPuts(stdout,
ef416fc2 263 _(" REF: Page 15, section 3.1.\n"));
264 break;
265 case PPD_ILLEGAL_CHARACTER :
fa73b229 266 _cupsLangPuts(stdout,
ef416fc2 267 _(" REF: Page 15, section 3.1.\n"));
268 break;
269 case PPD_ILLEGAL_MAIN_KEYWORD :
fa73b229 270 _cupsLangPuts(stdout,
ef416fc2 271 _(" REF: Pages 16-17, section 3.2.\n"));
272 break;
273 case PPD_ILLEGAL_OPTION_KEYWORD :
fa73b229 274 _cupsLangPuts(stdout,
ef416fc2 275 _(" REF: Page 19, section 3.3.\n"));
276 break;
277 case PPD_ILLEGAL_TRANSLATION :
fa73b229 278 _cupsLangPuts(stdout,
ef416fc2 279 _(" REF: Page 27, section 3.5.\n"));
280 break;
281 default :
282 break;
283 }
fa73b229 284
285 check_basics(argv[i]);
ef416fc2 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)
fa73b229 301 _cupsLangPuts(stdout,
ef416fc2 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
b423cd4c 308 /*
309 * Look for default keywords with no matching option...
310 */
311
d09495fa 312 for (j = 0; j < ppd->num_attrs; j ++)
ef416fc2 313 {
d09495fa 314 attr = ppd->attrs[j];
ef416fc2 315
b423cd4c 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))
ef416fc2 325 {
89d46774 326 if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
327 strcmp(attr->value, "Unknown"))
b423cd4c 328 {
329 /*
330 * Check that the default option value matches a choice...
331 */
332
d09495fa 333 for (k = 0; k < option->num_choices; k ++)
334 if (!strcmp(option->choices[k].choice, attr->value))
b423cd4c 335 break;
336
d09495fa 337 if (k >= option->num_choices)
b423cd4c 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 }
ef416fc2 353 }
354
355 if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
356 {
357 if (verbose >= 0)
358 {
359 if (!errors && !verbose)
fa73b229 360 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 361
fa73b229 362 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 375 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 376
fa73b229 377 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 388 _cupsLangPuts(stdout, _(" PASS DefaultImageableArea\n"));
ef416fc2 389 }
390
391 if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
392 {
393 if (verbose >= 0)
394 {
395 if (!errors && !verbose)
fa73b229 396 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 397
fa73b229 398 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 411 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 412
fa73b229 413 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 422 _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension\n"));
ef416fc2 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)
fa73b229 439 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 440
fa73b229 441 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 450 _cupsLangPrintf(stdout,
ef416fc2 451 _(" PASS Default%s\n"),
452 option->keyword);
453 }
454 else
455 {
456 if (verbose >= 0)
457 {
458 if (!errors && !verbose)
fa73b229 459 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 460
fa73b229 461 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 474 _cupsLangPuts(stdout, _(" PASS FileVersion\n"));
ef416fc2 475 }
476 else
477 {
478 if (verbose >= 0)
479 {
480 if (!errors && !verbose)
fa73b229 481 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 482
fa73b229 483 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 494 _cupsLangPuts(stdout, _(" PASS FormatVersion\n"));
ef416fc2 495 }
496 else
497 {
498 if (verbose >= 0)
499 {
500 if (!errors && !verbose)
fa73b229 501 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 502
fa73b229 503 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 514 _cupsLangPuts(stdout, _(" PASS LanguageEncoding\n"));
ef416fc2 515 }
516 else if (ppdversion > 40)
517 {
518 if (verbose >= 0)
519 {
520 if (!errors && !verbose)
fa73b229 521 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 522
fa73b229 523 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 534 _cupsLangPuts(stdout, _(" PASS LanguageVersion\n"));
ef416fc2 535 }
536 else
537 {
538 if (verbose >= 0)
539 {
540 if (!errors && !verbose)
fa73b229 541 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 542
fa73b229 543 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 559 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 560
fa73b229 561 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 570 _cupsLangPuts(stdout, _(" PASS Manufacturer\n"));
ef416fc2 571 }
572 else if (ppdversion >= 43)
573 {
574 if (verbose >= 0)
575 {
576 if (!errors && !verbose)
fa73b229 577 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 578
fa73b229 579 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 598 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 599
fa73b229 600 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 610 _cupsLangPuts(stdout, _(" PASS ModelName\n"));
ef416fc2 611 }
612 else
613 {
614 if (verbose >= 0)
615 {
616 if (!errors && !verbose)
fa73b229 617 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 618
fa73b229 619 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 630 _cupsLangPuts(stdout, _(" PASS NickName\n"));
ef416fc2 631 }
632 else
633 {
634 if (verbose >= 0)
635 {
636 if (!errors && !verbose)
fa73b229 637 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 638
fa73b229 639 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 650 _cupsLangPuts(stdout, _(" PASS PageSize\n"));
ef416fc2 651 }
652 else
653 {
654 if (verbose >= 0)
655 {
656 if (!errors && !verbose)
fa73b229 657 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 658
fa73b229 659 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 670 _cupsLangPuts(stdout, _(" PASS PageRegion\n"));
ef416fc2 671 }
672 else
673 {
674 if (verbose >= 0)
675 {
676 if (!errors && !verbose)
fa73b229 677 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 678
fa73b229 679 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 690 _cupsLangPuts(stdout, _(" PASS PCFileName\n"));
ef416fc2 691 }
692 else
693 {
694 if (verbose >= 0)
695 {
696 if (!errors && !verbose)
fa73b229 697 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 698
fa73b229 699 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 715 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 716
fa73b229 717 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 725 _cupsLangPuts(stdout, _(" PASS Product\n"));
ef416fc2 726 }
727 else
728 {
729 if (verbose >= 0)
730 {
731 if (!errors && !verbose)
fa73b229 732 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 733
fa73b229 734 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 754 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 755
fa73b229 756 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 765 _cupsLangPuts(stdout, _(" PASS PSVersion\n"));
ef416fc2 766 }
767 else
768 {
769 if (verbose >= 0)
770 {
771 if (!errors && !verbose)
fa73b229 772 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 773
fa73b229 774 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 789 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 790
fa73b229 791 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 800 _cupsLangPuts(stdout, _(" PASS ShortNickName\n"));
ef416fc2 801 }
802 else if (ppdversion >= 43)
803 {
804 if (verbose >= 0)
805 {
806 if (!errors && !verbose)
fa73b229 807 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 808
fa73b229 809 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 823 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 824
fa73b229 825 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 843 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 844
fa73b229 845 _cupsLangPuts(stdout,
ef416fc2 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)
fa73b229 874 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 875
fa73b229 876 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 896 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 897
fa73b229 898 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 944 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 945
fa73b229 946 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 973 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 974
fa73b229 975 _cupsLangPrintf(stdout,
ef416fc2 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)
fa73b229 994 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 995
fa73b229 996 _cupsLangPrintf(stdout,
ef416fc2 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
b423cd4c 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 */
d09495fa 1016 keyword[PPD_MAX_NAME], /* Localization keyword */
1017 ckeyword[PPD_MAX_NAME]; /* Custom option keyword */
1018 ppd_coption_t *coption; /* Custom option */
1019 ppd_cparam_t *cparam; /* Custom parameter */
b423cd4c 1020
1021
1022 languages = strdup(attr->value);
1023 for (langptr = languages; *langptr;)
1024 {
1025 /*
1026 * Skip leading whitespace...
1027 */
1028
1029 while (isspace(*langptr & 255))
1030 langptr ++;
1031
1032 if (!*langptr)
1033 break;
1034
1035 /*
1036 * Find the end of this language name...
1037 */
1038
1039 for (langstart = langptr;
1040 *langptr && !isspace(*langptr & 255);
1041 langptr ++);
1042
1043 if (*langptr)
1044 *langptr++ = '\0';
1045
1046 j = strlen(langstart);
1047 if (j != 2 && j != 5)
1048 {
1049 if (verbose >= 0)
1050 {
1051 if (!errors && !verbose)
1052 _cupsLangPuts(stdout, _(" FAIL\n"));
1053
1054 _cupsLangPrintf(stdout,
1055 _(" **FAIL** Bad language \"%s\"!\n"),
1056 langstart);
1057 }
1058
1059 errors ++;
1060 continue;
1061 }
1062
1063 /*
1064 * Loop through all options and choices...
1065 */
1066
1067 for (option = ppdFirstOption(ppd);
1068 option;
1069 option = ppdNextOption(ppd))
1070 {
1071 snprintf(keyword, sizeof(keyword), "%s.Translation", langstart);
f7deaa1a 1072 if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL)
b423cd4c 1073 {
1074 if (verbose >= 0)
1075 {
1076 if (!errors && !verbose)
1077 _cupsLangPuts(stdout, _(" FAIL\n"));
1078
1079 _cupsLangPrintf(stdout,
1080 _(" **FAIL** Missing \"%s\" translation "
1081 "string for option %s!\n"),
1082 langstart, option->keyword);
1083 }
1084
1085 errors ++;
1086 }
f7deaa1a 1087 else if (!valid_utf8(attr->text))
1088 {
1089 if (verbose >= 0)
1090 {
1091 if (!errors && !verbose)
1092 _cupsLangPuts(stdout, _(" FAIL\n"));
1093
1094 _cupsLangPrintf(stdout,
1095 _(" **FAIL** Bad UTF-8 \"%s\" translation "
1096 "string for option %s!\n"),
1097 langstart, option->keyword);
1098 }
1099
1100 errors ++;
1101 }
b423cd4c 1102
1103 for (ptr = option->text; *ptr; ptr ++)
1104 if (*ptr & 128)
1105 break;
1106
1107 if (*ptr)
1108 {
1109 if (verbose >= 0)
1110 {
1111 if (!errors && !verbose)
1112 _cupsLangPuts(stdout, _(" FAIL\n"));
1113
1114 _cupsLangPrintf(stdout,
1115 _(" **FAIL** Default translation "
1116 "string for option %s contains 8-bit "
1117 "characters!\n"),
1118 option->keyword);
1119 }
1120
1121 errors ++;
1122 }
1123
1124 snprintf(keyword, sizeof(keyword), "%s.%s", langstart,
1125 option->keyword);
1126 for (j = 0; j < option->num_choices; j ++)
1127 {
d09495fa 1128 if (!strcasecmp(option->choices[j].choice, "Custom") &&
1129 (coption = ppdFindCustomOption(ppd,
1130 option->keyword)) != NULL)
1131 {
1132 snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
1133 langstart, option->keyword);
1134
f7deaa1a 1135 if ((attr = ppdFindAttr(ppd, ckeyword, "True")) == NULL)
d09495fa 1136 {
1137 if (verbose >= 0)
1138 {
1139 if (!errors && !verbose)
1140 _cupsLangPuts(stdout, _(" FAIL\n"));
1141
1142 _cupsLangPrintf(stdout,
1143 _(" **FAIL** Missing \"%s\" "
1144 "translation string for option %s, "
1145 "choice %s!\n"),
1146 langstart, ckeyword + 1 + strlen(langstart),
1147 "True");
1148 }
1149
1150 errors ++;
1151 }
f7deaa1a 1152 else if (!valid_utf8(attr->text))
1153 {
1154 if (verbose >= 0)
1155 {
1156 if (!errors && !verbose)
1157 _cupsLangPuts(stdout, _(" FAIL\n"));
1158
1159 _cupsLangPrintf(stdout,
1160 _(" **FAIL** Bad UTF-8 \"%s\" "
1161 "translation string for option %s, "
1162 "choice %s!\n"),
1163 langstart, ckeyword + 1 + strlen(langstart),
1164 "True");
1165 }
1166
1167 errors ++;
1168 }
d09495fa 1169
1170 if (strcasecmp(option->keyword, "PageSize"))
1171 {
1172 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
1173 cparam;
1174 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
1175 {
1176 snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
1177 langstart, option->keyword);
f7deaa1a 1178 if ((attr = ppdFindAttr(ppd, ckeyword, cparam->name)) == NULL)
d09495fa 1179 {
1180 if (verbose >= 0)
1181 {
1182 if (!errors && !verbose)
1183 _cupsLangPuts(stdout, _(" FAIL\n"));
1184
1185 _cupsLangPrintf(stdout,
1186 _(" **FAIL** Missing \"%s\" "
1187 "translation string for option %s, "
1188 "choice %s!\n"),
1189 langstart,
1190 ckeyword + 1 + strlen(langstart),
1191 cparam->name);
1192 }
1193
f7deaa1a 1194 errors ++;
1195 }
1196 else if (!valid_utf8(attr->text))
1197 {
1198 if (verbose >= 0)
1199 {
1200 if (!errors && !verbose)
1201 _cupsLangPuts(stdout, _(" FAIL\n"));
1202
1203 _cupsLangPrintf(stdout,
1204 _(" **FAIL** Bad UTF-8 \"%s\" "
1205 "translation string for option %s, "
1206 "choice %s!\n"),
1207 langstart,
1208 ckeyword + 1 + strlen(langstart),
1209 cparam->name);
1210 }
1211
d09495fa 1212 errors ++;
1213 }
1214 }
1215 }
1216 }
f7deaa1a 1217 else if ((attr = ppdFindAttr(ppd, keyword, option->choices[j].choice)) == NULL)
b423cd4c 1218 {
1219 if (verbose >= 0)
1220 {
1221 if (!errors && !verbose)
1222 _cupsLangPuts(stdout, _(" FAIL\n"));
1223
1224 _cupsLangPrintf(stdout,
1225 _(" **FAIL** Missing \"%s\" "
1226 "translation string for option %s, "
1227 "choice %s!\n"),
1228 langstart, option->keyword,
1229 option->choices[j].choice);
1230 }
1231
1232 errors ++;
1233 }
f7deaa1a 1234 else if (!valid_utf8(attr->text))
1235 {
1236 if (verbose >= 0)
1237 {
1238 if (!errors && !verbose)
1239 _cupsLangPuts(stdout, _(" FAIL\n"));
1240
1241 _cupsLangPrintf(stdout,
1242 _(" **FAIL** Bad UTF-8 \"%s\" "
1243 "translation string for option %s, "
1244 "choice %s!\n"),
1245 langstart, option->keyword,
1246 option->choices[j].choice);
1247 }
1248
1249 errors ++;
1250 }
b423cd4c 1251
1252 for (ptr = option->choices[j].text; *ptr; ptr ++)
1253 if (*ptr & 128)
1254 break;
1255
1256 if (*ptr)
1257 {
1258 if (verbose >= 0)
1259 {
1260 if (!errors && !verbose)
1261 _cupsLangPuts(stdout, _(" FAIL\n"));
1262
1263 _cupsLangPrintf(stdout,
1264 _(" **FAIL** Default translation "
1265 "string for option %s choice %s contains "
1266 "8-bit characters!\n"),
1267 option->keyword,
1268 option->choices[j].choice);
1269 }
1270
1271 errors ++;
1272 }
1273 }
1274 }
1275 }
1276 }
1277
1278 for (attr = ppdFindAttr(ppd, "cupsFilter", NULL);
1279 attr;
1280 attr = ppdFindNextAttr(ppd, "cupsFilter", NULL))
1281 {
1282 char super[16], /* Filter super type */
1283 type[256], /* Filter type */
1284 program[256]; /* Filter program */
1285 int cost; /* Filter cost */
1286
1287
1288 if (!attr->value ||
1289 sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
1290 program) != 4)
1291 {
1292 if (verbose >= 0)
1293 {
1294 if (!errors && !verbose)
1295 _cupsLangPuts(stdout, _(" FAIL\n"));
1296
1297 _cupsLangPrintf(stdout,
1298 _(" **FAIL** Bad cupsFilter value \"%s\"!\n"),
1299 attr->value ? attr->value : "");
1300 }
1301
1302 errors ++;
1303 }
1304 }
1305
f301802f 1306 if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
1307 strcmp(attr->name, "1284DeviceID"))
89d46774 1308 {
1309 if (verbose >= 0)
1310 {
1311 if (!errors && !verbose)
1312 _cupsLangPuts(stdout, _(" FAIL\n"));
1313
d09495fa 1314 _cupsLangPrintf(stdout,
1315 _(" **FAIL** %s must be 1284DeviceID!\n"
1316 " REF: Page 72, section 5.5\n"),
1317 attr->name);
89d46774 1318 }
1319
1320 errors ++;
1321 }
1322
ef416fc2 1323 if (errors)
1324 status = ERROR_CONFORMANCE;
1325 else if (!verbose)
fa73b229 1326 _cupsLangPuts(stdout, _(" PASS\n"));
ef416fc2 1327
1328 if (verbose >= 0)
1329 {
fa73b229 1330 check_basics(argv[i]);
1331
89d46774 1332 /*
1333 * Look for default keywords with no corresponding option...
1334 */
1335
d09495fa 1336 for (j = 0; j < ppd->num_attrs; j ++)
89d46774 1337 {
d09495fa 1338 attr = ppd->attrs[j];
89d46774 1339
1340 if (!strcmp(attr->name, "DefaultColorSpace") ||
1341 !strcmp(attr->name, "DefaultColorSep") ||
1342 !strcmp(attr->name, "DefaultFont") ||
1343 !strcmp(attr->name, "DefaultImageableArea") ||
1344 !strcmp(attr->name, "DefaultOutputOrder") ||
1345 !strcmp(attr->name, "DefaultPaperDimension") ||
1346 !strcmp(attr->name, "DefaultScreenProc") ||
1347 !strcmp(attr->name, "DefaultTransfer"))
1348 continue;
1349
1350 if (!strncmp(attr->name, "Default", 7) &&
1351 !ppdFindOption(ppd, attr->name + 7))
1352 _cupsLangPrintf(stdout,
1353 _(" WARN %s has no corresponding "
1354 "options!\n"),
1355 attr->name);
1356 }
1357
1358 /*
1359 * Check for old Duplex option names...
1360 */
1361
1362 if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
1363 option = ppdFindOption(ppd, "KD03Duplex");
1364
1365 if (option)
ef416fc2 1366 {
fa73b229 1367 _cupsLangPrintf(stdout,
ef416fc2 1368 _(" WARN Duplex option keyword %s "
1369 "should be named Duplex or JCLDuplex!\n"
1370 " REF: Page 122, section 5.17\n"),
1371 option->keyword);
1372 }
1373
1374 ppdMarkDefaults(ppd);
1375 if (ppdConflicts(ppd))
1376 {
fa73b229 1377 _cupsLangPuts(stdout,
ef416fc2 1378 _(" WARN Default choices conflicting!\n"));
1379
1380 show_conflicts(ppd);
1381 }
1382
1383 if (ppdversion < 43)
1384 {
fa73b229 1385 _cupsLangPrintf(stdout,
ef416fc2 1386 _(" WARN Obsolete PPD version %.1f!\n"
1387 " REF: Page 42, section 5.2.\n"),
1388 0.1f * ppdversion);
1389 }
1390
1391 if (!ppd->lang_encoding && ppdversion < 41)
1392 {
fa73b229 1393 _cupsLangPuts(stdout,
ef416fc2 1394 _(" WARN LanguageEncoding required by PPD "
1395 "4.3 spec.\n"
1396 " REF: Pages 56-57, section 5.3.\n"));
1397 }
1398
1399 if (!ppd->manufacturer && ppdversion < 43)
1400 {
fa73b229 1401 _cupsLangPuts(stdout,
ef416fc2 1402 _(" WARN Manufacturer required by PPD "
1403 "4.3 spec.\n"
1404 " REF: Pages 58-59, section 5.3.\n"));
1405 }
1406
1407 /*
1408 * Treat a PCFileName attribute longer than 12 characters as
1409 * a warning and not a hard error...
1410 */
1411
1412 if (ppd->pcfilename && strlen(ppd->pcfilename) > 12)
1413 {
fa73b229 1414 _cupsLangPuts(stdout,
ef416fc2 1415 _(" WARN PCFileName longer than 8.3 in "
1416 "violation of PPD spec.\n"
1417 " REF: Pages 61-62, section 5.3.\n"));
1418 }
1419
1420 if (!ppd->shortnickname && ppdversion < 43)
1421 {
fa73b229 1422 _cupsLangPuts(stdout,
ef416fc2 1423 _(" WARN ShortNickName required by PPD "
1424 "4.3 spec.\n"
1425 " REF: Pages 64-65, section 5.3.\n"));
1426 }
1427
1428 /*
1429 * Check the Protocols line and flag PJL + BCP since TBCP is
1430 * usually used with PJL...
1431 */
1432
1433 if (ppd->protocols)
1434 {
1435 if (strstr(ppd->protocols, "PJL") &&
1436 strstr(ppd->protocols, "BCP") &&
1437 !strstr(ppd->protocols, "TBCP"))
1438 {
fa73b229 1439 _cupsLangPuts(stdout,
ef416fc2 1440 _(" WARN Protocols contains both PJL "
1441 "and BCP; expected TBCP.\n"
1442 " REF: Pages 78-79, section 5.7.\n"));
1443 }
1444
1445 if (strstr(ppd->protocols, "PJL") &&
1446 (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
1447 {
fa73b229 1448 _cupsLangPuts(stdout,
ef416fc2 1449 _(" WARN Protocols contains PJL but JCL "
1450 "attributes are not set.\n"
1451 " REF: Pages 78-79, section 5.7.\n"));
1452 }
1453 }
1454
1455 /*
1456 * Check for options with a common prefix, e.g. Duplex and Duplexer,
1457 * which are errors according to the spec but won't cause problems
1458 * with CUPS specifically...
1459 */
1460
1461 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1462 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1463 {
1464 len = strlen(option->keyword);
1465
1466 for (m = 0, group2 = ppd->groups;
1467 m < ppd->num_groups;
1468 m ++, group2 ++)
1469 for (n = 0, option2 = group2->options;
1470 n < group2->num_options;
1471 n ++, option2 ++)
1472 if (option != option2 &&
1473 len < strlen(option2->keyword) &&
1474 !strncmp(option->keyword, option2->keyword, len))
1475 {
fa73b229 1476 _cupsLangPrintf(stdout,
ef416fc2 1477 _(" WARN %s shares a common "
1478 "prefix with %s\n"
1479 " REF: Page 15, section "
1480 "3.2.\n"),
1481 option->keyword, option2->keyword);
1482 }
1483 }
1484 }
1485
1486 if (verbose > 0)
1487 {
1488 if (errors)
f7deaa1a 1489 _cupsLangPrintf(stdout, _(" %d ERRORS FOUND\n"), errors);
ef416fc2 1490 else
fa73b229 1491 _cupsLangPuts(stdout, _(" NO ERRORS FOUND\n"));
ef416fc2 1492 }
1493
ef416fc2 1494 /*
1495 * Then list the options, if "-v" was provided...
1496 */
1497
1498 if (verbose > 1)
1499 {
fa73b229 1500 _cupsLangPrintf(stdout,
ef416fc2 1501 "\n"
1502 " language_level = %d\n"
1503 " color_device = %s\n"
1504 " variable_sizes = %s\n"
1505 " landscape = %d\n",
1506 ppd->language_level,
1507 ppd->color_device ? "TRUE" : "FALSE",
1508 ppd->variable_sizes ? "TRUE" : "FALSE",
1509 ppd->landscape);
1510
1511 switch (ppd->colorspace)
1512 {
1513 case PPD_CS_CMYK :
fa73b229 1514 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK\n");
ef416fc2 1515 break;
1516 case PPD_CS_CMY :
fa73b229 1517 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY\n");
ef416fc2 1518 break;
1519 case PPD_CS_GRAY :
fa73b229 1520 _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY\n");
ef416fc2 1521 break;
1522 case PPD_CS_RGB :
fa73b229 1523 _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB\n");
ef416fc2 1524 break;
1525 default :
fa73b229 1526 _cupsLangPuts(stdout, " colorspace = <unknown>\n");
ef416fc2 1527 break;
1528 }
1529
fa73b229 1530 _cupsLangPrintf(stdout, " num_emulations = %d\n",
ef416fc2 1531 ppd->num_emulations);
1532 for (j = 0; j < ppd->num_emulations; j ++)
fa73b229 1533 _cupsLangPrintf(stdout, " emulations[%d] = %s\n",
ef416fc2 1534 j, ppd->emulations[j].name);
1535
fa73b229 1536 _cupsLangPrintf(stdout, " lang_encoding = %s\n",
ef416fc2 1537 ppd->lang_encoding);
fa73b229 1538 _cupsLangPrintf(stdout, " lang_version = %s\n",
ef416fc2 1539 ppd->lang_version);
fa73b229 1540 _cupsLangPrintf(stdout, " modelname = %s\n", ppd->modelname);
1541 _cupsLangPrintf(stdout, " ttrasterizer = %s\n",
ef416fc2 1542 ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
fa73b229 1543 _cupsLangPrintf(stdout, " manufacturer = %s\n",
ef416fc2 1544 ppd->manufacturer);
fa73b229 1545 _cupsLangPrintf(stdout, " product = %s\n", ppd->product);
1546 _cupsLangPrintf(stdout, " nickname = %s\n", ppd->nickname);
1547 _cupsLangPrintf(stdout, " shortnickname = %s\n",
ef416fc2 1548 ppd->shortnickname);
fa73b229 1549 _cupsLangPrintf(stdout, " patches = %d bytes\n",
ef416fc2 1550 ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
1551
fa73b229 1552 _cupsLangPrintf(stdout, " num_groups = %d\n", ppd->num_groups);
ef416fc2 1553 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1554 {
fa73b229 1555 _cupsLangPrintf(stdout, " group[%d] = %s\n",
ef416fc2 1556 j, group->text);
1557
1558 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1559 {
fa73b229 1560 _cupsLangPrintf(stdout,
ef416fc2 1561 " options[%d] = %s (%s) %s %s %.0f "
1562 "(%d choices)\n",
1563 k, option->keyword, option->text, uis[option->ui],
1564 sections[option->section], option->order,
1565 option->num_choices);
1566
1567 if (!strcmp(option->keyword, "PageSize") ||
1568 !strcmp(option->keyword, "PageRegion"))
1569 {
1570 for (m = option->num_choices, choice = option->choices;
1571 m > 0;
1572 m --, choice ++)
1573 {
1574 size = ppdPageSize(ppd, choice->choice);
1575
1576 if (size == NULL)
fa73b229 1577 _cupsLangPrintf(stdout,
ef416fc2 1578 " %s (%s) = ERROR",
1579 choice->choice, choice->text);
1580 else
fa73b229 1581 _cupsLangPrintf(stdout,
ef416fc2 1582 " %s (%s) = %.2fx%.2fin "
1583 "(%.1f,%.1f,%.1f,%.1f)",
1584 choice->choice, choice->text,
1585 size->width / 72.0, size->length / 72.0,
1586 size->left / 72.0, size->bottom / 72.0,
1587 size->right / 72.0, size->top / 72.0);
1588
1589 if (!strcmp(option->defchoice, choice->choice))
fa73b229 1590 _cupsLangPuts(stdout, " *\n");
ef416fc2 1591 else
fa73b229 1592 _cupsLangPuts(stdout, "\n");
ef416fc2 1593 }
1594 }
1595 else
1596 {
1597 for (m = option->num_choices, choice = option->choices;
1598 m > 0;
1599 m --, choice ++)
1600 {
fa73b229 1601 _cupsLangPrintf(stdout, " %s (%s)",
ef416fc2 1602 choice->choice, choice->text);
1603
1604 if (!strcmp(option->defchoice, choice->choice))
fa73b229 1605 _cupsLangPuts(stdout, " *\n");
ef416fc2 1606 else
fa73b229 1607 _cupsLangPuts(stdout, "\n");
ef416fc2 1608 }
1609 }
1610 }
1611 }
1612
fa73b229 1613 _cupsLangPrintf(stdout, " num_profiles = %d\n",
ef416fc2 1614 ppd->num_profiles);
1615 for (j = 0; j < ppd->num_profiles; j ++)
fa73b229 1616 _cupsLangPrintf(stdout,
ef416fc2 1617 " profiles[%d] = %s/%s %.3f %.3f "
1618 "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
1619 j, ppd->profiles[j].resolution,
1620 ppd->profiles[j].media_type,
1621 ppd->profiles[j].gamma, ppd->profiles[j].density,
1622 ppd->profiles[j].matrix[0][0],
1623 ppd->profiles[j].matrix[0][1],
1624 ppd->profiles[j].matrix[0][2],
1625 ppd->profiles[j].matrix[1][0],
1626 ppd->profiles[j].matrix[1][1],
1627 ppd->profiles[j].matrix[1][2],
1628 ppd->profiles[j].matrix[2][0],
1629 ppd->profiles[j].matrix[2][1],
1630 ppd->profiles[j].matrix[2][2]);
1631
fa73b229 1632 _cupsLangPrintf(stdout, " num_fonts = %d\n", ppd->num_fonts);
ef416fc2 1633 for (j = 0; j < ppd->num_fonts; j ++)
fa73b229 1634 _cupsLangPrintf(stdout, " fonts[%d] = %s\n",
ef416fc2 1635 j, ppd->fonts[j]);
1636
fa73b229 1637 _cupsLangPrintf(stdout, " num_attrs = %d\n", ppd->num_attrs);
ef416fc2 1638 for (j = 0; j < ppd->num_attrs; j ++)
fa73b229 1639 _cupsLangPrintf(stdout,
ef416fc2 1640 " attrs[%d] = %s %s%s%s: \"%s\"\n", j,
1641 ppd->attrs[j]->name, ppd->attrs[j]->spec,
1642 ppd->attrs[j]->text[0] ? "/" : "",
1643 ppd->attrs[j]->text,
1644 ppd->attrs[j]->value ?
1645 ppd->attrs[j]->value : "(null)");
1646 }
1647
1648 ppdClose(ppd);
1649 }
1650
1651 if (!files)
1652 usage();
1653
1654 return (status);
1655}
1656
1657
fa73b229 1658/*
1659 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1660 */
1661
1662void
1663check_basics(const char *filename) /* I - PPD file to check */
1664{
1665 cups_file_t *fp; /* File pointer */
1666 int ch; /* Current character */
1667 int col, /* Current column */
1668 whitespace; /* Only seen whitespace? */
1669 int eol; /* Line endings */
1670 int linenum; /* Line number */
1671 int mixed; /* Mixed line endings? */
1672
1673
1674 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1675 return;
1676
1677 linenum = 1;
1678 col = 0;
1679 eol = EOL_NONE;
1680 mixed = 0;
1681 whitespace = 1;
1682
1683 while ((ch = cupsFileGetChar(fp)) != EOF)
1684 {
1685 if (ch == '\r' || ch == '\n')
1686 {
1687 if (ch == '\n')
1688 {
1689 if (eol == EOL_NONE)
1690 eol = EOL_LF;
1691 else if (eol != EOL_LF)
1692 mixed = 1;
1693 }
1694 else if (ch == '\r')
1695 {
1696 if (cupsFilePeekChar(fp) == '\n')
1697 {
1698 cupsFileGetChar(fp);
1699
1700 if (eol == EOL_NONE)
1701 eol = EOL_CRLF;
1702 else
1703 mixed = 1;
1704 }
1705 else if (eol == EOL_NONE)
1706 eol = EOL_CR;
1707 else
1708 mixed = 1;
1709 }
1710
1711 if (col > 0 && whitespace)
1712 _cupsLangPrintf(stdout,
1713 _(" WARN Line %d only contains whitespace!\n"),
1714 linenum);
1715
1716 linenum ++;
1717 col = 0;
1718 whitespace = 1;
1719 }
1720 else
1721 {
1722 if (ch != ' ' && ch != '\t')
1723 whitespace = 0;
1724
1725 col ++;
1726 }
1727 }
1728
1729 if (mixed)
1730 _cupsLangPuts(stdout,
1731 _(" WARN File contains a mix of CR, LF, and "
1732 "CR LF line endings!\n"));
1733
1734 if (eol == EOL_CRLF)
1735 _cupsLangPuts(stdout,
1736 _(" WARN Non-Windows PPD files should use lines "
1737 "ending with only LF, not CR LF!\n"));
1738
1739 cupsFileClose(fp);
1740}
1741
1742
ef416fc2 1743/*
1744 * 'show_conflicts()' - Show option conflicts in a PPD file.
1745 */
1746
1747void
1748show_conflicts(ppd_file_t *ppd) /* I - PPD to check */
1749{
1750 int i, j; /* Looping variables */
1751 ppd_const_t *c; /* Current constraint */
1752 ppd_option_t *o1, *o2; /* Options */
1753 ppd_choice_t *c1, *c2; /* Choices */
1754
1755
1756 /*
1757 * Loop through all of the UI constraints and report any options
1758 * that conflict...
1759 */
1760
1761 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
1762 {
1763 /*
1764 * Grab pointers to the first option...
1765 */
1766
1767 o1 = ppdFindOption(ppd, c->option1);
1768
1769 if (o1 == NULL)
1770 continue;
1771 else if (c->choice1[0] != '\0')
1772 {
1773 /*
1774 * This constraint maps to a specific choice.
1775 */
1776
1777 c1 = ppdFindChoice(o1, c->choice1);
1778 }
1779 else
1780 {
1781 /*
1782 * This constraint applies to any choice for this option.
1783 */
1784
1785 for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
1786 if (c1->marked)
1787 break;
1788
1789 if (j == 0 ||
1790 !strcasecmp(c1->choice, "None") ||
1791 !strcasecmp(c1->choice, "Off") ||
1792 !strcasecmp(c1->choice, "False"))
1793 c1 = NULL;
1794 }
1795
1796 /*
1797 * Grab pointers to the second option...
1798 */
1799
1800 o2 = ppdFindOption(ppd, c->option2);
1801
1802 if (o2 == NULL)
1803 continue;
1804 else if (c->choice2[0] != '\0')
1805 {
1806 /*
1807 * This constraint maps to a specific choice.
1808 */
1809
1810 c2 = ppdFindChoice(o2, c->choice2);
1811 }
1812 else
1813 {
1814 /*
1815 * This constraint applies to any choice for this option.
1816 */
1817
1818 for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
1819 if (c2->marked)
1820 break;
1821
1822 if (j == 0 ||
1823 !strcasecmp(c2->choice, "None") ||
1824 !strcasecmp(c2->choice, "Off") ||
1825 !strcasecmp(c2->choice, "False"))
1826 c2 = NULL;
1827 }
1828
1829 /*
1830 * If both options are marked then there is a conflict...
1831 */
1832
1833 if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
fa73b229 1834 _cupsLangPrintf(stdout,
ef416fc2 1835 _(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
1836 " (constraint=\"%s %s %s %s\")\n"),
1837 o1->keyword, c1->choice, o2->keyword, c2->choice,
1838 c->option1, c->choice1, c->option2, c->choice2);
1839 }
1840}
1841
1842
1843/*
1844 * 'usage()' - Show program usage...
1845 */
1846
1847void
1848usage(void)
1849{
fa73b229 1850 _cupsLangPuts(stdout,
ef416fc2 1851 _("Usage: cupstestppd [-q] [-r] [-v[v]] filename1.ppd[.gz] "
1852 "[... filenameN.ppd[.gz]]\n"
1853 " program | cupstestppd [-q] [-r] [-v[v]] -\n"));
1854
1855 exit(ERROR_USAGE);
1856}
1857
1858
1859/*
f7deaa1a 1860 * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
1861 */
1862
1863int /* O - 1 if valid, 0 if not */
1864valid_utf8(const char *s) /* I - String to check */
1865{
1866 while (*s)
1867 {
1868 if (*s & 0x80)
1869 {
1870 /*
1871 * Check for valid UTF-8 sequence...
1872 */
1873
1874 if ((*s & 0xc0) == 0x80)
1875 return (0); /* Illegal suffix byte */
1876 else if ((*s & 0xe0) == 0xc0)
1877 {
1878 /*
1879 * 2-byte sequence...
1880 */
1881
1882 s ++;
1883
1884 if ((*s & 0xc0) != 0x80)
1885 return (0); /* Missing suffix byte */
1886 }
1887 else if ((*s & 0xf0) == 0xe0)
1888 {
1889 /*
1890 * 3-byte sequence...
1891 */
1892
1893 s ++;
1894
1895 if ((*s & 0xc0) != 0x80)
1896 return (0); /* Missing suffix byte */
1897
1898 s ++;
1899
1900 if ((*s & 0xc0) != 0x80)
1901 return (0); /* Missing suffix byte */
1902 }
1903 else if ((*s & 0xf8) == 0xf0)
1904 {
1905 /*
1906 * 4-byte sequence...
1907 */
1908
1909 s ++;
1910
1911 if ((*s & 0xc0) != 0x80)
1912 return (0); /* Missing suffix byte */
1913
1914 s ++;
1915
1916 if ((*s & 0xc0) != 0x80)
1917 return (0); /* Missing suffix byte */
1918
1919 s ++;
1920
1921 if ((*s & 0xc0) != 0x80)
1922 return (0); /* Missing suffix byte */
1923 }
1924 else
1925 return (0); /* Bad sequence */
1926 }
1927
1928 s ++;
1929 }
1930
1931 return (1);
1932}
1933
1934
1935/*
1936 * End of "$Id: cupstestppd.c 6322 2007-03-08 19:25:26Z mike $".
ef416fc2 1937 */