]> git.ipfire.org Git - thirdparty/cups.git/blame - systemv/cupstestppd.c
Make sure we send a job-progress event for new printer-state-messages...
[thirdparty/cups.git] / systemv / cupstestppd.c
CommitLineData
41b2c2e3 1/*
c9d3f842 2 * "$Id$"
41b2c2e3 3 *
4 * PPD test program for the Common UNIX Printing System (CUPS).
5 *
19b7f3b7 6 * Copyright 2007-2009 by Apple Inc.
8b056e32 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
41b2c2e3 8 *
9 * These coded instructions, statements, and computer programs are the
4e8d321f 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/".
41b2c2e3 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 *
769fe48b 21 * main() - Main entry for test program.
22 * check_basics() - Check for CR LF, mixed line endings, and blank
23 * lines.
02aabcda 24 * check_constraints() - Check UIConstraints in the PPD file.
48c9a702 25 * check_case() - Check that there are no duplicate groups, options,
26 * or choices that differ only by case.
769fe48b 27 * check_defaults() - Check default option keywords in the PPD file.
48c9a702 28 * check_duplex() - Check duplex keywords in the PPD file.
769fe48b 29 * check_filters() - Check filters in the PPD file.
30 * check_profiles() - Check ICC color profiles in the PPD file.
02aabcda 31 * check_sizes() - Check media sizes in the PPD file.
769fe48b 32 * check_translations() - Check translations in the PPD file.
33 * show_conflicts() - Show option conflicts in a PPD file.
34 * test_raster() - Test PostScript commands for raster printers.
35 * usage() - Show program usage...
02aabcda 36 * valid_path() - Check whether a path has the correct capitalization.
769fe48b 37 * valid_utf8() - Check whether a string contains valid UTF-8 text.
41b2c2e3 38 */
39
40/*
41 * Include necessary headers...
42 */
43
41b2c2e3 44#include <cups/string.h>
b7382443 45#include <cups/cups.h>
02aabcda 46#include <cups/dir.h>
c9be2428 47#include <cups/ppd-private.h>
b7382443 48#include <cups/i18n.h>
64c60afc 49#include <cups/raster.h>
41b2c2e3 50#include <errno.h>
43cd52be 51#include <stdlib.h>
19b7f3b7 52#include <math.h>
41b2c2e3 53
54
d1fc9981 55/*
56 * Error warning overrides...
57 */
58
59enum
60{
61 WARN_NONE = 0,
62 WARN_CONSTRAINTS = 1,
63 WARN_DEFAULTS = 2,
64 WARN_FILTERS = 4,
c9be2428 65 WARN_PROFILES = 8,
66 WARN_TRANSLATIONS = 16,
48c9a702 67 WARN_DUPLEX = 32,
19b7f3b7 68 WARN_SIZES = 64,
69 WARN_ALL = 127
d1fc9981 70};
71
72
41b2c2e3 73/*
74 * Error codes...
75 */
76
1a8f3056 77enum
78{
79 ERROR_NONE = 0,
80 ERROR_USAGE,
81 ERROR_FILE_OPEN,
82 ERROR_PPD_FORMAT,
83 ERROR_CONFORMANCE
84};
85
86
87/*
88 * Line endings...
89 */
90
91enum
92{
93 EOL_NONE = 0,
94 EOL_CR,
95 EOL_LF,
96 EOL_CRLF
97};
41b2c2e3 98
99
19b7f3b7 100/*
101 * Standard Adobe media keywords (must remain sorted)...
102 */
103
104static const char adobe_size_names[][PPD_MAX_NAME] =
105{
106 "10x11",
107 "10x13",
108 "10x14",
109 "12x11",
110 "15x11",
111 "7x9",
112 "8x10",
113 "9x11",
114 "9x12",
115 "A0",
116 "A1",
117 "A10",
118 "A2",
119 "A3",
120 "A3Extra",
121 "A3Rotated",
122 "A4",
123 "A4Extra",
124 "A4Plus",
125 "A4Rotated",
126 "A4Small",
127 "A5",
128 "A5Extra",
129 "A5Rotated",
130 "A6",
131 "A6Rotated",
132 "A7",
133 "A8",
134 "A9",
135 "ARCHA",
136 "ARCHB",
137 "ARCHC",
138 "ARCHD",
139 "ARCHE",
140 "AnsiA",
141 "AnsiB",
142 "AnsiC",
143 "AnsiD",
144 "AnsiE",
145 "B0",
146 "B1",
147 "B1",
148 "B10",
149 "B2",
150 "B3",
151 "B4",
152 "B4Rotated",
153 "B5",
154 "B5Rotated",
155 "B6",
156 "B6Rotated",
157 "B7",
158 "B8",
159 "B9",
160 "C4",
161 "C5",
162 "C6",
163 "DL",
164 "DoublePostcard",
165 "DoublePostcardRotated",
166 "Env10",
167 "Env11",
168 "Env12",
169 "Env14",
170 "Env9",
171 "EnvC0",
172 "EnvC1",
173 "EnvC2",
174 "EnvC3",
175 "EnvC4",
176 "EnvC5",
177 "EnvC6",
178 "EnvC65",
179 "EnvC7",
180 "EnvChou3",
181 "EnvChou3Rotated",
182 "EnvChou4",
183 "EnvChou4Rotated",
184 "EnvDL",
185 "EnvISOB4",
186 "EnvISOB5",
187 "EnvISOB6",
188 "EnvInvite",
189 "EnvItalian",
190 "EnvKaku2",
191 "EnvKaku2Rotated",
192 "EnvKaku3",
193 "EnvKaku3Rotated",
194 "EnvMonarch",
195 "EnvPRC1",
196 "EnvPRC10",
197 "EnvPRC10Rotated",
198 "EnvPRC1Rotated",
199 "EnvPRC2",
200 "EnvPRC2Rotated",
201 "EnvPRC3",
202 "EnvPRC3Rotated",
203 "EnvPRC4",
204 "EnvPRC4Rotated",
205 "EnvPRC5",
206 "EnvPRC5Rotated",
207 "EnvPRC6",
208 "EnvPRC6Rotated",
209 "EnvPRC7",
210 "EnvPRC7Rotated",
211 "EnvPRC8",
212 "EnvPRC8Rotated",
213 "EnvPRC9",
214 "EnvPRC9Rotated",
215 "EnvPersonal",
216 "EnvYou4",
217 "EnvYou4Rotated",
218 "Executive",
219 "FanFoldGerman",
220 "FanFoldGermanLegal",
221 "FanFoldUS",
222 "Folio",
223 "ISOB0",
224 "ISOB1",
225 "ISOB10",
226 "ISOB2",
227 "ISOB3",
228 "ISOB4",
229 "ISOB5",
230 "ISOB5Extra",
231 "ISOB6",
232 "ISOB7",
233 "ISOB8",
234 "ISOB9",
235 "Ledger",
236 "Legal",
237 "LegalExtra",
238 "Letter",
239 "LetterExtra",
240 "LetterPlus",
241 "LetterRotated",
242 "LetterSmall",
243 "Monarch",
244 "Note",
245 "PRC16K",
246 "PRC16KRotated",
247 "PRC32K",
248 "PRC32KBig",
249 "PRC32KBigRotated",
250 "PRC32KRotated",
251 "Postcard",
252 "PostcardRotated",
253 "Quarto",
254 "Statement",
255 "SuperA",
256 "SuperB",
257 "Tabloid",
258 "TabloidExtra"
259};
260
261
43cd52be 262/*
263 * Local functions...
264 */
265
0dcff045 266static void check_basics(const char *filename);
267static int check_constraints(ppd_file_t *ppd, int errors, int verbose,
268 int warn);
48c9a702 269static int check_case(ppd_file_t *ppd, int errors, int verbose);
0dcff045 270static int check_defaults(ppd_file_t *ppd, int errors, int verbose,
271 int warn);
48c9a702 272static int check_duplex(ppd_file_t *ppd, int errors, int verbose,
273 int warn);
0dcff045 274static int check_filters(ppd_file_t *ppd, const char *root, int errors,
275 int verbose, int warn);
c9be2428 276static int check_profiles(ppd_file_t *ppd, const char *root, int errors,
277 int verbose, int warn);
19b7f3b7 278static int check_sizes(ppd_file_t *ppd, int errors, int verbose, int warn);
279static int check_translations(ppd_file_t *ppd, int errors, int verbose,
0dcff045 280 int warn);
281static void show_conflicts(ppd_file_t *ppd);
12d95de0 282static int test_raster(ppd_file_t *ppd, int verbose);
0dcff045 283static void usage(void);
02aabcda 284static int valid_path(const char *keyword, const char *path, int errors,
285 int verbose, int warn);
0dcff045 286static int valid_utf8(const char *s);
43cd52be 287
d1fc9981 288
41b2c2e3 289/*
290 * 'main()' - Main entry for test program.
291 */
292
47bb2ead 293int /* O - Exit status */
294main(int argc, /* I - Number of command-line args */
295 char *argv[]) /* I - Command-line arguments */
41b2c2e3 296{
47bb2ead 297 int i, j, k, m, n; /* Looping vars */
298 int len; /* Length of option name */
299 char *opt; /* Option character */
300 const char *ptr; /* Pointer into string */
301 int files; /* Number of files */
302 int verbose; /* Want verbose output? */
d1fc9981 303 int warn; /* Which errors to just warn about */
47bb2ead 304 int status; /* Exit status */
305 int errors; /* Number of conformance errors */
306 int ppdversion; /* PPD spec version in PPD file */
307 ppd_status_t error; /* Status of ppdOpen*() */
308 int line; /* Line number for error */
c9be2428 309 char *root; /* Root directory */
47bb2ead 310 int xdpi, /* X resolution */
311 ydpi; /* Y resolution */
312 ppd_file_t *ppd; /* PPD file record */
313 ppd_attr_t *attr; /* PPD attribute */
314 ppd_size_t *size; /* Size record */
315 ppd_group_t *group; /* UI group */
316 ppd_option_t *option; /* Standard UI option */
317 ppd_group_t *group2; /* UI group */
318 ppd_option_t *option2; /* Standard UI option */
319 ppd_choice_t *choice; /* Standard UI option choice */
5d1b511e 320 struct lconv *loc; /* Locale data */
41b2c2e3 321 static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
322 static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
323 "JCL", "PAGE", "PROLOG" };
324
325
156dd8aa 326 _cupsSetLocale(argv);
5d1b511e 327 loc = localeconv();
ebad28ad 328
41b2c2e3 329 /*
330 * Display PPD files for each file listed on the command-line...
331 */
332
49de8bd2 333 ppdSetConformance(PPD_CONFORM_STRICT);
334
41b2c2e3 335 verbose = 0;
336 ppd = NULL;
79f448f9 337 files = 0;
41b2c2e3 338 status = ERROR_NONE;
16f04511 339 root = "";
d1fc9981 340 warn = WARN_NONE;
41b2c2e3 341
342 for (i = 1; i < argc; i ++)
43cd52be 343 if (argv[i][0] == '-' && argv[i][1])
98c2aef5 344 {
43cd52be 345 for (opt = argv[i] + 1; *opt; opt ++)
346 switch (*opt)
347 {
16f04511 348 case 'R' : /* Alternate root directory */
349 i ++;
350
351 if (i >= argc)
352 usage();
353
354 root = argv[i];
355 break;
356
d1fc9981 357 case 'W' : /* Turn errors into warnings */
358 i ++;
359
360 if (i >= argc)
361 usage();
362
363 if (!strcmp(argv[i], "none"))
364 warn = WARN_NONE;
365 else if (!strcmp(argv[i], "constraints"))
366 warn |= WARN_CONSTRAINTS;
367 else if (!strcmp(argv[i], "defaults"))
368 warn |= WARN_DEFAULTS;
48c9a702 369 else if (!strcmp(argv[i], "duplex"))
370 warn |= WARN_DUPLEX;
d1fc9981 371 else if (!strcmp(argv[i], "filters"))
372 warn |= WARN_FILTERS;
c9be2428 373 else if (!strcmp(argv[i], "profiles"))
374 warn |= WARN_PROFILES;
dead745e 375 else if (!strcmp(argv[i], "sizes"))
376 warn |= WARN_SIZES;
d1fc9981 377 else if (!strcmp(argv[i], "translations"))
378 warn |= WARN_TRANSLATIONS;
379 else if (!strcmp(argv[i], "all"))
380 warn = WARN_ALL;
381 else
382 usage();
383 break;
384
49de8bd2 385 case 'q' : /* Quiet mode */
43cd52be 386 if (verbose > 0)
387 {
89fd567e 388 _cupsLangPuts(stderr,
b7382443 389 _("cupstestppd: The -q option is incompatible "
390 "with the -v option.\n"));
43cd52be 391 return (1);
392 }
98c2aef5 393
43cd52be 394 verbose --;
395 break;
98c2aef5 396
49de8bd2 397 case 'r' : /* Relaxed mode */
398 ppdSetConformance(PPD_CONFORM_RELAXED);
399 break;
400
401 case 'v' : /* Verbose mode */
43cd52be 402 if (verbose < 0)
403 {
89fd567e 404 _cupsLangPuts(stderr,
b7382443 405 _("cupstestppd: The -v option is incompatible "
406 "with the -q option.\n"));
43cd52be 407 return (1);
408 }
409
410 verbose ++;
411 break;
412
413 default :
414 usage();
415 break;
416 }
41b2c2e3 417 }
418 else
419 {
420 /*
421 * Open the PPD file...
422 */
423
e2c9ebc5 424 if (files && verbose >= 0)
89fd567e 425 _cupsLangPuts(stdout, "\n");
b3291c5a 426
79f448f9 427 files ++;
428
41b2c2e3 429 if (argv[i][0] == '-')
430 {
431 /*
432 * Read from stdin...
433 */
434
41b2c2e3 435 ppd = ppdOpen(stdin);
18d049b0 436
437 if (verbose >= 0)
438 printf("%s:", (ppd && ppd->pcfilename) ? ppd->pcfilename : "(stdin)");
41b2c2e3 439 }
440 else
441 {
442 /*
443 * Read from a file...
444 */
445
98c2aef5 446 if (verbose >= 0)
43cd52be 447 printf("%s:", argv[i]);
41b2c2e3 448
449 ppd = ppdOpenFile(argv[i]);
450 }
451
452 if (ppd == NULL)
453 {
79f448f9 454 error = ppdLastError(&line);
455
7a76892d 456 if (error <= PPD_ALLOC_ERROR)
41b2c2e3 457 {
458 status = ERROR_FILE_OPEN;
98c2aef5 459
b3291c5a 460 if (verbose >= 0)
89fd567e 461 _cupsLangPrintf(stdout,
b7382443 462 _(" FAIL\n"
463 " **FAIL** Unable to open PPD file - %s\n"),
464 strerror(errno));
41b2c2e3 465 }
466 else
467 {
468 status = ERROR_PPD_FORMAT;
98c2aef5 469
b3291c5a 470 if (verbose >= 0)
471 {
89fd567e 472 _cupsLangPrintf(stdout,
b7382443 473 _(" FAIL\n"
474 " **FAIL** Unable to open PPD file - "
475 "%s on line %d.\n"),
476 ppdErrorString(error), line);
b3291c5a 477
478 switch (error)
479 {
480 case PPD_MISSING_PPDADOBE4 :
89fd567e 481 _cupsLangPuts(stdout,
b7382443 482 _(" REF: Page 42, section 5.2.\n"));
b3291c5a 483 break;
484 case PPD_MISSING_VALUE :
89fd567e 485 _cupsLangPuts(stdout,
b7382443 486 _(" REF: Page 20, section 3.4.\n"));
b3291c5a 487 break;
488 case PPD_BAD_OPEN_GROUP :
489 case PPD_NESTED_OPEN_GROUP :
89fd567e 490 _cupsLangPuts(stdout,
b7382443 491 _(" REF: Pages 45-46, section 5.2.\n"));
b3291c5a 492 break;
493 case PPD_BAD_OPEN_UI :
494 case PPD_NESTED_OPEN_UI :
89fd567e 495 _cupsLangPuts(stdout,
b7382443 496 _(" REF: Pages 42-45, section 5.2.\n"));
b3291c5a 497 break;
498 case PPD_BAD_ORDER_DEPENDENCY :
89fd567e 499 _cupsLangPuts(stdout,
b7382443 500 _(" REF: Pages 48-49, section 5.2.\n"));
b3291c5a 501 break;
502 case PPD_BAD_UI_CONSTRAINTS :
89fd567e 503 _cupsLangPuts(stdout,
b7382443 504 _(" REF: Pages 52-54, section 5.2.\n"));
b3291c5a 505 break;
506 case PPD_MISSING_ASTERISK :
89fd567e 507 _cupsLangPuts(stdout,
b7382443 508 _(" REF: Page 15, section 3.2.\n"));
b3291c5a 509 break;
510 case PPD_LINE_TOO_LONG :
89fd567e 511 _cupsLangPuts(stdout,
b7382443 512 _(" REF: Page 15, section 3.1.\n"));
b3291c5a 513 break;
514 case PPD_ILLEGAL_CHARACTER :
89fd567e 515 _cupsLangPuts(stdout,
b7382443 516 _(" REF: Page 15, section 3.1.\n"));
b3291c5a 517 break;
518 case PPD_ILLEGAL_MAIN_KEYWORD :
89fd567e 519 _cupsLangPuts(stdout,
b7382443 520 _(" REF: Pages 16-17, section 3.2.\n"));
b3291c5a 521 break;
522 case PPD_ILLEGAL_OPTION_KEYWORD :
89fd567e 523 _cupsLangPuts(stdout,
b7382443 524 _(" REF: Page 19, section 3.3.\n"));
b3291c5a 525 break;
526 case PPD_ILLEGAL_TRANSLATION :
89fd567e 527 _cupsLangPuts(stdout,
b7382443 528 _(" REF: Page 27, section 3.5.\n"));
b3291c5a 529 break;
530 default :
531 break;
532 }
1a8f3056 533
534 check_basics(argv[i]);
b3291c5a 535 }
41b2c2e3 536 }
537
538 continue;
539 }
540
541 /*
542 * Show the header and then perform basic conformance tests (limited
543 * only by what the CUPS PPD functions actually load...)
544 */
545
708e57d5 546 errors = 0;
547 ppdversion = 43;
b3291c5a 548
549 if (verbose > 0)
89fd567e 550 _cupsLangPuts(stdout,
b7382443 551 _("\n DETAILED CONFORMANCE TEST RESULTS\n"));
b3291c5a 552
1b98ab76 553 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
554 attr->value)
5d1b511e 555 ppdversion = (int)(10 * _cupsStrScand(attr->value, NULL, loc) + 0.5);
708e57d5 556
12d95de0 557 for (j = 0; j < ppd->num_filters; j ++)
558 if (strstr(ppd->filters[j], "application/vnd.cups-raster"))
559 {
560 if (!test_raster(ppd, verbose))
561 errors ++;
562 break;
563 }
564
121f3ef8 565 /*
566 * Look for default keywords with no matching option...
567 */
568
d1fc9981 569 if (!(warn & WARN_DEFAULTS))
570 errors = check_defaults(ppd, errors, verbose, 0);
ceaef43a 571
f1ea144d 572 if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
43cd52be 573 {
b3291c5a 574 if (verbose >= 0)
575 {
576 if (!errors && !verbose)
89fd567e 577 _cupsLangPuts(stdout, _(" FAIL\n"));
43cd52be 578
89fd567e 579 _cupsLangPuts(stdout,
d1fc9981 580 _(" **FAIL** REQUIRED DefaultImageableArea\n"
b7382443 581 " REF: Page 102, section 5.15.\n"));
d1fc9981 582 }
b3291c5a 583
584 errors ++;
43cd52be 585 }
e76deeff 586 else if (ppdPageSize(ppd, attr->value) == NULL &&
d1fc9981 587 strcmp(attr->value, "Unknown"))
f1ea144d 588 {
589 if (verbose >= 0)
590 {
591 if (!errors && !verbose)
89fd567e 592 _cupsLangPuts(stdout, _(" FAIL\n"));
43cd52be 593
89fd567e 594 _cupsLangPrintf(stdout,
d1fc9981 595 _(" **FAIL** BAD DefaultImageableArea %s!\n"
b7382443 596 " REF: Page 102, section 5.15.\n"),
597 attr->value);
d1fc9981 598 }
f1ea144d 599
600 errors ++;
601 }
602 else
43cd52be 603 {
604 if (verbose > 0)
89fd567e 605 _cupsLangPuts(stdout, _(" PASS DefaultImageableArea\n"));
43cd52be 606 }
f1ea144d 607
b0c625f9 608 if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
43cd52be 609 {
b3291c5a 610 if (verbose >= 0)
611 {
612 if (!errors && !verbose)
89fd567e 613 _cupsLangPuts(stdout, _(" FAIL\n"));
43cd52be 614
89fd567e 615 _cupsLangPuts(stdout,
d1fc9981 616 _(" **FAIL** REQUIRED DefaultPaperDimension\n"
b7382443 617 " REF: Page 103, section 5.15.\n"));
d1fc9981 618 }
b3291c5a 619
620 errors ++;
43cd52be 621 }
e76deeff 622 else if (ppdPageSize(ppd, attr->value) == NULL &&
d1fc9981 623 strcmp(attr->value, "Unknown"))
f1ea144d 624 {
625 if (verbose >= 0)
626 {
627 if (!errors && !verbose)
89fd567e 628 _cupsLangPuts(stdout, _(" FAIL\n"));
f1ea144d 629
89fd567e 630 _cupsLangPrintf(stdout,
d1fc9981 631 _(" **FAIL** BAD DefaultPaperDimension %s!\n"
b7382443 632 " REF: Page 103, section 5.15.\n"),
633 attr->value);
d1fc9981 634 }
f1ea144d 635
636 errors ++;
637 }
638 else if (verbose > 0)
89fd567e 639 _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension\n"));
43cd52be 640
41b2c2e3 641 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
642 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
e7186d00 643 {
644 /*
645 * Verify that we have a default choice...
646 */
647
41b2c2e3 648 if (option->defchoice[0])
649 {
d1fc9981 650 if (ppdFindChoice(option, option->defchoice) == NULL &&
651 strcmp(option->defchoice, "Unknown"))
f1ea144d 652 {
653 if (verbose >= 0)
654 {
655 if (!errors && !verbose)
89fd567e 656 _cupsLangPuts(stdout, _(" FAIL\n"));
f1ea144d 657
89fd567e 658 _cupsLangPrintf(stdout,
d1fc9981 659 _(" **FAIL** BAD Default%s %s\n"
b7382443 660 " REF: Page 40, section 4.5.\n"),
661 option->keyword, option->defchoice);
d1fc9981 662 }
f1ea144d 663
664 errors ++;
665 }
666 else if (verbose > 0)
89fd567e 667 _cupsLangPrintf(stdout,
d1fc9981 668 _(" PASS Default%s\n"),
b7382443 669 option->keyword);
41b2c2e3 670 }
671 else
672 {
b3291c5a 673 if (verbose >= 0)
674 {
675 if (!errors && !verbose)
89fd567e 676 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 677
89fd567e 678 _cupsLangPrintf(stdout,
d1fc9981 679 _(" **FAIL** REQUIRED Default%s\n"
680 " REF: Page 40, section 4.5.\n"),
681 option->keyword);
682 }
b3291c5a 683
684 errors ++;
41b2c2e3 685 }
e7186d00 686 }
687
8cb0118e 688 if ((attr = ppdFindAttr(ppd, "FileVersion", NULL)) != NULL)
98c2aef5 689 {
8cb0118e 690 for (ptr = attr->value; *ptr; ptr ++)
691 if (!isdigit(*ptr & 255) && *ptr != '.')
692 break;
693
694 if (*ptr)
695 {
696 if (verbose >= 0)
697 {
698 if (!errors && !verbose)
699 _cupsLangPuts(stdout, _(" FAIL\n"));
700
701 _cupsLangPrintf(stdout,
702 _(" **FAIL** Bad FileVersion \"%s\"\n"
703 " REF: Page 56, section 5.3.\n"),
704 attr->value);
705 }
706
707 errors ++;
708 }
709 else if (verbose > 0)
89fd567e 710 _cupsLangPuts(stdout, _(" PASS FileVersion\n"));
98c2aef5 711 }
41b2c2e3 712 else
713 {
b3291c5a 714 if (verbose >= 0)
715 {
716 if (!errors && !verbose)
89fd567e 717 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 718
89fd567e 719 _cupsLangPuts(stdout,
b7382443 720 _(" **FAIL** REQUIRED FileVersion\n"
721 " REF: Page 56, section 5.3.\n"));
b3291c5a 722 }
723
724 errors ++;
43cd52be 725 }
726
8cb0118e 727 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL)
43cd52be 728 {
8cb0118e 729 ptr = attr->value;
730 if (*ptr == '4' && ptr[1] == '.')
731 {
732
733 for (ptr += 2; *ptr; ptr ++)
734 if (!isdigit(*ptr & 255))
735 break;
736 }
737
738 if (*ptr)
739 {
740 if (verbose >= 0)
741 {
742 if (!errors && !verbose)
743 _cupsLangPuts(stdout, _(" FAIL\n"));
744
745 _cupsLangPrintf(stdout,
746 _(" **FAIL** Bad FormatVersion \"%s\"\n"
747 " REF: Page 56, section 5.3.\n"),
748 attr->value);
749 }
750
751 errors ++;
752 }
753 else if (verbose > 0)
89fd567e 754 _cupsLangPuts(stdout, _(" PASS FormatVersion\n"));
43cd52be 755 }
756 else
757 {
b3291c5a 758 if (verbose >= 0)
759 {
760 if (!errors && !verbose)
89fd567e 761 _cupsLangPuts(stdout, _(" FAIL\n"));
43cd52be 762
89fd567e 763 _cupsLangPuts(stdout,
b7382443 764 _(" **FAIL** REQUIRED FormatVersion\n"
765 " REF: Page 56, section 5.3.\n"));
b3291c5a 766 }
767
768 errors ++;
41b2c2e3 769 }
770
771 if (ppd->lang_encoding != NULL)
98c2aef5 772 {
43cd52be 773 if (verbose > 0)
89fd567e 774 _cupsLangPuts(stdout, _(" PASS LanguageEncoding\n"));
98c2aef5 775 }
b3291c5a 776 else if (ppdversion > 40)
41b2c2e3 777 {
b3291c5a 778 if (verbose >= 0)
779 {
780 if (!errors && !verbose)
89fd567e 781 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 782
89fd567e 783 _cupsLangPuts(stdout,
b7382443 784 _(" **FAIL** REQUIRED LanguageEncoding\n"
785 " REF: Pages 56-57, section 5.3.\n"));
b3291c5a 786 }
787
788 errors ++;
41b2c2e3 789 }
790
791 if (ppd->lang_version != NULL)
98c2aef5 792 {
43cd52be 793 if (verbose > 0)
89fd567e 794 _cupsLangPuts(stdout, _(" PASS LanguageVersion\n"));
98c2aef5 795 }
41b2c2e3 796 else
797 {
b3291c5a 798 if (verbose >= 0)
799 {
800 if (!errors && !verbose)
89fd567e 801 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 802
89fd567e 803 _cupsLangPuts(stdout,
b7382443 804 _(" **FAIL** REQUIRED LanguageVersion\n"
805 " REF: Pages 57-58, section 5.3.\n"));
b3291c5a 806 }
807
808 errors ++;
41b2c2e3 809 }
810
811 if (ppd->manufacturer != NULL)
98c2aef5 812 {
d3d40607 813 if (!strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) ||
814 !strncasecmp(ppd->manufacturer, "Hewlett Packard", 15))
815 {
816 if (verbose >= 0)
817 {
818 if (!errors && !verbose)
89fd567e 819 _cupsLangPuts(stdout, _(" FAIL\n"));
d3d40607 820
89fd567e 821 _cupsLangPuts(stdout,
b7382443 822 _(" **FAIL** BAD Manufacturer (should be "
823 "\"HP\")\n"
824 " REF: Page 211, table D.1.\n"));
d3d40607 825 }
826
827 errors ++;
828 }
d1fc9981 829 else if (!strncasecmp(ppd->manufacturer, "OkiData", 7) ||
830 !strncasecmp(ppd->manufacturer, "Oki Data", 8))
831 {
832 if (verbose >= 0)
833 {
834 if (!errors && !verbose)
835 _cupsLangPuts(stdout, _(" FAIL\n"));
836
837 _cupsLangPuts(stdout,
838 _(" **FAIL** BAD Manufacturer (should be "
839 "\"Oki\")\n"
840 " REF: Page 211, table D.1.\n"));
841 }
842
843 errors ++;
844 }
d3d40607 845 else if (verbose > 0)
89fd567e 846 _cupsLangPuts(stdout, _(" PASS Manufacturer\n"));
98c2aef5 847 }
b3291c5a 848 else if (ppdversion >= 43)
41b2c2e3 849 {
b3291c5a 850 if (verbose >= 0)
851 {
852 if (!errors && !verbose)
89fd567e 853 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 854
89fd567e 855 _cupsLangPuts(stdout,
b7382443 856 _(" **FAIL** REQUIRED Manufacturer\n"
857 " REF: Pages 58-59, section 5.3.\n"));
b3291c5a 858 }
859
860 errors ++;
41b2c2e3 861 }
862
863 if (ppd->modelname != NULL)
98c2aef5 864 {
43cd52be 865 for (ptr = ppd->modelname; *ptr; ptr ++)
da275f55 866 if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr))
43cd52be 867 break;
868
869 if (*ptr)
870 {
b3291c5a 871 if (verbose >= 0)
872 {
873 if (!errors && !verbose)
89fd567e 874 _cupsLangPuts(stdout, _(" FAIL\n"));
43cd52be 875
89fd567e 876 _cupsLangPrintf(stdout,
b7382443 877 _(" **FAIL** BAD ModelName - \"%c\" not "
878 "allowed in string.\n"
879 " REF: Pages 59-60, section 5.3.\n"),
880 *ptr);
b3291c5a 881 }
882
883 errors ++;
43cd52be 884 }
885 else if (verbose > 0)
89fd567e 886 _cupsLangPuts(stdout, _(" PASS ModelName\n"));
98c2aef5 887 }
41b2c2e3 888 else
889 {
b3291c5a 890 if (verbose >= 0)
891 {
892 if (!errors && !verbose)
89fd567e 893 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 894
89fd567e 895 _cupsLangPuts(stdout,
b7382443 896 _(" **FAIL** REQUIRED ModelName\n"
897 " REF: Pages 59-60, section 5.3.\n"));
b3291c5a 898 }
899
900 errors ++;
41b2c2e3 901 }
902
903 if (ppd->nickname != NULL)
98c2aef5 904 {
43cd52be 905 if (verbose > 0)
89fd567e 906 _cupsLangPuts(stdout, _(" PASS NickName\n"));
98c2aef5 907 }
41b2c2e3 908 else
909 {
b3291c5a 910 if (verbose >= 0)
911 {
912 if (!errors && !verbose)
89fd567e 913 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 914
89fd567e 915 _cupsLangPuts(stdout,
b7382443 916 _(" **FAIL** REQUIRED NickName\n"
917 " REF: Page 60, section 5.3.\n"));
b3291c5a 918 }
919
920 errors ++;
41b2c2e3 921 }
922
923 if (ppdFindOption(ppd, "PageSize") != NULL)
98c2aef5 924 {
43cd52be 925 if (verbose > 0)
89fd567e 926 _cupsLangPuts(stdout, _(" PASS PageSize\n"));
98c2aef5 927 }
41b2c2e3 928 else
929 {
b3291c5a 930 if (verbose >= 0)
931 {
932 if (!errors && !verbose)
89fd567e 933 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 934
89fd567e 935 _cupsLangPuts(stdout,
b7382443 936 _(" **FAIL** REQUIRED PageSize\n"
937 " REF: Pages 99-100, section 5.14.\n"));
b3291c5a 938 }
939
940 errors ++;
41b2c2e3 941 }
942
943 if (ppdFindOption(ppd, "PageRegion") != NULL)
98c2aef5 944 {
43cd52be 945 if (verbose > 0)
89fd567e 946 _cupsLangPuts(stdout, _(" PASS PageRegion\n"));
98c2aef5 947 }
41b2c2e3 948 else
949 {
b3291c5a 950 if (verbose >= 0)
951 {
952 if (!errors && !verbose)
89fd567e 953 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 954
89fd567e 955 _cupsLangPuts(stdout,
b7382443 956 _(" **FAIL** REQUIRED PageRegion\n"
957 " REF: Page 100, section 5.14.\n"));
b3291c5a 958 }
959
960 errors ++;
41b2c2e3 961 }
962
1a32c4fe 963 if (ppd->pcfilename != NULL)
964 {
43cd52be 965 if (verbose > 0)
89fd567e 966 _cupsLangPuts(stdout, _(" PASS PCFileName\n"));
1a32c4fe 967 }
968 else
969 {
b3291c5a 970 if (verbose >= 0)
971 {
972 if (!errors && !verbose)
89fd567e 973 _cupsLangPuts(stdout, _(" FAIL\n"));
1a32c4fe 974
89fd567e 975 _cupsLangPuts(stdout,
b7382443 976 _(" **FAIL** REQUIRED PCFileName\n"
977 " REF: Pages 61-62, section 5.3.\n"));
b3291c5a 978 }
979
980 errors ++;
1a32c4fe 981 }
982
41b2c2e3 983 if (ppd->product != NULL)
98c2aef5 984 {
43cd52be 985 if (ppd->product[0] != '(' ||
986 ppd->product[strlen(ppd->product) - 1] != ')')
987 {
b3291c5a 988 if (verbose >= 0)
989 {
990 if (!errors && !verbose)
89fd567e 991 _cupsLangPuts(stdout, _(" FAIL\n"));
b3291c5a 992
89fd567e 993 _cupsLangPuts(stdout,
b7382443 994 _(" **FAIL** BAD Product - not \"(string)\".\n"
995 " REF: Page 62, section 5.3.\n"));
b3291c5a 996 }
43cd52be 997
b3291c5a 998 errors ++;
43cd52be 999 }
1000 else if (verbose > 0)
89fd567e 1001 _cupsLangPuts(stdout, _(" PASS Product\n"));
98c2aef5 1002 }
41b2c2e3 1003 else
1004 {
b3291c5a 1005 if (verbose >= 0)
1006 {
1007 if (!errors && !verbose)
89fd567e 1008 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 1009
89fd567e 1010 _cupsLangPuts(stdout,
b7382443 1011 _(" **FAIL** REQUIRED Product\n"
1012 " REF: Page 62, section 5.3.\n"));
b3291c5a 1013 }
1014
1015 errors ++;
41b2c2e3 1016 }
1017
1b98ab76 1018 if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
1019 attr->value != NULL)
43cd52be 1020 {
1021 char junkstr[255]; /* Temp string */
1022 int junkint; /* Temp integer */
1023
1024
1b98ab76 1025 if (sscanf(attr->value, "(%[^)])%d", junkstr, &junkint) != 2)
43cd52be 1026 {
b3291c5a 1027 if (verbose >= 0)
1028 {
1029 if (!errors && !verbose)
89fd567e 1030 _cupsLangPuts(stdout, _(" FAIL\n"));
b3291c5a 1031
89fd567e 1032 _cupsLangPuts(stdout,
b7382443 1033 _(" **FAIL** BAD PSVersion - not \"(string) "
1034 "int\".\n"
1035 " REF: Pages 62-64, section 5.3.\n"));
b3291c5a 1036 }
43cd52be 1037
b3291c5a 1038 errors ++;
43cd52be 1039 }
1040 else if (verbose > 0)
89fd567e 1041 _cupsLangPuts(stdout, _(" PASS PSVersion\n"));
43cd52be 1042 }
1043 else
1044 {
b3291c5a 1045 if (verbose >= 0)
1046 {
1047 if (!errors && !verbose)
89fd567e 1048 _cupsLangPuts(stdout, _(" FAIL\n"));
43cd52be 1049
89fd567e 1050 _cupsLangPuts(stdout,
b7382443 1051 _(" **FAIL** REQUIRED PSVersion\n"
1052 " REF: Pages 62-64, section 5.3.\n"));
b3291c5a 1053 }
1054
1055 errors ++;
43cd52be 1056 }
1057
41b2c2e3 1058 if (ppd->shortnickname != NULL)
98c2aef5 1059 {
43cd52be 1060 if (strlen(ppd->shortnickname) > 31)
1061 {
b3291c5a 1062 if (verbose >= 0)
1063 {
1064 if (!errors && !verbose)
89fd567e 1065 _cupsLangPuts(stdout, _(" FAIL\n"));
b3291c5a 1066
89fd567e 1067 _cupsLangPuts(stdout,
b7382443 1068 _(" **FAIL** BAD ShortNickName - longer "
1069 "than 31 chars.\n"
1070 " REF: Pages 64-65, section 5.3.\n"));
b3291c5a 1071 }
43cd52be 1072
b3291c5a 1073 errors ++;
43cd52be 1074 }
1075 else if (verbose > 0)
89fd567e 1076 _cupsLangPuts(stdout, _(" PASS ShortNickName\n"));
98c2aef5 1077 }
b3291c5a 1078 else if (ppdversion >= 43)
708e57d5 1079 {
b3291c5a 1080 if (verbose >= 0)
1081 {
1082 if (!errors && !verbose)
89fd567e 1083 _cupsLangPuts(stdout, _(" FAIL\n"));
98c2aef5 1084
89fd567e 1085 _cupsLangPuts(stdout,
b7382443 1086 _(" **FAIL** REQUIRED ShortNickName\n"
1087 " REF: Page 64-65, section 5.3.\n"));
b3291c5a 1088 }
1089
1090 errors ++;
41b2c2e3 1091 }
1092
d3d40607 1093 if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
1094 strstr(ppd->patches, "*End"))
1095 {
1096 if (verbose >= 0)
1097 {
1098 if (!errors && !verbose)
89fd567e 1099 _cupsLangPuts(stdout, _(" FAIL\n"));
d3d40607 1100
89fd567e 1101 _cupsLangPuts(stdout,
b7382443 1102 _(" **FAIL** BAD JobPatchFile attribute in file\n"
1103 " REF: Page 24, section 3.4.\n"));
d3d40607 1104 }
1105
1106 errors ++;
1107 }
1108
d68320d4 1109 /*
1110 * Check for page sizes without the corresponding ImageableArea or
1111 * PaperDimension values...
1112 */
1113
1114 if (ppd->num_sizes == 0)
1115 {
1116 if (verbose >= 0)
1117 {
1118 if (!errors && !verbose)
89fd567e 1119 _cupsLangPuts(stdout, _(" FAIL\n"));
d68320d4 1120
89fd567e 1121 _cupsLangPuts(stdout,
b7382443 1122 _(" **FAIL** REQUIRED PageSize\n"
1123 " REF: Page 41, section 5.\n"
1124 " REF: Page 99, section 5.14.\n"));
d68320d4 1125 }
1126
1127 errors ++;
1128 }
1129 else
1130 {
1131 for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++)
1132 {
1133 /*
1134 * Don't check custom size...
1135 */
1136
1137 if (!strcmp(size->name, "Custom"))
1138 continue;
1139
1140 /*
1141 * Check for ImageableArea...
1142 */
1143
1144 if (size->left == 0.0 && size->bottom == 0.0 &&
1145 size->right == 0.0 && size->top == 0.0)
1146 {
1147 if (verbose >= 0)
1148 {
1149 if (!errors && !verbose)
89fd567e 1150 _cupsLangPuts(stdout, _(" FAIL\n"));
b7382443 1151
89fd567e 1152 _cupsLangPrintf(stdout,
b7382443 1153 _(" **FAIL** REQUIRED ImageableArea for "
1154 "PageSize %s\n"
1155 " REF: Page 41, section 5.\n"
1156 " REF: Page 102, section 5.15.\n"),
1157 size->name);
d68320d4 1158 }
1159
1160 errors ++;
1161 }
1162
1163 /*
1164 * Check for PaperDimension...
1165 */
1166
1167 if (size->width == 0.0 && size->length == 0.0)
1168 {
1169 if (verbose >= 0)
1170 {
1171 if (!errors && !verbose)
89fd567e 1172 _cupsLangPuts(stdout, _(" FAIL\n"));
b7382443 1173
89fd567e 1174 _cupsLangPrintf(stdout,
b7382443 1175 _(" **FAIL** REQUIRED PaperDimension "
1176 "for PageSize %s\n"
1177 " REF: Page 41, section 5.\n"
1178 " REF: Page 103, section 5.15.\n"),
1179 size->name);
d68320d4 1180 }
1181
1182 errors ++;
1183 }
1184 }
1185 }
1186
4fb9a5bd 1187 /*
1188 * Check for valid Resolution, JCLResolution, or SetResolution values...
1189 */
1190
1191 if ((option = ppdFindOption(ppd, "Resolution")) == NULL)
1192 if ((option = ppdFindOption(ppd, "JCLResolution")) == NULL)
1193 option = ppdFindOption(ppd, "SetResolution");
1194
1195 if (option != NULL)
1196 {
1197 for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
1198 {
1199 /*
1200 * Verify that all resolution options are of the form NNNdpi
1201 * or NNNxNNNdpi...
1202 */
1203
1204 xdpi = strtol(choice->choice, (char **)&ptr, 10);
1205 if (ptr > choice->choice && xdpi > 0)
1206 {
1207 if (*ptr == 'x')
1208 ydpi = strtol(ptr + 1, (char **)&ptr, 10);
1209 else
1210 ydpi = xdpi;
1211 }
e6e44566 1212 else
1213 ydpi = xdpi;
4fb9a5bd 1214
6cacb255 1215 if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 ||
1216 strcmp(ptr, "dpi"))
4fb9a5bd 1217 {
1218 if (verbose >= 0)
1219 {
1220 if (!errors && !verbose)
89fd567e 1221 _cupsLangPuts(stdout, _(" FAIL\n"));
4fb9a5bd 1222
89fd567e 1223 _cupsLangPrintf(stdout,
b7382443 1224 _(" **FAIL** Bad %s choice %s!\n"
1225 " REF: Page 84, section 5.9\n"),
1226 option->keyword, choice->choice);
4fb9a5bd 1227 }
1228
1229 errors ++;
1230 }
1231 }
1232 }
1233
d1fc9981 1234 if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
1235 strcmp(attr->name, "1284DeviceID"))
73aa2ce1 1236 {
d1fc9981 1237 if (verbose >= 0)
73aa2ce1 1238 {
d1fc9981 1239 if (!errors && !verbose)
1240 _cupsLangPuts(stdout, _(" FAIL\n"));
1241
1242 _cupsLangPrintf(stdout,
1243 _(" **FAIL** %s must be 1284DeviceID!\n"
1244 " REF: Page 72, section 5.5\n"),
1245 attr->name);
1246 }
73aa2ce1 1247
d1fc9981 1248 errors ++;
1249 }
73aa2ce1 1250
48c9a702 1251 errors = check_case(ppd, errors, verbose);
1252
d1fc9981 1253 if (!(warn & WARN_CONSTRAINTS))
1254 errors = check_constraints(ppd, errors, verbose, 0);
73aa2ce1 1255
d1fc9981 1256 if (!(warn & WARN_FILTERS))
1257 errors = check_filters(ppd, root, errors, verbose, 0);
73aa2ce1 1258
c9be2428 1259 if (!(warn & WARN_PROFILES))
1260 errors = check_profiles(ppd, root, errors, verbose, 0);
1261
19b7f3b7 1262 if (!(warn & WARN_SIZES))
1263 errors = check_sizes(ppd, errors, verbose, 0);
1264
d1fc9981 1265 if (!(warn & WARN_TRANSLATIONS))
1266 errors = check_translations(ppd, errors, verbose, 0);
73aa2ce1 1267
48c9a702 1268 if (!(warn & WARN_DUPLEX))
1269 errors = check_duplex(ppd, errors, verbose, 0);
1270
d1fc9981 1271 if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
1272 attr->value)
1273 {
1274 /*
1275 * This file contains localizations, check for conformance of the
1276 * base translation...
1277 */
73aa2ce1 1278
d1fc9981 1279 if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL)
1280 {
1281 if (!attr->value || strcmp(attr->value, "ISOLatin1"))
73aa2ce1 1282 {
d1fc9981 1283 if (!errors && !verbose)
1284 _cupsLangPuts(stdout, _(" FAIL\n"));
73aa2ce1 1285
d1fc9981 1286 if (verbose >= 0)
3315c7a1 1287 _cupsLangPrintf(stdout,
d1fc9981 1288 _(" **FAIL** Bad LanguageEncoding %s - "
1289 "must be ISOLatin1!\n"),
1290 attr->value ? attr->value : "(null)");
73aa2ce1 1291
d1fc9981 1292 errors ++;
73aa2ce1 1293 }
1294
d1fc9981 1295 if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
1296 {
1297 if (!errors && !verbose)
1298 _cupsLangPuts(stdout, _(" FAIL\n"));
c83c77d1 1299
d1fc9981 1300 if (verbose >= 0)
3315c7a1 1301 _cupsLangPrintf(stdout,
d1fc9981 1302 _(" **FAIL** Bad LanguageVersion %s - "
1303 "must be English!\n"),
1304 ppd->lang_version ? ppd->lang_version : "(null)");
7f486b29 1305
d1fc9981 1306 errors ++;
1307 }
1308
1309 /*
73aa2ce1 1310 * Loop through all options and choices...
1311 */
1312
d1fc9981 1313 for (option = ppdFirstOption(ppd);
73aa2ce1 1314 option;
1315 option = ppdNextOption(ppd))
1316 {
d1fc9981 1317 /*
1318 * Check for special characters outside A0 to BF, F7, or F8
1319 * that are used for languages other than English.
1320 */
73aa2ce1 1321
d1fc9981 1322 for (ptr = option->text; *ptr; ptr ++)
1323 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1324 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1325 break;
73aa2ce1 1326
d1fc9981 1327 if (*ptr)
8b056e32 1328 {
d1fc9981 1329 if (!errors && !verbose)
1330 _cupsLangPuts(stdout, _(" FAIL\n"));
73aa2ce1 1331
73aa2ce1 1332 if (verbose >= 0)
73aa2ce1 1333 _cupsLangPrintf(stdout,
d1fc9981 1334 _(" **FAIL** Default translation "
73aa2ce1 1335 "string for option %s contains 8-bit "
1336 "characters!\n"),
1337 option->keyword);
73aa2ce1 1338
1339 errors ++;
1340 }
1341
d1fc9981 1342 for (j = 0; j < option->num_choices; j ++)
73aa2ce1 1343 {
d1fc9981 1344 /*
1345 * Check for special characters outside A0 to BF, F7, or F8
1346 * that are used for languages other than English.
1347 */
73aa2ce1 1348
d1fc9981 1349 for (ptr = option->choices[j].text; *ptr; ptr ++)
1350 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1351 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1352 break;
73aa2ce1 1353
d1fc9981 1354 if (*ptr)
8b056e32 1355 {
d1fc9981 1356 if (!errors && !verbose)
1357 _cupsLangPuts(stdout, _(" FAIL\n"));
73aa2ce1 1358
73aa2ce1 1359 if (verbose >= 0)
73aa2ce1 1360 _cupsLangPrintf(stdout,
d1fc9981 1361 _(" **FAIL** Default translation "
73aa2ce1 1362 "string for option %s choice %s contains "
1363 "8-bit characters!\n"),
1364 option->keyword,
1365 option->choices[j].choice);
73aa2ce1 1366
1367 errors ++;
1368 }
d1fc9981 1369 }
73aa2ce1 1370 }
d1fc9981 1371 }
1372 }
c83c77d1 1373
d1fc9981 1374 /*
1375 * Final pass/fail notification...
1376 */
c83c77d1 1377
d1fc9981 1378 if (errors)
1379 status = ERROR_CONFORMANCE;
1380 else if (!verbose)
1381 _cupsLangPuts(stdout, _(" PASS\n"));
c83c77d1 1382
d1fc9981 1383 if (verbose >= 0)
1384 {
1385 check_basics(argv[i]);
c83c77d1 1386
d1fc9981 1387 if (warn & WARN_CONSTRAINTS)
1388 errors = check_constraints(ppd, errors, verbose, 1);
c83c77d1 1389
d1fc9981 1390 if (warn & WARN_DEFAULTS)
1391 errors = check_defaults(ppd, errors, verbose, 1);
c83c77d1 1392
c9be2428 1393 if (warn & WARN_PROFILES)
1394 errors = check_profiles(ppd, root, errors, verbose, 1);
1395
d1fc9981 1396 if (warn & WARN_FILTERS)
1397 errors = check_filters(ppd, root, errors, verbose, 1);
c83c77d1 1398
19b7f3b7 1399 if (warn & WARN_SIZES)
1400 errors = check_sizes(ppd, errors, verbose, 1);
1401 else
1402 errors = check_sizes(ppd, errors, verbose, 2);
1403
d1fc9981 1404 if (warn & WARN_TRANSLATIONS)
1405 errors = check_translations(ppd, errors, verbose, 1);
c83c77d1 1406
48c9a702 1407 if (warn & WARN_DUPLEX)
1408 errors = check_duplex(ppd, errors, verbose, 1);
1409
1410 /*
1411 * Look for legacy duplex keywords...
1412 */
1413
1414 if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
1415 if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
1416 option = ppdFindOption(ppd, "KD03Duplex");
1417
1418 if (option)
1419 _cupsLangPrintf(stdout,
1420 _(" WARN Duplex option keyword %s may not "
1421 "work as expected and should be named Duplex!\n"
1422 " REF: Page 122, section 5.17\n"),
1423 option->keyword);
1424
c83c77d1 1425 /*
d1fc9981 1426 * Look for default keywords with no corresponding option...
c83c77d1 1427 */
1428
d1fc9981 1429 for (j = 0; j < ppd->num_attrs; j ++)
1430 {
1431 attr = ppd->attrs[j];
73aa2ce1 1432
d1fc9981 1433 if (!strcmp(attr->name, "DefaultColorSpace") ||
1434 !strcmp(attr->name, "DefaultColorSep") ||
1435 !strcmp(attr->name, "DefaultFont") ||
12d95de0 1436 !strcmp(attr->name, "DefaultHalftoneType") ||
d1fc9981 1437 !strcmp(attr->name, "DefaultImageableArea") ||
12d95de0 1438 !strcmp(attr->name, "DefaultLeadingEdge") ||
d1fc9981 1439 !strcmp(attr->name, "DefaultOutputOrder") ||
1440 !strcmp(attr->name, "DefaultPaperDimension") ||
12d95de0 1441 !strcmp(attr->name, "DefaultResolution") ||
d1fc9981 1442 !strcmp(attr->name, "DefaultScreenProc") ||
1443 !strcmp(attr->name, "DefaultTransfer"))
1444 continue;
73aa2ce1 1445
d1fc9981 1446 if (!strncmp(attr->name, "Default", 7) &&
1447 !ppdFindOption(ppd, attr->name + 7))
1448 _cupsLangPrintf(stdout,
1449 _(" WARN %s has no corresponding "
1450 "options!\n"),
1451 attr->name);
754653e0 1452 }
1453
36aba8cf 1454 ppdMarkDefaults(ppd);
1455 if (ppdConflicts(ppd))
ea13a0f9 1456 {
89fd567e 1457 _cupsLangPuts(stdout,
b7382443 1458 _(" WARN Default choices conflicting!\n"));
36aba8cf 1459
ea13a0f9 1460 show_conflicts(ppd);
1461 }
1462
b3291c5a 1463 if (ppdversion < 43)
1464 {
89fd567e 1465 _cupsLangPrintf(stdout,
b7382443 1466 _(" WARN Obsolete PPD version %.1f!\n"
1467 " REF: Page 42, section 5.2.\n"),
1468 0.1f * ppdversion);
b3291c5a 1469 }
98c2aef5 1470
c0cfd579 1471 if (!ppd->lang_encoding && ppdversion < 41)
b3291c5a 1472 {
89fd567e 1473 _cupsLangPuts(stdout,
b7382443 1474 _(" WARN LanguageEncoding required by PPD "
1475 "4.3 spec.\n"
1476 " REF: Pages 56-57, section 5.3.\n"));
b3291c5a 1477 }
1478
1479 if (!ppd->manufacturer && ppdversion < 43)
1480 {
89fd567e 1481 _cupsLangPuts(stdout,
b7382443 1482 _(" WARN Manufacturer required by PPD "
1483 "4.3 spec.\n"
1484 " REF: Pages 58-59, section 5.3.\n"));
b3291c5a 1485 }
1486
1487 /*
1488 * Treat a PCFileName attribute longer than 12 characters as
1489 * a warning and not a hard error...
1490 */
1491
1492 if (ppd->pcfilename && strlen(ppd->pcfilename) > 12)
1493 {
89fd567e 1494 _cupsLangPuts(stdout,
b7382443 1495 _(" WARN PCFileName longer than 8.3 in "
1496 "violation of PPD spec.\n"
1497 " REF: Pages 61-62, section 5.3.\n"));
b3291c5a 1498 }
1499
1500 if (!ppd->shortnickname && ppdversion < 43)
1501 {
89fd567e 1502 _cupsLangPuts(stdout,
b7382443 1503 _(" WARN ShortNickName required by PPD "
1504 "4.3 spec.\n"
1505 " REF: Pages 64-65, section 5.3.\n"));
b3291c5a 1506 }
54e5ec55 1507
58d47694 1508 /*
1509 * Check the Protocols line and flag PJL + BCP since TBCP is
1510 * usually used with PJL...
1511 */
1512
1513 if (ppd->protocols)
1514 {
1515 if (strstr(ppd->protocols, "PJL") &&
1516 strstr(ppd->protocols, "BCP") &&
1517 !strstr(ppd->protocols, "TBCP"))
1518 {
89fd567e 1519 _cupsLangPuts(stdout,
b7382443 1520 _(" WARN Protocols contains both PJL "
1521 "and BCP; expected TBCP.\n"
1522 " REF: Pages 78-79, section 5.7.\n"));
58d47694 1523 }
1524
1525 if (strstr(ppd->protocols, "PJL") &&
1526 (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
1527 {
89fd567e 1528 _cupsLangPuts(stdout,
b7382443 1529 _(" WARN Protocols contains PJL but JCL "
1530 "attributes are not set.\n"
1531 " REF: Pages 78-79, section 5.7.\n"));
58d47694 1532 }
1533 }
1534
54e5ec55 1535 /*
1536 * Check for options with a common prefix, e.g. Duplex and Duplexer,
1537 * which are errors according to the spec but won't cause problems
1538 * with CUPS specifically...
1539 */
1540
1541 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1542 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1543 {
1544 len = strlen(option->keyword);
1545
1546 for (m = 0, group2 = ppd->groups;
1547 m < ppd->num_groups;
1548 m ++, group2 ++)
1549 for (n = 0, option2 = group2->options;
1550 n < group2->num_options;
1551 n ++, option2 ++)
1552 if (option != option2 &&
1553 len < strlen(option2->keyword) &&
1554 !strncmp(option->keyword, option2->keyword, len))
1555 {
89fd567e 1556 _cupsLangPrintf(stdout,
b7382443 1557 _(" WARN %s shares a common "
1558 "prefix with %s\n"
1559 " REF: Page 15, section "
1560 "3.2.\n"),
1561 option->keyword, option2->keyword);
54e5ec55 1562 }
1563 }
41b2c2e3 1564 }
1565
43cd52be 1566 if (verbose > 0)
b3291c5a 1567 {
1568 if (errors)
d1e61ff2 1569 _cupsLangPrintf(stdout, _(" %d ERRORS FOUND\n"), errors);
b3291c5a 1570 else
89fd567e 1571 _cupsLangPuts(stdout, _(" NO ERRORS FOUND\n"));
b3291c5a 1572 }
1573
41b2c2e3 1574 /*
1575 * Then list the options, if "-v" was provided...
1576 */
1577
43cd52be 1578 if (verbose > 1)
41b2c2e3 1579 {
89fd567e 1580 _cupsLangPrintf(stdout,
b7382443 1581 "\n"
1582 " language_level = %d\n"
1583 " color_device = %s\n"
1584 " variable_sizes = %s\n"
1585 " landscape = %d\n",
1586 ppd->language_level,
1587 ppd->color_device ? "TRUE" : "FALSE",
1588 ppd->variable_sizes ? "TRUE" : "FALSE",
1589 ppd->landscape);
41b2c2e3 1590
1591 switch (ppd->colorspace)
1592 {
1593 case PPD_CS_CMYK :
89fd567e 1594 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK\n");
41b2c2e3 1595 break;
1596 case PPD_CS_CMY :
89fd567e 1597 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY\n");
41b2c2e3 1598 break;
1599 case PPD_CS_GRAY :
89fd567e 1600 _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY\n");
41b2c2e3 1601 break;
1602 case PPD_CS_RGB :
89fd567e 1603 _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB\n");
41b2c2e3 1604 break;
1605 default :
89fd567e 1606 _cupsLangPuts(stdout, " colorspace = <unknown>\n");
41b2c2e3 1607 break;
1608 }
1609
89fd567e 1610 _cupsLangPrintf(stdout, " num_emulations = %d\n",
b7382443 1611 ppd->num_emulations);
41b2c2e3 1612 for (j = 0; j < ppd->num_emulations; j ++)
89fd567e 1613 _cupsLangPrintf(stdout, " emulations[%d] = %s\n",
b7382443 1614 j, ppd->emulations[j].name);
1615
89fd567e 1616 _cupsLangPrintf(stdout, " lang_encoding = %s\n",
b7382443 1617 ppd->lang_encoding);
89fd567e 1618 _cupsLangPrintf(stdout, " lang_version = %s\n",
b7382443 1619 ppd->lang_version);
89fd567e 1620 _cupsLangPrintf(stdout, " modelname = %s\n", ppd->modelname);
1621 _cupsLangPrintf(stdout, " ttrasterizer = %s\n",
b7382443 1622 ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
89fd567e 1623 _cupsLangPrintf(stdout, " manufacturer = %s\n",
b7382443 1624 ppd->manufacturer);
89fd567e 1625 _cupsLangPrintf(stdout, " product = %s\n", ppd->product);
1626 _cupsLangPrintf(stdout, " nickname = %s\n", ppd->nickname);
1627 _cupsLangPrintf(stdout, " shortnickname = %s\n",
b7382443 1628 ppd->shortnickname);
89fd567e 1629 _cupsLangPrintf(stdout, " patches = %d bytes\n",
b7382443 1630 ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
1631
89fd567e 1632 _cupsLangPrintf(stdout, " num_groups = %d\n", ppd->num_groups);
41b2c2e3 1633 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1634 {
89fd567e 1635 _cupsLangPrintf(stdout, " group[%d] = %s\n",
b7382443 1636 j, group->text);
41b2c2e3 1637
1638 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1639 {
89fd567e 1640 _cupsLangPrintf(stdout,
b7382443 1641 " options[%d] = %s (%s) %s %s %.0f "
1642 "(%d choices)\n",
1643 k, option->keyword, option->text, uis[option->ui],
1644 sections[option->section], option->order,
1645 option->num_choices);
1646
1647 if (!strcmp(option->keyword, "PageSize") ||
1648 !strcmp(option->keyword, "PageRegion"))
41b2c2e3 1649 {
1650 for (m = option->num_choices, choice = option->choices;
1651 m > 0;
1652 m --, choice ++)
1653 {
1654 size = ppdPageSize(ppd, choice->choice);
1655
1656 if (size == NULL)
89fd567e 1657 _cupsLangPrintf(stdout,
b7382443 1658 " %s (%s) = ERROR",
1659 choice->choice, choice->text);
41b2c2e3 1660 else
89fd567e 1661 _cupsLangPrintf(stdout,
b7382443 1662 " %s (%s) = %.2fx%.2fin "
1663 "(%.1f,%.1f,%.1f,%.1f)",
1664 choice->choice, choice->text,
1665 size->width / 72.0, size->length / 72.0,
1666 size->left / 72.0, size->bottom / 72.0,
1667 size->right / 72.0, size->top / 72.0);
1668
1669 if (!strcmp(option->defchoice, choice->choice))
89fd567e 1670 _cupsLangPuts(stdout, " *\n");
41b2c2e3 1671 else
89fd567e 1672 _cupsLangPuts(stdout, "\n");
41b2c2e3 1673 }
1674 }
1675 else
1676 {
1677 for (m = option->num_choices, choice = option->choices;
1678 m > 0;
1679 m --, choice ++)
1680 {
89fd567e 1681 _cupsLangPrintf(stdout, " %s (%s)",
b7382443 1682 choice->choice, choice->text);
41b2c2e3 1683
b7382443 1684 if (!strcmp(option->defchoice, choice->choice))
89fd567e 1685 _cupsLangPuts(stdout, " *\n");
41b2c2e3 1686 else
89fd567e 1687 _cupsLangPuts(stdout, "\n");
41b2c2e3 1688 }
1689 }
1690 }
1691 }
1692
12d95de0 1693 _cupsLangPrintf(stdout, " num_consts = %d\n",
1694 ppd->num_consts);
1695 for (j = 0; j < ppd->num_consts; j ++)
1696 _cupsLangPrintf(stdout,
1697 " consts[%d] = *%s %s *%s %s\n",
1698 j, ppd->consts[j].option1, ppd->consts[j].choice1,
1699 ppd->consts[j].option2, ppd->consts[j].choice2);
1700
89fd567e 1701 _cupsLangPrintf(stdout, " num_profiles = %d\n",
b7382443 1702 ppd->num_profiles);
41b2c2e3 1703 for (j = 0; j < ppd->num_profiles; j ++)
89fd567e 1704 _cupsLangPrintf(stdout,
b7382443 1705 " profiles[%d] = %s/%s %.3f %.3f "
1706 "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
1707 j, ppd->profiles[j].resolution,
1708 ppd->profiles[j].media_type,
1709 ppd->profiles[j].gamma, ppd->profiles[j].density,
1710 ppd->profiles[j].matrix[0][0],
1711 ppd->profiles[j].matrix[0][1],
1712 ppd->profiles[j].matrix[0][2],
1713 ppd->profiles[j].matrix[1][0],
1714 ppd->profiles[j].matrix[1][1],
1715 ppd->profiles[j].matrix[1][2],
1716 ppd->profiles[j].matrix[2][0],
1717 ppd->profiles[j].matrix[2][1],
1718 ppd->profiles[j].matrix[2][2]);
1719
89fd567e 1720 _cupsLangPrintf(stdout, " num_fonts = %d\n", ppd->num_fonts);
41b2c2e3 1721 for (j = 0; j < ppd->num_fonts; j ++)
89fd567e 1722 _cupsLangPrintf(stdout, " fonts[%d] = %s\n",
b7382443 1723 j, ppd->fonts[j]);
af50faa7 1724
89fd567e 1725 _cupsLangPrintf(stdout, " num_attrs = %d\n", ppd->num_attrs);
af50faa7 1726 for (j = 0; j < ppd->num_attrs; j ++)
89fd567e 1727 _cupsLangPrintf(stdout,
b7382443 1728 " attrs[%d] = %s %s%s%s: \"%s\"\n", j,
1729 ppd->attrs[j]->name, ppd->attrs[j]->spec,
1730 ppd->attrs[j]->text[0] ? "/" : "",
1731 ppd->attrs[j]->text,
1732 ppd->attrs[j]->value ?
1733 ppd->attrs[j]->value : "(null)");
41b2c2e3 1734 }
1735
1736 ppdClose(ppd);
1737 }
1738
43cd52be 1739 if (!files)
1740 usage();
1741
1742 return (status);
1743}
1744
1745
1a8f3056 1746/*
1747 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1748 */
1749
0dcff045 1750static void
1a8f3056 1751check_basics(const char *filename) /* I - PPD file to check */
1752{
1753 cups_file_t *fp; /* File pointer */
1754 int ch; /* Current character */
1755 int col, /* Current column */
1756 whitespace; /* Only seen whitespace? */
1757 int eol; /* Line endings */
1758 int linenum; /* Line number */
1759 int mixed; /* Mixed line endings? */
1760
1761
1762 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1763 return;
1764
1765 linenum = 1;
1766 col = 0;
1767 eol = EOL_NONE;
1768 mixed = 0;
1769 whitespace = 1;
1770
1771 while ((ch = cupsFileGetChar(fp)) != EOF)
1772 {
1773 if (ch == '\r' || ch == '\n')
1774 {
1775 if (ch == '\n')
1776 {
1777 if (eol == EOL_NONE)
1778 eol = EOL_LF;
1779 else if (eol != EOL_LF)
1780 mixed = 1;
1781 }
1782 else if (ch == '\r')
1783 {
1784 if (cupsFilePeekChar(fp) == '\n')
1785 {
1786 cupsFileGetChar(fp);
1787
1788 if (eol == EOL_NONE)
1789 eol = EOL_CRLF;
dabcf635 1790 else if (eol != EOL_CRLF)
1a8f3056 1791 mixed = 1;
1792 }
1793 else if (eol == EOL_NONE)
1794 eol = EOL_CR;
dabcf635 1795 else if (eol != EOL_CR)
1a8f3056 1796 mixed = 1;
1797 }
1798
1799 if (col > 0 && whitespace)
1800 _cupsLangPrintf(stdout,
1801 _(" WARN Line %d only contains whitespace!\n"),
1802 linenum);
1803
1804 linenum ++;
1805 col = 0;
1806 whitespace = 1;
1807 }
1808 else
1809 {
1810 if (ch != ' ' && ch != '\t')
1811 whitespace = 0;
1812
1813 col ++;
1814 }
1815 }
1816
1817 if (mixed)
1818 _cupsLangPuts(stdout,
1819 _(" WARN File contains a mix of CR, LF, and "
1820 "CR LF line endings!\n"));
1821
1822 if (eol == EOL_CRLF)
1823 _cupsLangPuts(stdout,
1824 _(" WARN Non-Windows PPD files should use lines "
1825 "ending with only LF, not CR LF!\n"));
1826
1827 cupsFileClose(fp);
1828}
1829
1830
d1fc9981 1831/*
1832 * 'check_constraints()' - Check UIConstraints in the PPD file.
1833 */
1834
0dcff045 1835static int /* O - Errors found */
d1fc9981 1836check_constraints(ppd_file_t *ppd, /* I - PPD file */
1837 int errors, /* I - Errors found */
1838 int verbose, /* I - Verbosity level */
1839 int warn) /* I - Warnings only? */
1840{
d3392537 1841 int i; /* Looping var */
1842 const char *prefix; /* WARN/FAIL prefix */
1843 ppd_const_t *c; /* Current UIConstraints data */
1844 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
1845 const char *vptr; /* Pointer into constraint value */
1846 char option[PPD_MAX_NAME],
1847 /* Option name/MainKeyword */
1848 choice[PPD_MAX_NAME],
1849 /* Choice/OptionKeyword */
1850 *ptr; /* Pointer into option or choice */
1851 int num_options; /* Number of options */
1852 cups_option_t *options; /* Options */
1853 ppd_option_t *o; /* PPD option */
d1fc9981 1854
1855
1856 prefix = warn ? " WARN " : "**FAIL**";
1857
d3392537 1858
1859 /*
1860 * See what kind of constraint data we have in the PPD...
1861 */
1862
1863 if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
d1fc9981 1864 {
d3392537 1865 /*
1866 * Check new-style cupsUIConstraints data...
1867 */
d1fc9981 1868
d3392537 1869 for (; constattr;
1870 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
d1fc9981 1871 {
d3392537 1872 if (!constattr->value)
1873 {
1874 if (!warn && !errors && !verbose)
1875 _cupsLangPuts(stdout, _(" FAIL\n"));
d1fc9981 1876
d1fc9981 1877 _cupsLangPrintf(stdout,
d3392537 1878 _(" %s Empty cupsUIConstraints %s!\n"),
1879 prefix, constattr->spec);
1880
1881 if (!warn)
1882 errors ++;
1883
1884 continue;
1885 }
1886
1887 for (i = 0, vptr = strchr(constattr->value, '*');
1888 vptr;
1889 i ++, vptr = strchr(vptr + 1, '*'));
1890
1891 if (i == 0)
1892 {
1893 if (!warn && !errors && !verbose)
1894 _cupsLangPuts(stdout, _(" FAIL\n"));
1895
d1fc9981 1896 _cupsLangPrintf(stdout,
d3392537 1897 _(" %s Bad cupsUIConstraints %s: \"%s\"!\n"),
1898 prefix, constattr->spec, constattr->value);
d1fc9981 1899
d3392537 1900 if (!warn)
1901 errors ++;
d1fc9981 1902
d3392537 1903 continue;
1904 }
d1fc9981 1905
d3392537 1906 cupsArraySave(ppd->sorted_attrs);
d1fc9981 1907
d3392537 1908 if (constattr->spec[0] &&
1909 !ppdFindAttr(ppd, "cupsUIResolver", constattr->spec))
1910 {
1911 if (!warn && !errors && !verbose)
1912 _cupsLangPuts(stdout, _(" FAIL\n"));
1913
1914 _cupsLangPrintf(stdout,
1915 _(" %s Missing cupsUIResolver %s!\n"),
1916 prefix, constattr->spec);
d1fc9981 1917
d3392537 1918 if (!warn)
1919 errors ++;
1920 }
1921
1922 cupsArrayRestore(ppd->sorted_attrs);
1923
1924 num_options = 0;
1925 options = NULL;
1926
1927 for (vptr = strchr(constattr->value, '*');
1928 vptr;
e05c5d0c 1929 vptr = strchr(vptr, '*'))
d3392537 1930 {
1931 /*
1932 * Extract "*Option Choice" or just "*Option"...
1933 */
1934
1935 for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
1936 if (ptr < (option + sizeof(option) - 1))
1937 *ptr++ = *vptr;
1938
1939 *ptr = '\0';
1940
1941 while (isspace(*vptr & 255))
1942 vptr ++;
1943
1944 if (*vptr == '*')
d3392537 1945 choice[0] = '\0';
d3392537 1946 else
1947 {
1948 for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
1949 if (ptr < (choice + sizeof(choice) - 1))
1950 *ptr++ = *vptr;
1951
1952 *ptr = '\0';
1953 }
1954
09a27d08 1955 if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
1956 {
1957 _cups_strcpy(option, option + 6);
1958 strcpy(choice, "Custom");
1959 }
1960
d3392537 1961 if ((o = ppdFindOption(ppd, option)) == NULL)
1962 {
1963 if (!warn && !errors && !verbose)
1964 _cupsLangPuts(stdout, _(" FAIL\n"));
1965
1966 _cupsLangPrintf(stdout,
1967 _(" %s Missing option %s in "
1968 "cupsUIConstraints %s: \"%s\"!\n"),
1969 prefix, option, constattr->spec, constattr->value);
1970
1971 if (!warn)
1972 errors ++;
1973
1974 continue;
1975 }
1976
1977 if (choice[0] && !ppdFindChoice(o, choice))
1978 {
1979 if (!warn && !errors && !verbose)
1980 _cupsLangPuts(stdout, _(" FAIL\n"));
1981
1982 _cupsLangPrintf(stdout,
1983 _(" %s Missing choice *%s %s in "
1984 "cupsUIConstraints %s: \"%s\"!\n"),
1985 prefix, option, choice, constattr->spec,
1986 constattr->value);
1987
1988 if (!warn)
1989 errors ++;
1990
1991 continue;
1992 }
1993
1994 if (choice[0])
1995 num_options = cupsAddOption(option, choice, num_options, &options);
1996 else
1997 {
1998 for (i = 0; i < o->num_choices; i ++)
1999 if (strcasecmp(o->choices[i].choice, "None") &&
2000 strcasecmp(o->choices[i].choice, "Off") &&
2001 strcasecmp(o->choices[i].choice, "False"))
2002 {
2003 num_options = cupsAddOption(option, o->choices[i].choice,
2004 num_options, &options);
2005 break;
2006 }
2007 }
2008 }
2009
b2c85c09 2010 /*
2011 * Resolvers must list at least two options...
2012 */
2013
2014 if (num_options < 2)
2015 {
2016 if (!warn && !errors && !verbose)
2017 _cupsLangPuts(stdout, _(" FAIL\n"));
2018
2019 _cupsLangPrintf(stdout,
2020 _(" %s cupsUIResolver %s does not list at least "
2021 "two different options!\n"),
2022 prefix, constattr->spec);
2023
2024 if (!warn)
2025 errors ++;
2026 }
2027
d3392537 2028 /*
2029 * Test the resolver...
2030 */
2031
2032 if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options))
2033 {
2034 if (!warn && !errors && !verbose)
2035 _cupsLangPuts(stdout, _(" FAIL\n"));
2036
2037 _cupsLangPrintf(stdout,
2038 _(" %s cupsUIResolver %s causes a loop!\n"),
2039 prefix, constattr->spec);
2040
2041 if (!warn)
2042 errors ++;
2043 }
2044
2045 cupsFreeOptions(num_options, options);
d1fc9981 2046 }
d3392537 2047 }
2048 else
2049 {
2050 /*
2051 * Check old-style [Non]UIConstraints data...
2052 */
d1fc9981 2053
d3392537 2054 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
d1fc9981 2055 {
09a27d08 2056 if (!strncasecmp(c->option1, "Custom", 6) &&
2057 !strcasecmp(c->choice1, "True"))
2058 {
2059 strcpy(option, c->option1 + 6);
2060 strcpy(choice, "Custom");
2061 }
2062 else
2063 {
2064 strcpy(option, c->option1);
2065 strcpy(choice, c->choice1);
2066 }
2067
2068 if ((o = ppdFindOption(ppd, option)) == NULL)
d3392537 2069 {
2070 if (!warn && !errors && !verbose)
2071 _cupsLangPuts(stdout, _(" FAIL\n"));
2072
2073 _cupsLangPrintf(stdout,
2074 _(" %s Missing option %s in "
2075 "UIConstraints \"*%s %s *%s %s\"!\n"),
2076 prefix, c->option1,
2077 c->option1, c->choice1, c->option2, c->choice2);
2078
2079 if (!warn)
2080 errors ++;
2081 }
09a27d08 2082 else if (choice[0] && !ppdFindChoice(o, choice))
d3392537 2083 {
2084 if (!warn && !errors && !verbose)
2085 _cupsLangPuts(stdout, _(" FAIL\n"));
d1fc9981 2086
d3392537 2087 _cupsLangPrintf(stdout,
2088 _(" %s Missing choice *%s %s in "
2089 "UIConstraints \"*%s %s *%s %s\"!\n"),
2090 prefix, c->option1, c->choice1,
2091 c->option1, c->choice1, c->option2, c->choice2);
2092
2093 if (!warn)
2094 errors ++;
2095 }
2096
09a27d08 2097 if (!strncasecmp(c->option2, "Custom", 6) &&
2098 !strcasecmp(c->choice2, "True"))
2099 {
2100 strcpy(option, c->option2 + 6);
2101 strcpy(choice, "Custom");
2102 }
2103 else
2104 {
2105 strcpy(option, c->option2);
2106 strcpy(choice, c->choice2);
2107 }
2108
2109 if ((o = ppdFindOption(ppd, option)) == NULL)
d3392537 2110 {
2111 if (!warn && !errors && !verbose)
2112 _cupsLangPuts(stdout, _(" FAIL\n"));
d1fc9981 2113
d3392537 2114 _cupsLangPrintf(stdout,
2115 _(" %s Missing option %s in "
2116 "UIConstraints \"*%s %s *%s %s\"!\n"),
2117 prefix, c->option2,
2118 c->option1, c->choice1, c->option2, c->choice2);
2119
2120 if (!warn)
2121 errors ++;
2122 }
09a27d08 2123 else if (choice[0] && !ppdFindChoice(o, choice))
d3392537 2124 {
2125 if (!warn && !errors && !verbose)
2126 _cupsLangPuts(stdout, _(" FAIL\n"));
2127
2128 _cupsLangPrintf(stdout,
2129 _(" %s Missing choice *%s %s in "
2130 "UIConstraints \"*%s %s *%s %s\"!\n"),
2131 prefix, c->option2, c->choice2,
2132 c->option1, c->choice1, c->option2, c->choice2);
2133
2134 if (!warn)
2135 errors ++;
2136 }
d1fc9981 2137 }
2138 }
2139
2140 return (errors);
2141}
2142
2143
48c9a702 2144/*
2145 * 'check_case()' - Check that there are no duplicate groups, options,
2146 * or choices that differ only by case.
2147 */
2148
2149static int /* O - Errors found */
2150check_case(ppd_file_t *ppd, /* I - PPD file */
2151 int errors, /* I - Errors found */
2152 int verbose) /* I - Verbosity level */
2153{
2154 int i, j; /* Looping vars */
2155 ppd_group_t *groupa, /* First group */
2156 *groupb; /* Second group */
2157 ppd_option_t *optiona, /* First option */
2158 *optionb; /* Second option */
2159 ppd_choice_t *choicea, /* First choice */
2160 *choiceb; /* Second choice */
2161
2162
2163 /*
2164 * Check that the groups do not have any duplicate names...
2165 */
2166
2167 for (i = ppd->num_groups, groupa = ppd->groups; i > 1; i --, groupa ++)
2168 for (j = i - 1, groupb = groupa + 1; j > 0; j --, groupb ++)
2169 if (!strcasecmp(groupa->name, groupb->name))
2170 {
2171 if (!errors && !verbose)
2172 _cupsLangPuts(stdout, _(" FAIL\n"));
2173
2174 if (verbose >= 0)
2175 _cupsLangPrintf(stdout,
2176 _(" **FAIL** Group names %s and %s differ only "
2177 "by case!\n"),
2178 groupa->name, groupb->name);
2179
2180 errors ++;
2181 }
2182
2183 /*
2184 * Check that the options do not have any duplicate names...
2185 */
2186
2187 for (optiona = ppdFirstOption(ppd); optiona; optiona = ppdNextOption(ppd))
2188 {
2189 cupsArraySave(ppd->options);
2190 for (optionb = ppdNextOption(ppd); optionb; optionb = ppdNextOption(ppd))
2191 if (!strcasecmp(optiona->keyword, optionb->keyword))
2192 {
2193 if (!errors && !verbose)
2194 _cupsLangPuts(stdout, _(" FAIL\n"));
2195
2196 if (verbose >= 0)
2197 _cupsLangPrintf(stdout,
2198 _(" **FAIL** Option names %s and %s differ only "
2199 "by case!\n"),
2200 optiona->keyword, optionb->keyword);
2201
2202 errors ++;
2203 }
2204 cupsArrayRestore(ppd->options);
2205
2206 /*
2207 * Then the choices...
2208 */
2209
2210 for (i = optiona->num_choices, choicea = optiona->choices;
2211 i > 1;
2212 i --, choicea ++)
2213 for (j = i - 1, choiceb = choicea + 1; j > 0; j --, choiceb ++)
2214 if (!strcmp(choicea->choice, choiceb->choice))
2215 {
2216 if (!errors && !verbose)
2217 _cupsLangPuts(stdout, _(" FAIL\n"));
2218
2219 if (verbose >= 0)
2220 _cupsLangPrintf(stdout,
2221 _(" **FAIL** Multiple occurrences of %s "
2222 "choice name %s!\n"),
2223 optiona->keyword, choicea->choice);
2224
2225 errors ++;
2226
2227 choicea ++;
2228 i --;
2229 break;
2230 }
2231 else if (!strcasecmp(choicea->choice, choiceb->choice))
2232 {
2233 if (!errors && !verbose)
2234 _cupsLangPuts(stdout, _(" FAIL\n"));
2235
2236 if (verbose >= 0)
2237 _cupsLangPrintf(stdout,
2238 _(" **FAIL** %s choice names %s and %s "
2239 "differ only by case!\n"),
2240 optiona->keyword, choicea->choice, choiceb->choice);
2241
2242 errors ++;
2243 }
2244 }
2245
2246 /*
2247 * Return the number of errors found...
2248 */
2249
2250 return (errors);
2251}
2252
2253
d1fc9981 2254/*
2255 * 'check_defaults()' - Check default option keywords in the PPD file.
2256 */
2257
0dcff045 2258static int /* O - Errors found */
d1fc9981 2259check_defaults(ppd_file_t *ppd, /* I - PPD file */
2260 int errors, /* I - Errors found */
2261 int verbose, /* I - Verbosity level */
2262 int warn) /* I - Warnings only? */
2263{
2264 int j, k; /* Looping vars */
2265 ppd_attr_t *attr; /* PPD attribute */
2266 ppd_option_t *option; /* Standard UI option */
2267 const char *prefix; /* WARN/FAIL prefix */
2268
2269
2270 prefix = warn ? " WARN " : "**FAIL**";
2271
2272 for (j = 0; j < ppd->num_attrs; j ++)
2273 {
2274 attr = ppd->attrs[j];
2275
2276 if (!strcmp(attr->name, "DefaultColorSpace") ||
2277 !strcmp(attr->name, "DefaultFont") ||
12d95de0 2278 !strcmp(attr->name, "DefaultHalftoneType") ||
d1fc9981 2279 !strcmp(attr->name, "DefaultImageableArea") ||
12d95de0 2280 !strcmp(attr->name, "DefaultLeadingEdge") ||
d1fc9981 2281 !strcmp(attr->name, "DefaultOutputOrder") ||
2282 !strcmp(attr->name, "DefaultPaperDimension") ||
2283 !strcmp(attr->name, "DefaultResolution") ||
2284 !strcmp(attr->name, "DefaultTransfer"))
2285 continue;
2286
2287 if (!strncmp(attr->name, "Default", 7))
2288 {
2289 if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
2290 strcmp(attr->value, "Unknown"))
2291 {
2292 /*
2293 * Check that the default option value matches a choice...
2294 */
2295
2296 for (k = 0; k < option->num_choices; k ++)
2297 if (!strcmp(option->choices[k].choice, attr->value))
2298 break;
2299
2300 if (k >= option->num_choices)
2301 {
2302 if (!warn && !errors && !verbose)
2303 _cupsLangPuts(stdout, _(" FAIL\n"));
2304
2305 if (verbose >= 0)
2306 _cupsLangPrintf(stdout,
2307 _(" %s %s %s does not exist!\n"),
2308 prefix, attr->name, attr->value);
2309
2310 if (!warn)
2311 errors ++;
2312 }
2313 }
2314 }
2315 }
2316
2317 return (errors);
2318}
2319
2320
48c9a702 2321/*
2322 * 'check_duplex()' - Check duplex keywords in the PPD file.
2323 */
2324
2325static int /* O - Errors found */
2326check_duplex(ppd_file_t *ppd, /* I - PPD file */
2327 int errors, /* I - Error found */
2328 int verbose, /* I - Verbosity level */
2329 int warn) /* I - Warnings only? */
2330{
2331 int i; /* Looping var */
2332 ppd_option_t *option; /* PPD option */
2333 ppd_choice_t *choice; /* Current choice */
2334 const char *prefix; /* Message prefix */
2335
2336
2337 prefix = warn ? " WARN " : "**FAIL**";
2338
2339 /*
2340 * Check for a duplex option, and for standard values...
2341 */
2342
2343 if ((option = ppdFindOption(ppd, "Duplex")) != NULL)
2344 {
2345 if (!ppdFindChoice(option, "None"))
2346 {
2347 if (verbose >= 0)
2348 {
2349 if (!warn && !errors && !verbose)
2350 _cupsLangPuts(stdout, _(" FAIL\n"));
2351
2352 _cupsLangPrintf(stdout,
2353 _(" %s REQUIRED %s does not define "
2354 "choice None!\n"
2355 " REF: Page 122, section 5.17\n"),
2356 prefix, option->keyword);
2357 }
2358
2359 if (!warn)
2360 errors ++;
2361 }
2362
2363 for (i = option->num_choices, choice = option->choices;
2364 i > 0;
2365 i --, choice ++)
2366 if (strcmp(choice->choice, "None") &&
2367 strcmp(choice->choice, "DuplexNoTumble") &&
2368 strcmp(choice->choice, "DuplexTumble") &&
2369 strcmp(choice->choice, "SimplexTumble"))
2370 {
2371 if (verbose >= 0)
2372 {
2373 if (!warn && !errors && !verbose)
2374 _cupsLangPuts(stdout, _(" FAIL\n"));
2375
2376 _cupsLangPrintf(stdout,
2377 _(" %s Bad %s choice %s!\n"
2378 " REF: Page 122, section 5.17\n"),
2379 prefix, option->keyword, choice->choice);
2380 }
2381
2382 if (!warn)
2383 errors ++;
2384 }
2385 }
2386
2387 return (errors);
2388}
2389
2390
d1fc9981 2391/*
2392 * 'check_filters()' - Check filters in the PPD file.
2393 */
2394
0dcff045 2395static int /* O - Errors found */
d1fc9981 2396check_filters(ppd_file_t *ppd, /* I - PPD file */
2397 const char *root, /* I - Root directory */
2398 int errors, /* I - Errors found */
2399 int verbose, /* I - Verbosity level */
2400 int warn) /* I - Warnings only? */
2401{
c4cef23d 2402 int i; /* Looping var */
d1fc9981 2403 ppd_attr_t *attr; /* PPD attribute */
2404 const char *ptr; /* Pointer into string */
d1fc9981 2405 char super[16], /* Super-type for filter */
2406 type[256], /* Type for filter */
c4cef23d 2407 program[1024], /* Program/filter name */
d1fc9981 2408 pathprog[1024]; /* Complete path to program/filter */
2409 int cost; /* Cost of filter */
2410 const char *prefix; /* WARN/FAIL prefix */
2411
2412
2413 prefix = warn ? " WARN " : "**FAIL**";
2414
02aabcda 2415 /*
2416 * cupsFilter
2417 */
2418
c4cef23d 2419 for (i = 0; i < ppd->num_filters; i ++)
d1fc9981 2420 {
c4cef23d 2421 if (sscanf(ppd->filters[i], "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
2422 &cost, program) != 4)
d1fc9981 2423 {
2424 if (!warn && !errors && !verbose)
2425 _cupsLangPuts(stdout, _(" FAIL\n"));
2426
2427 if (verbose >= 0)
2428 _cupsLangPrintf(stdout,
2429 _(" %s Bad cupsFilter value \"%s\"!\n"),
c4cef23d 2430 prefix, ppd->filters[i]);
d1fc9981 2431
2432 if (!warn)
2433 errors ++;
2434 }
48c9a702 2435 else if (strcmp(program, "-"))
d1fc9981 2436 {
2437 if (program[0] == '/')
2438 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
2439 else
2440 {
2441 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
2442 ptr = CUPS_SERVERBIN;
2443
2444 if (*ptr == '/' || !*root)
2445 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
2446 program);
2447 else
2448 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
2449 program);
2450 }
2451
c9be2428 2452 if (access(pathprog, X_OK))
d1fc9981 2453 {
2454 if (!warn && !errors && !verbose)
2455 _cupsLangPuts(stdout, _(" FAIL\n"));
2456
2457 if (verbose >= 0)
2458 _cupsLangPrintf(stdout, _(" %s Missing cupsFilter "
2459 "file \"%s\"\n"), prefix, program);
2460
2461 if (!warn)
2462 errors ++;
2463 }
02aabcda 2464 else
2465 errors = valid_path("cupsFilter", pathprog, errors, verbose, warn);
d1fc9981 2466 }
2467 }
2468
02aabcda 2469 /*
2470 * cupsPreFilter
2471 */
2472
d1fc9981 2473 for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
2474 attr;
2475 attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
2476 {
2477 if (!attr->value ||
c4cef23d 2478 sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
2479 &cost, program) != 4)
d1fc9981 2480 {
2481 if (!warn && !errors && !verbose)
2482 _cupsLangPuts(stdout, _(" FAIL\n"));
2483
2484 if (verbose >= 0)
2485 _cupsLangPrintf(stdout,
2486 _(" %s Bad cupsPreFilter value \"%s\"!\n"),
2487 prefix, attr->value ? attr->value : "");
2488
2489 if (!warn)
2490 errors ++;
2491 }
48c9a702 2492 else if (strcmp(program, "-"))
d1fc9981 2493 {
2494 if (program[0] == '/')
2495 snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
2496 else
2497 {
2498 if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
2499 ptr = CUPS_SERVERBIN;
2500
2501 if (*ptr == '/' || !*root)
2502 snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
2503 program);
2504 else
2505 snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
2506 program);
2507 }
2508
c9be2428 2509 if (access(pathprog, X_OK))
d1fc9981 2510 {
2511 if (!warn && !errors && !verbose)
2512 _cupsLangPuts(stdout, _(" FAIL\n"));
2513
2514 if (verbose >= 0)
2515 _cupsLangPrintf(stdout, _(" %s Missing cupsPreFilter "
2516 "file \"%s\"\n"), prefix, program);
2517
2518 if (!warn)
2519 errors ++;
2520 }
02aabcda 2521 else
2522 errors = valid_path("cupsPreFilter", pathprog, errors, verbose, warn);
2523 }
2524 }
2525
2526#ifdef __APPLE__
2527 /*
2528 * APDialogExtension
2529 */
2530
2531 for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL);
2532 attr != NULL;
2533 attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
2534 {
2535 if (!attr->value || access(attr->value, 0))
2536 {
2537 if (!warn && !errors && !verbose)
2538 _cupsLangPuts(stdout, _(" FAIL\n"));
2539
2540 if (verbose >= 0)
2541 _cupsLangPrintf(stdout, _(" %s Missing "
2542 "APDialogExtension file \"%s\"\n"),
2543 prefix, attr->value ? attr->value : "<NULL>");
2544
2545 if (!warn)
2546 errors ++;
2547 }
2548 else
2549 errors = valid_path("APDialogExtension", attr->value, errors, verbose,
2550 warn);
2551 }
2552
2553 /*
2554 * APPrinterIconPath
2555 */
2556
2557 if ((attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
2558 {
2559 if (!attr->value || access(attr->value, 0))
2560 {
2561 if (!warn && !errors && !verbose)
2562 _cupsLangPuts(stdout, _(" FAIL\n"));
2563
2564 if (verbose >= 0)
2565 _cupsLangPrintf(stdout, _(" %s Missing "
2566 "APPrinterIconPath file \"%s\"\n"),
2567 prefix, attr->value ? attr->value : "<NULL>");
2568
2569 if (!warn)
2570 errors ++;
d1fc9981 2571 }
02aabcda 2572 else
2573 errors = valid_path("APPrinterIconPath", attr->value, errors, verbose,
2574 warn);
d1fc9981 2575 }
2576
02aabcda 2577 /*
2578 * APPrinterLowInkTool
2579 */
2580
2581 if ((attr = ppdFindAttr(ppd, "APPrinterLowInkTool", NULL)) != NULL)
2582 {
2583 if (!attr->value || access(attr->value, 0))
2584 {
2585 if (!warn && !errors && !verbose)
2586 _cupsLangPuts(stdout, _(" FAIL\n"));
2587
2588 if (verbose >= 0)
2589 _cupsLangPrintf(stdout, _(" %s Missing "
2590 "APPrinterLowInkTool file \"%s\"\n"),
2591 prefix, attr->value ? attr->value : "<NULL>");
2592
2593 if (!warn)
2594 errors ++;
2595 }
2596 else
2597 errors = valid_path("APPrinterLowInkTool", attr->value, errors, verbose,
2598 warn);
2599 }
2600
2601 /*
2602 * APPrinterUtilityPath
2603 */
2604
2605 if ((attr = ppdFindAttr(ppd, "APPrinterUtilityPath", NULL)) != NULL)
2606 {
2607 if (!attr->value || access(attr->value, 0))
2608 {
2609 if (!warn && !errors && !verbose)
2610 _cupsLangPuts(stdout, _(" FAIL\n"));
2611
2612 if (verbose >= 0)
2613 _cupsLangPrintf(stdout, _(" %s Missing "
2614 "APPrinterUtilityPath file \"%s\"\n"),
2615 prefix, attr->value ? attr->value : "<NULL>");
2616
2617 if (!warn)
2618 errors ++;
2619 }
2620 else
2621 errors = valid_path("APPrinterUtilityPath", attr->value, errors, verbose,
2622 warn);
2623 }
71a557e3 2624
2625 /*
2626 * APScanAppBundleID and APScanAppPath
2627 */
2628
2629 if ((attr = ppdFindAttr(ppd, "APScanAppPath", NULL)) != NULL)
2630 {
2631 if (!attr->value || access(attr->value, 0))
2632 {
2633 if (!warn && !errors && !verbose)
2634 _cupsLangPuts(stdout, _(" FAIL\n"));
2635
2636 if (verbose >= 0)
2637 _cupsLangPrintf(stdout, _(" %s Missing "
2638 "APScanAppPath file \"%s\"\n"),
2639 prefix, attr->value ? attr->value : "<NULL>");
2640
2641 if (!warn)
2642 errors ++;
2643 }
2644 else
2645 errors = valid_path("APScanAppPath", attr->value, errors, verbose,
2646 warn);
2647
2648 if (ppdFindAttr(ppd, "APScanAppBundleID", NULL))
2649 {
2650 if (!warn && !errors && !verbose)
2651 _cupsLangPuts(stdout, _(" FAIL\n"));
2652
2653 if (verbose >= 0)
2654 _cupsLangPrintf(stdout, _(" %s Cannot provide both "
2655 "APScanAppPath and APScanAppBundleID!\n"),
2656 prefix);
2657
2658 if (!warn)
2659 errors ++;
2660 }
2661 }
02aabcda 2662#endif /* __APPLE__ */
2663
d1fc9981 2664 return (errors);
2665}
2666
2667
c9be2428 2668/*
2669 * 'check_profiles()' - Check ICC color profiles in the PPD file.
2670 */
2671
2672static int /* O - Errors found */
2673check_profiles(ppd_file_t *ppd, /* I - PPD file */
2674 const char *root, /* I - Root directory */
2675 int errors, /* I - Errors found */
2676 int verbose, /* I - Verbosity level */
2677 int warn) /* I - Warnings only? */
2678{
2679 int i; /* Looping var */
2680 ppd_attr_t *attr; /* PPD attribute */
2681 const char *ptr; /* Pointer into string */
2682 const char *prefix; /* WARN/FAIL prefix */
2683 char filename[1024]; /* Profile filename */
2684 int num_profiles = 0; /* Number of profiles */
2685 unsigned hash, /* Current hash value */
2686 hashes[1000]; /* Hash values of profile names */
2687 const char *specs[1000]; /* Specifiers for profiles */
2688
2689
2690 prefix = warn ? " WARN " : "**FAIL**";
2691
2692 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
2693 attr;
2694 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
2695 {
2696 /*
2697 * Check for valid selector...
2698 */
2699
2700 for (i = 0, ptr = strchr(attr->spec, '.'); ptr; ptr = strchr(ptr + 1, '.'))
2701 i ++;
2702
2703 if (!attr->value || i < 2)
2704 {
2705 if (!warn && !errors && !verbose)
2706 _cupsLangPuts(stdout, _(" FAIL\n"));
2707
2708 if (verbose >= 0)
2709 _cupsLangPrintf(stdout,
2710 _(" %s Bad cupsICCProfile %s!\n"),
2711 prefix, attr->spec);
2712
2713 if (!warn)
2714 errors ++;
2715
2716 continue;
2717 }
2718
2719 /*
2720 * Check for valid profile filename...
2721 */
2722
2723 if (attr->value[0] == '/')
2724 snprintf(filename, sizeof(filename), "%s%s", root, attr->value);
2725 else
2726 {
2727 if ((ptr = getenv("CUPS_DATADIR")) == NULL)
2728 ptr = CUPS_DATADIR;
2729
2730 if (*ptr == '/' || !*root)
2731 snprintf(filename, sizeof(filename), "%s%s/profiles/%s", root, ptr,
2732 attr->value);
2733 else
2734 snprintf(filename, sizeof(filename), "%s/%s/profiles/%s", root, ptr,
2735 attr->value);
2736 }
2737
2738 if (access(filename, 0))
2739 {
2740 if (!warn && !errors && !verbose)
2741 _cupsLangPuts(stdout, _(" FAIL\n"));
2742
2743 if (verbose >= 0)
2744 _cupsLangPrintf(stdout, _(" %s Missing cupsICCProfile "
2745 "file \"%s\"!\n"), prefix, attr->value);
2746
2747 if (!warn)
2748 errors ++;
2749 }
02aabcda 2750 else
2751 errors = valid_path("cupsICCProfile", filename, errors, verbose, warn);
c9be2428 2752
2753 /*
2754 * Check for hash collisions...
2755 */
2756
2757 hash = _ppdHashName(attr->spec);
2758
2759 if (num_profiles > 0)
2760 {
2761 for (i = 0; i < num_profiles; i ++)
2762 if (hashes[i] == hash)
2763 break;
2764
2765 if (i < num_profiles)
2766 {
2767 if (!warn && !errors && !verbose)
2768 _cupsLangPuts(stdout, _(" FAIL\n"));
2769
2770 if (verbose >= 0)
2771 _cupsLangPrintf(stdout,
2772 _(" %s cupsICCProfile %s hash value "
2773 "collides with %s!\n"), prefix, attr->spec,
2774 specs[i]);
2775
2776 if (!warn)
2777 errors ++;
2778 }
2779 }
2780
2781 /*
2782 * Remember up to 1000 profiles...
2783 */
2784
2785 if (num_profiles < 1000)
2786 {
2787 hashes[num_profiles] = hash;
2788 specs[num_profiles] = attr->spec;
2789 num_profiles ++;
2790 }
2791 }
2792
2793 return (errors);
2794}
2795
2796
19b7f3b7 2797/*
2798 * 'check_sizes()' - Check media sizes in the PPD file.
2799 */
2800
2801static int /* O - Errors found */
2802check_sizes(ppd_file_t *ppd, /* I - PPD file */
2803 int errors, /* I - Errors found */
2804 int verbose, /* I - Verbosity level */
2805 int warn) /* I - Warnings only? */
2806{
2807 int i; /* Looping vars */
2808 ppd_size_t *size; /* Current size */
2809 int width, /* Custom width */
2810 length; /* Custom length */
2811 char name[PPD_MAX_NAME], /* Size name without dot suffix */
2812 *nameptr; /* Pointer into name */
2813 const char *prefix; /* WARN/FAIL prefix */
2814 ppd_option_t *page_size, /* PageSize option */
2815 *page_region; /* PageRegion option */
2816
2817
2818 prefix = warn ? " WARN " : "**FAIL**";
2819
ab59a541 2820 if ((page_size = ppdFindOption(ppd, "PageSize")) == NULL && warn != 2)
19b7f3b7 2821 {
2822 if (!warn && !errors && !verbose)
2823 _cupsLangPuts(stdout, _(" FAIL\n"));
2824
2825 if (verbose >= 0)
2826 _cupsLangPrintf(stdout,
2827 _(" %s Missing REQUIRED PageSize option!\n"
2828 " REF: Page 99, section 5.14.\n"),
2829 prefix);
2830
2831 if (!warn)
2832 errors ++;
2833 }
2834
ab59a541 2835 if ((page_region = ppdFindOption(ppd, "PageRegion")) == NULL && warn != 2)
19b7f3b7 2836 {
2837 if (!warn && !errors && !verbose)
2838 _cupsLangPuts(stdout, _(" FAIL\n"));
2839
2840 if (verbose >= 0)
2841 _cupsLangPrintf(stdout,
2842 _(" %s Missing REQUIRED PageRegion option!\n"
2843 " REF: Page 100, section 5.14.\n"),
2844 prefix);
2845
2846 if (!warn)
2847 errors ++;
2848 }
2849
2850 for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
2851 {
2852 /*
2853 * Check that the size name is standard...
2854 */
2855
2856 if (!strcmp(size->name, "Custom"))
2857 {
2858 /*
2859 * Skip custom page size...
2860 */
2861
2862 continue;
2863 }
2864 else if (warn != 2 && size->name[0] == 'w' &&
2865 sscanf(size->name, "w%dh%d", &width, &length) == 2)
2866 {
2867 /*
2868 * Validate device-specific size wNNNhNNN should have proper width and
2869 * length...
2870 */
2871
2872 if (fabs(width - size->width) >= 1.0 ||
2873 fabs(length - size->length) >= 1.0)
2874 {
2875 if (!warn && !errors && !verbose)
2876 _cupsLangPuts(stdout, _(" FAIL\n"));
2877
2878 if (verbose >= 0)
2879 _cupsLangPrintf(stdout,
2880 _(" %s Size \"%s\" has unexpected dimensions "
2881 "(%gx%g)!\n"),
2882 prefix, size->name, size->width, size->length);
2883
2884 if (!warn)
2885 errors ++;
2886 }
2887 }
2888 else if (warn && verbose >= 0)
2889 {
2890 /*
2891 * Lookup the size name in the standard size table...
2892 */
2893
2894 strlcpy(name, size->name, sizeof(name));
2895 if ((nameptr = strchr(name, '.')) != NULL)
2896 *nameptr = '\0';
2897
2898 if (!bsearch(name, adobe_size_names,
2899 sizeof(adobe_size_names) /
2900 sizeof(adobe_size_names[0]),
2901 sizeof(adobe_size_names[0]),
2902 (int (*)(const void *, const void *))strcmp))
2903 {
2904 _cupsLangPrintf(stdout,
2905 _(" %s Non-standard size name \"%s\"!\n"
2906 " REF: Page 187, section B.2.\n"),
2907 prefix, size->name);
2908 }
2909 }
2910
2911 /*
2912 * Verify that the size is defined for both PageSize and PageRegion...
2913 */
2914
2915 if (warn != 2 && !ppdFindChoice(page_size, size->name))
2916 {
2917 if (!warn && !errors && !verbose)
2918 _cupsLangPuts(stdout, _(" FAIL\n"));
2919
2920 if (verbose >= 0)
2921 _cupsLangPrintf(stdout,
2922 _(" %s Size \"%s\" defined for %s but not for "
2923 "%s!\n"),
2924 prefix, size->name, "PageRegion", "PageSize");
2925
2926 if (!warn)
2927 errors ++;
2928 }
2929 else if (warn != 2 && !ppdFindChoice(page_region, size->name))
2930 {
2931 if (!warn && !errors && !verbose)
2932 _cupsLangPuts(stdout, _(" FAIL\n"));
2933
2934 if (verbose >= 0)
2935 _cupsLangPrintf(stdout,
2936 _(" %s Size \"%s\" defined for %s but not for "
2937 "%s!\n"),
2938 prefix, size->name, "PageSize", "PageRegion");
2939
2940 if (!warn)
2941 errors ++;
2942 }
2943 }
2944
2945 return (errors);
2946}
2947
2948
d1fc9981 2949/*
2950 * 'check_translations()' - Check translations in the PPD file.
2951 */
2952
0dcff045 2953static int /* O - Errors found */
d1fc9981 2954check_translations(ppd_file_t *ppd, /* I - PPD file */
2955 int errors, /* I - Errors found */
2956 int verbose, /* I - Verbosity level */
2957 int warn) /* I - Warnings only? */
2958{
2959 int j; /* Looping var */
2960 ppd_attr_t *attr; /* PPD attribute */
d0fde099 2961 cups_array_t *languages; /* Array of languages */
2962 int langlen; /* Length of language */
2963 char *language, /* Current language */
d1fc9981 2964 keyword[PPD_MAX_NAME], /* Localization keyword (full) */
2965 llkeyword[PPD_MAX_NAME],/* Localization keyword (base) */
2966 ckeyword[PPD_MAX_NAME], /* Custom option keyword (full) */
2967 cllkeyword[PPD_MAX_NAME];
2968 /* Custom option keyword (base) */
2969 ppd_option_t *option; /* Standard UI option */
2970 ppd_coption_t *coption; /* Custom option */
2971 ppd_cparam_t *cparam; /* Custom parameter */
d1fc9981 2972 char ll[3]; /* Base language */
2973 const char *prefix; /* WARN/FAIL prefix */
48c9a702 2974 const char *text; /* Pointer into UI text */
d1fc9981 2975
2976
2977 prefix = warn ? " WARN " : "**FAIL**";
2978
d0fde099 2979 if ((languages = _ppdGetLanguages(ppd)) != NULL)
d1fc9981 2980 {
2981 /*
2982 * This file contains localizations, check them...
2983 */
2984
d0fde099 2985 for (language = (char *)cupsArrayFirst(languages);
2986 language;
2987 language = (char *)cupsArrayNext(languages))
d1fc9981 2988 {
d0fde099 2989 langlen = strlen(language);
2990 if (langlen != 2 && langlen != 5)
d1fc9981 2991 {
2992 if (!warn && !errors && !verbose)
2993 _cupsLangPuts(stdout, _(" FAIL\n"));
2994
2995 if (verbose >= 0)
2996 _cupsLangPrintf(stdout,
2997 _(" %s Bad language \"%s\"!\n"),
d0fde099 2998 prefix, language);
d1fc9981 2999
3000 if (!warn)
3001 errors ++;
3002
3003 continue;
3004 }
3005
d0fde099 3006 if (!strcmp(language, "en"))
c78229ce 3007 continue;
3008
d0fde099 3009 strlcpy(ll, language, sizeof(ll));
d1fc9981 3010
3011 /*
3012 * Loop through all options and choices...
3013 */
3014
3015 for (option = ppdFirstOption(ppd);
3016 option;
3017 option = ppdNextOption(ppd))
3018 {
12d95de0 3019 if (!strcmp(option->keyword, "PageRegion"))
3020 continue;
3021
d0fde099 3022 snprintf(keyword, sizeof(keyword), "%s.Translation", language);
d1fc9981 3023 snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll);
3024
3025 if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL &&
3026 (attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL)
3027 {
3028 if (!warn && !errors && !verbose)
3029 _cupsLangPuts(stdout, _(" FAIL\n"));
3030
3031 if (verbose >= 0)
3032 _cupsLangPrintf(stdout,
3033 _(" %s Missing \"%s\" translation "
3034 "string for option %s!\n"),
d0fde099 3035 prefix, language, option->keyword);
d1fc9981 3036
3037 if (!warn)
3038 errors ++;
3039 }
3040 else if (!valid_utf8(attr->text))
3041 {
3042 if (!warn && !errors && !verbose)
3043 _cupsLangPuts(stdout, _(" FAIL\n"));
3044
3045 if (verbose >= 0)
3046 _cupsLangPrintf(stdout,
3047 _(" %s Bad UTF-8 \"%s\" translation "
3048 "string for option %s!\n"),
d0fde099 3049 prefix, language, option->keyword);
d1fc9981 3050
3051 if (!warn)
3052 errors ++;
3053 }
3054
d0fde099 3055 snprintf(keyword, sizeof(keyword), "%s.%s", language,
d1fc9981 3056 option->keyword);
3057 snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll,
3058 option->keyword);
3059
3060 for (j = 0; j < option->num_choices; j ++)
3061 {
48c9a702 3062 /*
3063 * First see if this choice is a number; if so, don't require
3064 * translation...
3065 */
3066
3067 for (text = option->choices[j].text; *text; text ++)
3068 if (!strchr("0123456789-+.", *text))
3069 break;
3070
3071 if (!*text)
3072 continue;
3073
3074 /*
3075 * Check custom choices differently...
3076 */
3077
d1fc9981 3078 if (!strcasecmp(option->choices[j].choice, "Custom") &&
3079 (coption = ppdFindCustomOption(ppd,
3080 option->keyword)) != NULL)
3081 {
3082 snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
d0fde099 3083 language, option->keyword);
d1fc9981 3084
3085 if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL &&
3086 !valid_utf8(attr->text))
3087 {
3088 if (!warn && !errors && !verbose)
3089 _cupsLangPuts(stdout, _(" FAIL\n"));
3090
3091 if (verbose >= 0)
3092 _cupsLangPrintf(stdout,
3093 _(" %s Bad UTF-8 \"%s\" "
3094 "translation string for option %s, "
3095 "choice %s!\n"),
d0fde099 3096 prefix, language,
3097 ckeyword + 1 + strlen(language),
d1fc9981 3098 "True");
3099
3100 if (!warn)
3101 errors ++;
3102 }
3103
3104 if (strcasecmp(option->keyword, "PageSize"))
3105 {
3106 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
3107 cparam;
3108 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
3109 {
3110 snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
d0fde099 3111 language, option->keyword);
d1fc9981 3112 snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s",
3113 ll, option->keyword);
3114
3115 if ((attr = ppdFindAttr(ppd, ckeyword,
3116 cparam->name)) == NULL &&
3117 (attr = ppdFindAttr(ppd, cllkeyword,
3118 cparam->name)) == NULL)
3119 {
3120 if (!warn && !errors && !verbose)
3121 _cupsLangPuts(stdout, _(" FAIL\n"));
3122
3123 if (verbose >= 0)
3124 _cupsLangPrintf(stdout,
3125 _(" %s Missing \"%s\" "
3126 "translation string for option %s, "
3127 "choice %s!\n"),
d0fde099 3128 prefix, language,
3129 ckeyword + 1 + strlen(language),
d1fc9981 3130 cparam->name);
3131
3132 if (!warn)
3133 errors ++;
3134 }
3135 else if (!valid_utf8(attr->text))
3136 {
3137 if (!warn && !errors && !verbose)
3138 _cupsLangPuts(stdout, _(" FAIL\n"));
3139
3140 if (verbose >= 0)
3141 _cupsLangPrintf(stdout,
3142 _(" %s Bad UTF-8 \"%s\" "
3143 "translation string for option %s, "
3144 "choice %s!\n"),
d0fde099 3145 prefix, language,
3146 ckeyword + 1 + strlen(language),
d1fc9981 3147 cparam->name);
3148
3149 if (!warn)
3150 errors ++;
3151 }
3152 }
3153 }
3154 }
3155 else if ((attr = ppdFindAttr(ppd, keyword,
3156 option->choices[j].choice)) == NULL &&
3157 (attr = ppdFindAttr(ppd, llkeyword,
3158 option->choices[j].choice)) == NULL)
3159 {
3160 if (!warn && !errors && !verbose)
3161 _cupsLangPuts(stdout, _(" FAIL\n"));
3162
3163 if (verbose >= 0)
3164 _cupsLangPrintf(stdout,
3165 _(" %s Missing \"%s\" "
3166 "translation string for option %s, "
3167 "choice %s!\n"),
d0fde099 3168 prefix, language, option->keyword,
d1fc9981 3169 option->choices[j].choice);
3170
3171 if (!warn)
3172 errors ++;
3173 }
3174 else if (!valid_utf8(attr->text))
3175 {
3176 if (!warn && !errors && !verbose)
3177 _cupsLangPuts(stdout, _(" FAIL\n"));
3178
3179 if (verbose >= 0)
3180 _cupsLangPrintf(stdout,
3181 _(" %s Bad UTF-8 \"%s\" "
3182 "translation string for option %s, "
3183 "choice %s!\n"),
d0fde099 3184 prefix, language, option->keyword,
d1fc9981 3185 option->choices[j].choice);
3186
3187 if (!warn)
3188 errors ++;
3189 }
3190 }
3191 }
3192 }
3193
3194 /*
3195 * Verify that we have the base language for each localized one...
3196 */
3197
d0fde099 3198 for (language = (char *)cupsArrayFirst(languages);
3199 language;
3200 language = (char *)cupsArrayNext(languages))
3201 if (language[2])
d1fc9981 3202 {
3203 /*
3204 * Lookup the base language...
3205 */
3206
d0fde099 3207 cupsArraySave(languages);
d1fc9981 3208
d0fde099 3209 strlcpy(ll, language, sizeof(ll));
d1fc9981 3210
d134307b 3211 if (!cupsArrayFind(languages, ll) &&
3212 strcmp(ll, "zh") && strcmp(ll, "en"))
d1fc9981 3213 {
3214 if (!warn && !errors && !verbose)
3215 _cupsLangPuts(stdout, _(" FAIL\n"));
3216
3217 if (verbose >= 0)
3218 _cupsLangPrintf(stdout,
3219 _(" %s No base translation \"%s\" "
3220 "is included in file!\n"), prefix, ll);
3221
3222 if (!warn)
3223 errors ++;
3224 }
3225
d0fde099 3226 cupsArrayRestore(languages);
d1fc9981 3227 }
3228
3229 /*
3230 * Free memory used for the languages...
3231 */
3232
d0fde099 3233 _ppdFreeLanguages(languages);
d1fc9981 3234 }
3235
3236 return (errors);
3237}
3238
3239
ea13a0f9 3240/*
3241 * 'show_conflicts()' - Show option conflicts in a PPD file.
3242 */
3243
0dcff045 3244static void
ea13a0f9 3245show_conflicts(ppd_file_t *ppd) /* I - PPD to check */
3246{
3247 int i, j; /* Looping variables */
3248 ppd_const_t *c; /* Current constraint */
3249 ppd_option_t *o1, *o2; /* Options */
3250 ppd_choice_t *c1, *c2; /* Choices */
3251
3252
3253 /*
3254 * Loop through all of the UI constraints and report any options
3255 * that conflict...
3256 */
3257
3258 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
3259 {
3260 /*
3261 * Grab pointers to the first option...
3262 */
3263
3264 o1 = ppdFindOption(ppd, c->option1);
3265
3266 if (o1 == NULL)
3267 continue;
3268 else if (c->choice1[0] != '\0')
3269 {
3270 /*
3271 * This constraint maps to a specific choice.
3272 */
3273
3274 c1 = ppdFindChoice(o1, c->choice1);
3275 }
3276 else
3277 {
3278 /*
3279 * This constraint applies to any choice for this option.
3280 */
3281
3282 for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
3283 if (c1->marked)
3284 break;
3285
3286 if (j == 0 ||
b7382443 3287 !strcasecmp(c1->choice, "None") ||
3288 !strcasecmp(c1->choice, "Off") ||
3289 !strcasecmp(c1->choice, "False"))
ea13a0f9 3290 c1 = NULL;
3291 }
3292
3293 /*
3294 * Grab pointers to the second option...
3295 */
3296
3297 o2 = ppdFindOption(ppd, c->option2);
3298
3299 if (o2 == NULL)
3300 continue;
3301 else if (c->choice2[0] != '\0')
3302 {
3303 /*
3304 * This constraint maps to a specific choice.
3305 */
3306
3307 c2 = ppdFindChoice(o2, c->choice2);
3308 }
3309 else
3310 {
3311 /*
3312 * This constraint applies to any choice for this option.
3313 */
3314
3315 for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
3316 if (c2->marked)
3317 break;
3318
3319 if (j == 0 ||
b7382443 3320 !strcasecmp(c2->choice, "None") ||
3321 !strcasecmp(c2->choice, "Off") ||
3322 !strcasecmp(c2->choice, "False"))
ea13a0f9 3323 c2 = NULL;
3324 }
3325
3326 /*
3327 * If both options are marked then there is a conflict...
3328 */
3329
3330 if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
89fd567e 3331 _cupsLangPrintf(stdout,
b7382443 3332 _(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
3333 " (constraint=\"%s %s %s %s\")\n"),
3334 o1->keyword, c1->choice, o2->keyword, c2->choice,
3335 c->option1, c->choice1, c->option2, c->choice2);
ea13a0f9 3336 }
3337}
3338
3339
12d95de0 3340/*
3341 * 'test_raster()' - Test PostScript commands for raster printers.
3342 */
3343
3344static int /* O - 1 on success, 0 on failure */
3345test_raster(ppd_file_t *ppd, /* I - PPD file */
3346 int verbose) /* I - Verbosity */
3347{
3348 cups_page_header2_t header; /* Page header */
3349
3350
3351 ppdMarkDefaults(ppd);
3352 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
3353 {
3354 if (!verbose)
3355 _cupsLangPuts(stdout, _(" FAIL\n"));
3356
3357 if (verbose >= 0)
3358 _cupsLangPrintf(stdout,
3359 _(" **FAIL** Default option code cannot be "
3360 "interpreted: %s\n"), cupsRasterErrorString());
3361
3362 return (0);
3363 }
decec5b9 3364
3365 /*
3366 * Try a test of custom page size code, if available...
3367 */
3368
3369 if (!ppdPageSize(ppd, "Custom.612x792"))
12d95de0 3370 return (1);
decec5b9 3371
3372 ppdMarkOption(ppd, "PageSize", "Custom.612x792");
3373
3374 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
3375 {
3376 if (!verbose)
3377 _cupsLangPuts(stdout, _(" FAIL\n"));
3378
3379 if (verbose >= 0)
3380 _cupsLangPrintf(stdout,
3381 _(" **FAIL** Default option code cannot be "
3382 "interpreted: %s\n"), cupsRasterErrorString());
3383
3384 return (0);
3385 }
3386
3387 return (1);
12d95de0 3388}
3389
3390
43cd52be 3391/*
3392 * 'usage()' - Show program usage...
3393 */
3394
0dcff045 3395static void
43cd52be 3396usage(void)
3397{
89fd567e 3398 _cupsLangPuts(stdout,
d1fc9981 3399 _("Usage: cupstestppd [options] filename1.ppd[.gz] "
3400 "[... filenameN.ppd[.gz]]\n"
3401 " program | cupstestppd [options] -\n"
3402 "\n"
3403 "Options:\n"
3404 "\n"
3405 " -R root-directory Set alternate root\n"
48c9a702 3406 " -W {all,none,constraints,defaults,duplex,filters,"
dead745e 3407 "profiles,sizes,translations}\n"
d1fc9981 3408 " Issue warnings instead of errors\n"
3409 " -q Run silently\n"
3410 " -r Use 'relaxed' open mode\n"
3411 " -v Be slightly verbose\n"
3412 " -vv Be very verbose\n"));
41b2c2e3 3413
43cd52be 3414 exit(ERROR_USAGE);
41b2c2e3 3415}
3416
3417
02aabcda 3418/*
3419 * 'valid_path()' - Check whether a path has the correct capitalization.
3420 */
3421
3422static int /* O - Errors found */
3423valid_path(const char *keyword, /* I - Keyword using path */
3424 const char *path, /* I - Path to check */
3425 int errors, /* I - Errors found */
3426 int verbose, /* I - Verbosity level */
3427 int warn) /* I - Warnings only? */
3428{
3429 cups_dir_t *dir; /* Current directory */
3430 cups_dentry_t *dentry; /* Current directory entry */
3431 char temp[1024], /* Temporary path */
3432 *ptr; /* Pointer into temporary path */
3433 const char *prefix; /* WARN/FAIL prefix */
3434
3435
3436 prefix = warn ? " WARN " : "**FAIL**";
3437
3438 /*
3439 * Loop over the components of the path, checking that the entry exists with
3440 * the same capitalization...
3441 */
3442
3443 strlcpy(temp, path, sizeof(temp));
3444
3445 while ((ptr = strrchr(temp, '/')) != NULL)
3446 {
3447 /*
3448 * Chop off the trailing component so temp == dirname and ptr == basename.
3449 */
3450
3451 *ptr++ = '\0';
3452
3453 /*
3454 * Try opening the directory containing the base name...
3455 */
3456
3457 if (temp[0])
3458 dir = cupsDirOpen(temp);
3459 else
3460 dir = cupsDirOpen("/");
3461
3462 if (!dir)
3463 dentry = NULL;
3464 else
3465 {
3466 while ((dentry = cupsDirRead(dir)) != NULL)
3467 {
3468 if (!strcmp(dentry->filename, ptr))
3469 break;
3470 }
3471
3472 cupsDirClose(dir);
3473 }
3474
3475 /*
3476 * Display an error if the filename doesn't exist with the same
3477 * capitalization...
3478 */
3479
3480 if (!dentry)
3481 {
3482 if (!warn && !errors && !verbose)
3483 _cupsLangPuts(stdout, _(" FAIL\n"));
3484
3485 if (verbose >= 0)
3486 _cupsLangPrintf(stdout,
3487 _(" %s %s file \"%s\" has the wrong "
3488 "capitalization!\n"), prefix, keyword, path);
3489
3490 if (!warn)
3491 errors ++;
3492
3493 break;
3494 }
3495 }
3496
3497 return (errors);
3498}
3499
3500
8b056e32 3501/*
3502 * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
3503 */
3504
0dcff045 3505static int /* O - 1 if valid, 0 if not */
8b056e32 3506valid_utf8(const char *s) /* I - String to check */
3507{
3508 while (*s)
3509 {
3510 if (*s & 0x80)
3511 {
3512 /*
3513 * Check for valid UTF-8 sequence...
3514 */
3515
3516 if ((*s & 0xc0) == 0x80)
3517 return (0); /* Illegal suffix byte */
3518 else if ((*s & 0xe0) == 0xc0)
3519 {
3520 /*
3521 * 2-byte sequence...
3522 */
3523
3524 s ++;
3525
3526 if ((*s & 0xc0) != 0x80)
3527 return (0); /* Missing suffix byte */
3528 }
3529 else if ((*s & 0xf0) == 0xe0)
3530 {
3531 /*
3532 * 3-byte sequence...
3533 */
3534
3535 s ++;
3536
3537 if ((*s & 0xc0) != 0x80)
3538 return (0); /* Missing suffix byte */
3539
3540 s ++;
3541
3542 if ((*s & 0xc0) != 0x80)
3543 return (0); /* Missing suffix byte */
3544 }
3545 else if ((*s & 0xf8) == 0xf0)
3546 {
3547 /*
3548 * 4-byte sequence...
3549 */
3550
3551 s ++;
3552
3553 if ((*s & 0xc0) != 0x80)
3554 return (0); /* Missing suffix byte */
3555
3556 s ++;
3557
3558 if ((*s & 0xc0) != 0x80)
3559 return (0); /* Missing suffix byte */
3560
3561 s ++;
3562
3563 if ((*s & 0xc0) != 0x80)
3564 return (0); /* Missing suffix byte */
3565 }
3566 else
3567 return (0); /* Bad sequence */
3568 }
3569
3570 s ++;
3571 }
3572
3573 return (1);
3574}
3575
3576
41b2c2e3 3577/*
c9d3f842 3578 * End of "$Id$".
41b2c2e3 3579 */