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