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