]> git.ipfire.org Git - thirdparty/cups.git/blame - systemv/cupstestppd.c
Merge changes from CUPS 1.4svn-r8414.
[thirdparty/cups.git] / systemv / cupstestppd.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: cupstestppd.c 7807 2008-07-28 21:54:24Z mike $"
ef416fc2 3 *
4 * PPD test program for the Common UNIX Printing System (CUPS).
5 *
1340db2d 6 * Copyright 2007-2009 by Apple Inc.
f7deaa1a 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14 *
15 * PostScript is a trademark of Adobe Systems, Inc.
16 *
17 * This file is subject to the Apple OS-Developed Software exception.
18 *
19 * Contents:
20 *
7a14d768
MS
21 * main() - Main entry for test program.
22 * check_basics() - Check for CR LF, mixed line endings, and blank
23 * lines.
1340db2d 24 * check_constraints() - Check UIConstraints in the PPD file.
749b1e90
MS
25 * check_case() - Check that there are no duplicate groups, options,
26 * or choices that differ only by case.
7a14d768 27 * check_defaults() - Check default option keywords in the PPD file.
749b1e90 28 * check_duplex() - Check duplex keywords in the PPD file.
7a14d768
MS
29 * check_filters() - Check filters in the PPD file.
30 * check_profiles() - Check ICC color profiles in the PPD file.
1340db2d 31 * check_sizes() - Check media sizes in the PPD file.
7a14d768
MS
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...
1340db2d 36 * valid_path() - Check whether a path has the correct capitalization.
7a14d768 37 * valid_utf8() - Check whether a string contains valid UTF-8 text.
ef416fc2 38 */
39
40/*
41 * Include necessary headers...
42 */
43
44#include <cups/string.h>
45#include <cups/cups.h>
1340db2d 46#include <cups/dir.h>
a603edef 47#include <cups/ppd-private.h>
ef416fc2 48#include <cups/i18n.h>
ac884b6a 49#include <cups/raster.h>
ef416fc2 50#include <errno.h>
51#include <stdlib.h>
1340db2d 52#include <math.h>
ef416fc2 53
54
b94498cf 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,
a603edef
MS
65 WARN_PROFILES = 8,
66 WARN_TRANSLATIONS = 16,
749b1e90 67 WARN_DUPLEX = 32,
1340db2d
MS
68 WARN_SIZES = 64,
69 WARN_ALL = 127
b94498cf 70};
71
72
ef416fc2 73/*
74 * Error codes...
75 */
76
fa73b229 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};
ef416fc2 98
99
1340db2d
MS
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
ef416fc2 262/*
263 * Local functions...
264 */
265
3d8365b8 266static void check_basics(const char *filename);
267static int check_constraints(ppd_file_t *ppd, int errors, int verbose,
268 int warn);
749b1e90 269static int check_case(ppd_file_t *ppd, int errors, int verbose);
3d8365b8 270static int check_defaults(ppd_file_t *ppd, int errors, int verbose,
271 int warn);
749b1e90
MS
272static int check_duplex(ppd_file_t *ppd, int errors, int verbose,
273 int warn);
3d8365b8 274static int check_filters(ppd_file_t *ppd, const char *root, int errors,
275 int verbose, int warn);
a603edef
MS
276static int check_profiles(ppd_file_t *ppd, const char *root, int errors,
277 int verbose, int warn);
1340db2d
MS
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,
3d8365b8 280 int warn);
281static void show_conflicts(ppd_file_t *ppd);
09a101d6 282static int test_raster(ppd_file_t *ppd, int verbose);
3d8365b8 283static void usage(void);
1340db2d
MS
284static int valid_path(const char *keyword, const char *path, int errors,
285 int verbose, int warn);
3d8365b8 286static int valid_utf8(const char *s);
ef416fc2 287
b94498cf 288
ef416fc2 289/*
290 * 'main()' - Main entry for test program.
291 */
292
323c5de1 293int /* O - Exit status */
294main(int argc, /* I - Number of command-line args */
295 char *argv[]) /* I - Command-line arguments */
ef416fc2 296{
323c5de1 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? */
b94498cf 303 int warn; /* Which errors to just warn about */
323c5de1 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 */
a603edef 309 char *root; /* Root directory */
323c5de1 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 */
dd1abb6b 320 struct lconv *loc; /* Locale data */
ef416fc2 321 static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
322 static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
323 "JCL", "PAGE", "PROLOG" };
324
325
07725fee 326 _cupsSetLocale(argv);
dd1abb6b 327 loc = localeconv();
d09495fa 328
ef416fc2 329 /*
330 * Display PPD files for each file listed on the command-line...
331 */
332
333 ppdSetConformance(PPD_CONFORM_STRICT);
334
335 verbose = 0;
336 ppd = NULL;
337 files = 0;
338 status = ERROR_NONE;
323c5de1 339 root = "";
b94498cf 340 warn = WARN_NONE;
ef416fc2 341
342 for (i = 1; i < argc; i ++)
343 if (argv[i][0] == '-' && argv[i][1])
344 {
345 for (opt = argv[i] + 1; *opt; opt ++)
346 switch (*opt)
347 {
323c5de1 348 case 'R' : /* Alternate root directory */
349 i ++;
350
351 if (i >= argc)
352 usage();
353
354 root = argv[i];
355 break;
356
b94498cf 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;
749b1e90
MS
369 else if (!strcmp(argv[i], "duplex"))
370 warn |= WARN_DUPLEX;
b94498cf 371 else if (!strcmp(argv[i], "filters"))
372 warn |= WARN_FILTERS;
a603edef
MS
373 else if (!strcmp(argv[i], "profiles"))
374 warn |= WARN_PROFILES;
1340db2d
MS
375 else if (!strcmp(argv[i], "sizes"))
376 warn |= WARN_SIZES;
b94498cf 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
ef416fc2 385 case 'q' : /* Quiet mode */
386 if (verbose > 0)
387 {
fa73b229 388 _cupsLangPuts(stderr,
ef416fc2 389 _("cupstestppd: The -q option is incompatible "
390 "with the -v option.\n"));
391 return (1);
392 }
393
394 verbose --;
395 break;
396
397 case 'r' : /* Relaxed mode */
398 ppdSetConformance(PPD_CONFORM_RELAXED);
399 break;
400
401 case 'v' : /* Verbose mode */
402 if (verbose < 0)
403 {
fa73b229 404 _cupsLangPuts(stderr,
ef416fc2 405 _("cupstestppd: The -v option is incompatible "
406 "with the -q option.\n"));
407 return (1);
408 }
409
410 verbose ++;
411 break;
412
413 default :
414 usage();
415 break;
416 }
417 }
418 else
419 {
420 /*
421 * Open the PPD file...
422 */
423
424 if (files && verbose >= 0)
fa73b229 425 _cupsLangPuts(stdout, "\n");
ef416fc2 426
427 files ++;
428
429 if (argv[i][0] == '-')
430 {
431 /*
432 * Read from stdin...
433 */
434
ef416fc2 435 ppd = ppdOpen(stdin);
bdd6c45b
MS
436
437 if (verbose >= 0)
438 printf("%s:", (ppd && ppd->pcfilename) ? ppd->pcfilename : "(stdin)");
ef416fc2 439 }
440 else
441 {
442 /*
443 * Read from a file...
444 */
445
446 if (verbose >= 0)
447 printf("%s:", argv[i]);
448
449 ppd = ppdOpenFile(argv[i]);
450 }
451
452 if (ppd == NULL)
453 {
454 error = ppdLastError(&line);
455
456 if (error <= PPD_ALLOC_ERROR)
457 {
458 status = ERROR_FILE_OPEN;
459
460 if (verbose >= 0)
fa73b229 461 _cupsLangPrintf(stdout,
ef416fc2 462 _(" FAIL\n"
463 " **FAIL** Unable to open PPD file - %s\n"),
464 strerror(errno));
465 }
466 else
467 {
468 status = ERROR_PPD_FORMAT;
469
470 if (verbose >= 0)
471 {
fa73b229 472 _cupsLangPrintf(stdout,
ef416fc2 473 _(" FAIL\n"
474 " **FAIL** Unable to open PPD file - "
475 "%s on line %d.\n"),
476 ppdErrorString(error), line);
477
478 switch (error)
479 {
480 case PPD_MISSING_PPDADOBE4 :
fa73b229 481 _cupsLangPuts(stdout,
ef416fc2 482 _(" REF: Page 42, section 5.2.\n"));
483 break;
484 case PPD_MISSING_VALUE :
fa73b229 485 _cupsLangPuts(stdout,
ef416fc2 486 _(" REF: Page 20, section 3.4.\n"));
487 break;
488 case PPD_BAD_OPEN_GROUP :
489 case PPD_NESTED_OPEN_GROUP :
fa73b229 490 _cupsLangPuts(stdout,
ef416fc2 491 _(" REF: Pages 45-46, section 5.2.\n"));
492 break;
493 case PPD_BAD_OPEN_UI :
494 case PPD_NESTED_OPEN_UI :
fa73b229 495 _cupsLangPuts(stdout,
ef416fc2 496 _(" REF: Pages 42-45, section 5.2.\n"));
497 break;
498 case PPD_BAD_ORDER_DEPENDENCY :
fa73b229 499 _cupsLangPuts(stdout,
ef416fc2 500 _(" REF: Pages 48-49, section 5.2.\n"));
501 break;
502 case PPD_BAD_UI_CONSTRAINTS :
fa73b229 503 _cupsLangPuts(stdout,
ef416fc2 504 _(" REF: Pages 52-54, section 5.2.\n"));
505 break;
506 case PPD_MISSING_ASTERISK :
fa73b229 507 _cupsLangPuts(stdout,
ef416fc2 508 _(" REF: Page 15, section 3.2.\n"));
509 break;
510 case PPD_LINE_TOO_LONG :
fa73b229 511 _cupsLangPuts(stdout,
ef416fc2 512 _(" REF: Page 15, section 3.1.\n"));
513 break;
514 case PPD_ILLEGAL_CHARACTER :
fa73b229 515 _cupsLangPuts(stdout,
ef416fc2 516 _(" REF: Page 15, section 3.1.\n"));
517 break;
518 case PPD_ILLEGAL_MAIN_KEYWORD :
fa73b229 519 _cupsLangPuts(stdout,
ef416fc2 520 _(" REF: Pages 16-17, section 3.2.\n"));
521 break;
522 case PPD_ILLEGAL_OPTION_KEYWORD :
fa73b229 523 _cupsLangPuts(stdout,
ef416fc2 524 _(" REF: Page 19, section 3.3.\n"));
525 break;
526 case PPD_ILLEGAL_TRANSLATION :
fa73b229 527 _cupsLangPuts(stdout,
ef416fc2 528 _(" REF: Page 27, section 3.5.\n"));
529 break;
530 default :
531 break;
532 }
fa73b229 533
534 check_basics(argv[i]);
ef416fc2 535 }
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
546 errors = 0;
547 ppdversion = 43;
548
549 if (verbose > 0)
fa73b229 550 _cupsLangPuts(stdout,
ef416fc2 551 _("\n DETAILED CONFORMANCE TEST RESULTS\n"));
552
553 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
554 attr->value)
dd1abb6b 555 ppdversion = (int)(10 * _cupsStrScand(attr->value, NULL, loc) + 0.5);
ef416fc2 556
09a101d6 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
b423cd4c 565 /*
566 * Look for default keywords with no matching option...
567 */
568
b94498cf 569 if (!(warn & WARN_DEFAULTS))
570 errors = check_defaults(ppd, errors, verbose, 0);
ef416fc2 571
572 if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
573 {
574 if (verbose >= 0)
575 {
576 if (!errors && !verbose)
fa73b229 577 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 578
fa73b229 579 _cupsLangPuts(stdout,
b94498cf 580 _(" **FAIL** REQUIRED DefaultImageableArea\n"
ef416fc2 581 " REF: Page 102, section 5.15.\n"));
b94498cf 582 }
ef416fc2 583
584 errors ++;
585 }
586 else if (ppdPageSize(ppd, attr->value) == NULL &&
b94498cf 587 strcmp(attr->value, "Unknown"))
ef416fc2 588 {
589 if (verbose >= 0)
590 {
591 if (!errors && !verbose)
fa73b229 592 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 593
fa73b229 594 _cupsLangPrintf(stdout,
b94498cf 595 _(" **FAIL** BAD DefaultImageableArea %s!\n"
ef416fc2 596 " REF: Page 102, section 5.15.\n"),
597 attr->value);
b94498cf 598 }
ef416fc2 599
600 errors ++;
601 }
602 else
603 {
604 if (verbose > 0)
fa73b229 605 _cupsLangPuts(stdout, _(" PASS DefaultImageableArea\n"));
ef416fc2 606 }
607
608 if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
609 {
610 if (verbose >= 0)
611 {
612 if (!errors && !verbose)
fa73b229 613 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 614
fa73b229 615 _cupsLangPuts(stdout,
b94498cf 616 _(" **FAIL** REQUIRED DefaultPaperDimension\n"
ef416fc2 617 " REF: Page 103, section 5.15.\n"));
b94498cf 618 }
ef416fc2 619
620 errors ++;
621 }
622 else if (ppdPageSize(ppd, attr->value) == NULL &&
b94498cf 623 strcmp(attr->value, "Unknown"))
ef416fc2 624 {
625 if (verbose >= 0)
626 {
627 if (!errors && !verbose)
fa73b229 628 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 629
fa73b229 630 _cupsLangPrintf(stdout,
b94498cf 631 _(" **FAIL** BAD DefaultPaperDimension %s!\n"
ef416fc2 632 " REF: Page 103, section 5.15.\n"),
633 attr->value);
b94498cf 634 }
ef416fc2 635
636 errors ++;
637 }
638 else if (verbose > 0)
fa73b229 639 _cupsLangPuts(stdout, _(" PASS DefaultPaperDimension\n"));
ef416fc2 640
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 ++)
643 {
644 /*
645 * Verify that we have a default choice...
646 */
647
648 if (option->defchoice[0])
649 {
b94498cf 650 if (ppdFindChoice(option, option->defchoice) == NULL &&
651 strcmp(option->defchoice, "Unknown"))
ef416fc2 652 {
653 if (verbose >= 0)
654 {
655 if (!errors && !verbose)
fa73b229 656 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 657
fa73b229 658 _cupsLangPrintf(stdout,
b94498cf 659 _(" **FAIL** BAD Default%s %s\n"
ef416fc2 660 " REF: Page 40, section 4.5.\n"),
661 option->keyword, option->defchoice);
b94498cf 662 }
ef416fc2 663
664 errors ++;
665 }
666 else if (verbose > 0)
fa73b229 667 _cupsLangPrintf(stdout,
b94498cf 668 _(" PASS Default%s\n"),
ef416fc2 669 option->keyword);
670 }
671 else
672 {
673 if (verbose >= 0)
674 {
675 if (!errors && !verbose)
fa73b229 676 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 677
fa73b229 678 _cupsLangPrintf(stdout,
b94498cf 679 _(" **FAIL** REQUIRED Default%s\n"
680 " REF: Page 40, section 4.5.\n"),
681 option->keyword);
682 }
ef416fc2 683
684 errors ++;
685 }
686 }
687
557dde9f 688 if ((attr = ppdFindAttr(ppd, "FileVersion", NULL)) != NULL)
ef416fc2 689 {
557dde9f
MS
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)
fa73b229 710 _cupsLangPuts(stdout, _(" PASS FileVersion\n"));
ef416fc2 711 }
712 else
713 {
714 if (verbose >= 0)
715 {
716 if (!errors && !verbose)
fa73b229 717 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 718
fa73b229 719 _cupsLangPuts(stdout,
ef416fc2 720 _(" **FAIL** REQUIRED FileVersion\n"
721 " REF: Page 56, section 5.3.\n"));
722 }
723
724 errors ++;
725 }
726
557dde9f 727 if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL)
ef416fc2 728 {
557dde9f
MS
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)
fa73b229 754 _cupsLangPuts(stdout, _(" PASS FormatVersion\n"));
ef416fc2 755 }
756 else
757 {
758 if (verbose >= 0)
759 {
760 if (!errors && !verbose)
fa73b229 761 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 762
fa73b229 763 _cupsLangPuts(stdout,
ef416fc2 764 _(" **FAIL** REQUIRED FormatVersion\n"
765 " REF: Page 56, section 5.3.\n"));
766 }
767
768 errors ++;
769 }
770
771 if (ppd->lang_encoding != NULL)
772 {
773 if (verbose > 0)
fa73b229 774 _cupsLangPuts(stdout, _(" PASS LanguageEncoding\n"));
ef416fc2 775 }
776 else if (ppdversion > 40)
777 {
778 if (verbose >= 0)
779 {
780 if (!errors && !verbose)
fa73b229 781 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 782
fa73b229 783 _cupsLangPuts(stdout,
ef416fc2 784 _(" **FAIL** REQUIRED LanguageEncoding\n"
785 " REF: Pages 56-57, section 5.3.\n"));
786 }
787
788 errors ++;
789 }
790
791 if (ppd->lang_version != NULL)
792 {
793 if (verbose > 0)
fa73b229 794 _cupsLangPuts(stdout, _(" PASS LanguageVersion\n"));
ef416fc2 795 }
796 else
797 {
798 if (verbose >= 0)
799 {
800 if (!errors && !verbose)
fa73b229 801 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 802
fa73b229 803 _cupsLangPuts(stdout,
ef416fc2 804 _(" **FAIL** REQUIRED LanguageVersion\n"
805 " REF: Pages 57-58, section 5.3.\n"));
806 }
807
808 errors ++;
809 }
810
811 if (ppd->manufacturer != NULL)
812 {
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)
fa73b229 819 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 820
fa73b229 821 _cupsLangPuts(stdout,
ef416fc2 822 _(" **FAIL** BAD Manufacturer (should be "
823 "\"HP\")\n"
824 " REF: Page 211, table D.1.\n"));
825 }
826
827 errors ++;
828 }
b94498cf 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 }
ef416fc2 845 else if (verbose > 0)
fa73b229 846 _cupsLangPuts(stdout, _(" PASS Manufacturer\n"));
ef416fc2 847 }
848 else if (ppdversion >= 43)
849 {
850 if (verbose >= 0)
851 {
852 if (!errors && !verbose)
fa73b229 853 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 854
fa73b229 855 _cupsLangPuts(stdout,
ef416fc2 856 _(" **FAIL** REQUIRED Manufacturer\n"
857 " REF: Pages 58-59, section 5.3.\n"));
858 }
859
860 errors ++;
861 }
862
863 if (ppd->modelname != NULL)
864 {
865 for (ptr = ppd->modelname; *ptr; ptr ++)
866 if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr))
867 break;
868
869 if (*ptr)
870 {
871 if (verbose >= 0)
872 {
873 if (!errors && !verbose)
fa73b229 874 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 875
fa73b229 876 _cupsLangPrintf(stdout,
ef416fc2 877 _(" **FAIL** BAD ModelName - \"%c\" not "
878 "allowed in string.\n"
879 " REF: Pages 59-60, section 5.3.\n"),
880 *ptr);
881 }
882
883 errors ++;
884 }
885 else if (verbose > 0)
fa73b229 886 _cupsLangPuts(stdout, _(" PASS ModelName\n"));
ef416fc2 887 }
888 else
889 {
890 if (verbose >= 0)
891 {
892 if (!errors && !verbose)
fa73b229 893 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 894
fa73b229 895 _cupsLangPuts(stdout,
ef416fc2 896 _(" **FAIL** REQUIRED ModelName\n"
897 " REF: Pages 59-60, section 5.3.\n"));
898 }
899
900 errors ++;
901 }
902
903 if (ppd->nickname != NULL)
904 {
905 if (verbose > 0)
fa73b229 906 _cupsLangPuts(stdout, _(" PASS NickName\n"));
ef416fc2 907 }
908 else
909 {
910 if (verbose >= 0)
911 {
912 if (!errors && !verbose)
fa73b229 913 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 914
fa73b229 915 _cupsLangPuts(stdout,
ef416fc2 916 _(" **FAIL** REQUIRED NickName\n"
917 " REF: Page 60, section 5.3.\n"));
918 }
919
920 errors ++;
921 }
922
923 if (ppdFindOption(ppd, "PageSize") != NULL)
924 {
925 if (verbose > 0)
fa73b229 926 _cupsLangPuts(stdout, _(" PASS PageSize\n"));
ef416fc2 927 }
928 else
929 {
930 if (verbose >= 0)
931 {
932 if (!errors && !verbose)
fa73b229 933 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 934
fa73b229 935 _cupsLangPuts(stdout,
ef416fc2 936 _(" **FAIL** REQUIRED PageSize\n"
937 " REF: Pages 99-100, section 5.14.\n"));
938 }
939
940 errors ++;
941 }
942
943 if (ppdFindOption(ppd, "PageRegion") != NULL)
944 {
945 if (verbose > 0)
fa73b229 946 _cupsLangPuts(stdout, _(" PASS PageRegion\n"));
ef416fc2 947 }
948 else
949 {
950 if (verbose >= 0)
951 {
952 if (!errors && !verbose)
fa73b229 953 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 954
fa73b229 955 _cupsLangPuts(stdout,
ef416fc2 956 _(" **FAIL** REQUIRED PageRegion\n"
957 " REF: Page 100, section 5.14.\n"));
958 }
959
960 errors ++;
961 }
962
963 if (ppd->pcfilename != NULL)
964 {
965 if (verbose > 0)
fa73b229 966 _cupsLangPuts(stdout, _(" PASS PCFileName\n"));
ef416fc2 967 }
968 else
969 {
970 if (verbose >= 0)
971 {
972 if (!errors && !verbose)
fa73b229 973 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 974
fa73b229 975 _cupsLangPuts(stdout,
ef416fc2 976 _(" **FAIL** REQUIRED PCFileName\n"
977 " REF: Pages 61-62, section 5.3.\n"));
978 }
979
980 errors ++;
981 }
982
983 if (ppd->product != NULL)
984 {
985 if (ppd->product[0] != '(' ||
986 ppd->product[strlen(ppd->product) - 1] != ')')
987 {
988 if (verbose >= 0)
989 {
990 if (!errors && !verbose)
fa73b229 991 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 992
fa73b229 993 _cupsLangPuts(stdout,
ef416fc2 994 _(" **FAIL** BAD Product - not \"(string)\".\n"
995 " REF: Page 62, section 5.3.\n"));
996 }
997
998 errors ++;
999 }
1000 else if (verbose > 0)
fa73b229 1001 _cupsLangPuts(stdout, _(" PASS Product\n"));
ef416fc2 1002 }
1003 else
1004 {
1005 if (verbose >= 0)
1006 {
1007 if (!errors && !verbose)
fa73b229 1008 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1009
fa73b229 1010 _cupsLangPuts(stdout,
ef416fc2 1011 _(" **FAIL** REQUIRED Product\n"
1012 " REF: Page 62, section 5.3.\n"));
1013 }
1014
1015 errors ++;
1016 }
1017
1018 if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
1019 attr->value != NULL)
1020 {
1021 char junkstr[255]; /* Temp string */
1022 int junkint; /* Temp integer */
1023
1024
1025 if (sscanf(attr->value, "(%[^)])%d", junkstr, &junkint) != 2)
1026 {
1027 if (verbose >= 0)
1028 {
1029 if (!errors && !verbose)
fa73b229 1030 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1031
fa73b229 1032 _cupsLangPuts(stdout,
ef416fc2 1033 _(" **FAIL** BAD PSVersion - not \"(string) "
1034 "int\".\n"
1035 " REF: Pages 62-64, section 5.3.\n"));
1036 }
1037
1038 errors ++;
1039 }
1040 else if (verbose > 0)
fa73b229 1041 _cupsLangPuts(stdout, _(" PASS PSVersion\n"));
ef416fc2 1042 }
1043 else
1044 {
1045 if (verbose >= 0)
1046 {
1047 if (!errors && !verbose)
fa73b229 1048 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1049
fa73b229 1050 _cupsLangPuts(stdout,
ef416fc2 1051 _(" **FAIL** REQUIRED PSVersion\n"
1052 " REF: Pages 62-64, section 5.3.\n"));
1053 }
1054
1055 errors ++;
1056 }
1057
1058 if (ppd->shortnickname != NULL)
1059 {
1060 if (strlen(ppd->shortnickname) > 31)
1061 {
1062 if (verbose >= 0)
1063 {
1064 if (!errors && !verbose)
fa73b229 1065 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1066
fa73b229 1067 _cupsLangPuts(stdout,
ef416fc2 1068 _(" **FAIL** BAD ShortNickName - longer "
1069 "than 31 chars.\n"
1070 " REF: Pages 64-65, section 5.3.\n"));
1071 }
1072
1073 errors ++;
1074 }
1075 else if (verbose > 0)
fa73b229 1076 _cupsLangPuts(stdout, _(" PASS ShortNickName\n"));
ef416fc2 1077 }
1078 else if (ppdversion >= 43)
1079 {
1080 if (verbose >= 0)
1081 {
1082 if (!errors && !verbose)
fa73b229 1083 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1084
fa73b229 1085 _cupsLangPuts(stdout,
ef416fc2 1086 _(" **FAIL** REQUIRED ShortNickName\n"
1087 " REF: Page 64-65, section 5.3.\n"));
1088 }
1089
1090 errors ++;
1091 }
1092
1093 if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
1094 strstr(ppd->patches, "*End"))
1095 {
1096 if (verbose >= 0)
1097 {
1098 if (!errors && !verbose)
fa73b229 1099 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1100
fa73b229 1101 _cupsLangPuts(stdout,
ef416fc2 1102 _(" **FAIL** BAD JobPatchFile attribute in file\n"
1103 " REF: Page 24, section 3.4.\n"));
1104 }
1105
1106 errors ++;
1107 }
1108
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)
fa73b229 1119 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1120
fa73b229 1121 _cupsLangPuts(stdout,
ef416fc2 1122 _(" **FAIL** REQUIRED PageSize\n"
1123 " REF: Page 41, section 5.\n"
1124 " REF: Page 99, section 5.14.\n"));
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)
fa73b229 1150 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1151
fa73b229 1152 _cupsLangPrintf(stdout,
ef416fc2 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);
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)
fa73b229 1172 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1173
fa73b229 1174 _cupsLangPrintf(stdout,
ef416fc2 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);
1180 }
1181
1182 errors ++;
1183 }
1184 }
1185 }
1186
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 }
1212 else
1213 ydpi = xdpi;
1214
db1f069b
MS
1215 if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 ||
1216 strcmp(ptr, "dpi"))
ef416fc2 1217 {
1218 if (verbose >= 0)
1219 {
1220 if (!errors && !verbose)
fa73b229 1221 _cupsLangPuts(stdout, _(" FAIL\n"));
ef416fc2 1222
fa73b229 1223 _cupsLangPrintf(stdout,
ef416fc2 1224 _(" **FAIL** Bad %s choice %s!\n"
1225 " REF: Page 84, section 5.9\n"),
1226 option->keyword, choice->choice);
1227 }
1228
1229 errors ++;
1230 }
1231 }
1232 }
1233
b94498cf 1234 if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
1235 strcmp(attr->name, "1284DeviceID"))
b423cd4c 1236 {
b94498cf 1237 if (verbose >= 0)
1238 {
1239 if (!errors && !verbose)
1240 _cupsLangPuts(stdout, _(" FAIL\n"));
b423cd4c 1241
b94498cf 1242 _cupsLangPrintf(stdout,
1243 _(" **FAIL** %s must be 1284DeviceID!\n"
1244 " REF: Page 72, section 5.5\n"),
1245 attr->name);
1246 }
b423cd4c 1247
b94498cf 1248 errors ++;
1249 }
b423cd4c 1250
749b1e90
MS
1251 errors = check_case(ppd, errors, verbose);
1252
b94498cf 1253 if (!(warn & WARN_CONSTRAINTS))
1254 errors = check_constraints(ppd, errors, verbose, 0);
c0e1af83 1255
b94498cf 1256 if (!(warn & WARN_FILTERS))
1257 errors = check_filters(ppd, root, errors, verbose, 0);
b423cd4c 1258
a603edef
MS
1259 if (!(warn & WARN_PROFILES))
1260 errors = check_profiles(ppd, root, errors, verbose, 0);
1261
1340db2d
MS
1262 if (!(warn & WARN_SIZES))
1263 errors = check_sizes(ppd, errors, verbose, 0);
1264
b94498cf 1265 if (!(warn & WARN_TRANSLATIONS))
1266 errors = check_translations(ppd, errors, verbose, 0);
b423cd4c 1267
749b1e90
MS
1268 if (!(warn & WARN_DUPLEX))
1269 errors = check_duplex(ppd, errors, verbose, 0);
1270
b94498cf 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 */
b423cd4c 1278
b94498cf 1279 if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL)
1280 {
1281 if (!attr->value || strcmp(attr->value, "ISOLatin1"))
1282 {
1283 if (!errors && !verbose)
1284 _cupsLangPuts(stdout, _(" FAIL\n"));
b423cd4c 1285
b94498cf 1286 if (verbose >= 0)
bc44d920 1287 _cupsLangPrintf(stdout,
b94498cf 1288 _(" **FAIL** Bad LanguageEncoding %s - "
1289 "must be ISOLatin1!\n"),
1290 attr->value ? attr->value : "(null)");
b423cd4c 1291
b94498cf 1292 errors ++;
1293 }
b423cd4c 1294
b94498cf 1295 if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
b423cd4c 1296 {
b94498cf 1297 if (!errors && !verbose)
1298 _cupsLangPuts(stdout, _(" FAIL\n"));
b423cd4c 1299
b94498cf 1300 if (verbose >= 0)
bc44d920 1301 _cupsLangPrintf(stdout,
b94498cf 1302 _(" **FAIL** Bad LanguageVersion %s - "
1303 "must be English!\n"),
1304 ppd->lang_version ? ppd->lang_version : "(null)");
b423cd4c 1305
b94498cf 1306 errors ++;
b423cd4c 1307 }
b94498cf 1308
1309 /*
b423cd4c 1310 * Loop through all options and choices...
1311 */
1312
b94498cf 1313 for (option = ppdFirstOption(ppd);
b423cd4c 1314 option;
1315 option = ppdNextOption(ppd))
1316 {
b94498cf 1317 /*
1318 * Check for special characters outside A0 to BF, F7, or F8
1319 * that are used for languages other than English.
1320 */
b423cd4c 1321
b94498cf 1322 for (ptr = option->text; *ptr; ptr ++)
1323 if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
1324 (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
1325 break;
b423cd4c 1326
b94498cf 1327 if (*ptr)
f7deaa1a 1328 {
b94498cf 1329 if (!errors && !verbose)
1330 _cupsLangPuts(stdout, _(" FAIL\n"));
b423cd4c 1331
b423cd4c 1332 if (verbose >= 0)
b423cd4c 1333 _cupsLangPrintf(stdout,
b94498cf 1334 _(" **FAIL** Default translation "
b423cd4c 1335 "string for option %s contains 8-bit "
1336 "characters!\n"),
1337 option->keyword);
b423cd4c 1338
1339 errors ++;
1340 }
1341
b94498cf 1342 for (j = 0; j < option->num_choices; j ++)
b423cd4c 1343 {
b94498cf 1344 /*
1345 * Check for special characters outside A0 to BF, F7, or F8
1346 * that are used for languages other than English.
1347 */
d09495fa 1348
b94498cf 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;
b423cd4c 1353
b94498cf 1354 if (*ptr)
f7deaa1a 1355 {
b94498cf 1356 if (!errors && !verbose)
1357 _cupsLangPuts(stdout, _(" FAIL\n"));
b423cd4c 1358
b423cd4c 1359 if (verbose >= 0)
b423cd4c 1360 _cupsLangPrintf(stdout,
b94498cf 1361 _(" **FAIL** Default translation "
b423cd4c 1362 "string for option %s choice %s contains "
1363 "8-bit characters!\n"),
1364 option->keyword,
1365 option->choices[j].choice);
b423cd4c 1366
1367 errors ++;
1368 }
b94498cf 1369 }
b423cd4c 1370 }
b94498cf 1371 }
1372 }
c0e1af83 1373
b94498cf 1374 /*
1375 * Final pass/fail notification...
1376 */
c0e1af83 1377
b94498cf 1378 if (errors)
1379 status = ERROR_CONFORMANCE;
1380 else if (!verbose)
1381 _cupsLangPuts(stdout, _(" PASS\n"));
c0e1af83 1382
b94498cf 1383 if (verbose >= 0)
1384 {
1385 check_basics(argv[i]);
c0e1af83 1386
b94498cf 1387 if (warn & WARN_CONSTRAINTS)
1388 errors = check_constraints(ppd, errors, verbose, 1);
c0e1af83 1389
b94498cf 1390 if (warn & WARN_DEFAULTS)
1391 errors = check_defaults(ppd, errors, verbose, 1);
c0e1af83 1392
a603edef
MS
1393 if (warn & WARN_PROFILES)
1394 errors = check_profiles(ppd, root, errors, verbose, 1);
1395
b94498cf 1396 if (warn & WARN_FILTERS)
1397 errors = check_filters(ppd, root, errors, verbose, 1);
c0e1af83 1398
1340db2d
MS
1399 if (warn & WARN_SIZES)
1400 errors = check_sizes(ppd, errors, verbose, 1);
1401 else
1402 errors = check_sizes(ppd, errors, verbose, 2);
1403
b94498cf 1404 if (warn & WARN_TRANSLATIONS)
1405 errors = check_translations(ppd, errors, verbose, 1);
c0e1af83 1406
749b1e90
MS
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
c0e1af83 1425 /*
b94498cf 1426 * Look for default keywords with no corresponding option...
c0e1af83 1427 */
1428
b94498cf 1429 for (j = 0; j < ppd->num_attrs; j ++)
1430 {
1431 attr = ppd->attrs[j];
323c5de1 1432
b94498cf 1433 if (!strcmp(attr->name, "DefaultColorSpace") ||
1434 !strcmp(attr->name, "DefaultColorSep") ||
1435 !strcmp(attr->name, "DefaultFont") ||
09a101d6 1436 !strcmp(attr->name, "DefaultHalftoneType") ||
b94498cf 1437 !strcmp(attr->name, "DefaultImageableArea") ||
09a101d6 1438 !strcmp(attr->name, "DefaultLeadingEdge") ||
b94498cf 1439 !strcmp(attr->name, "DefaultOutputOrder") ||
1440 !strcmp(attr->name, "DefaultPaperDimension") ||
09a101d6 1441 !strcmp(attr->name, "DefaultResolution") ||
b94498cf 1442 !strcmp(attr->name, "DefaultScreenProc") ||
1443 !strcmp(attr->name, "DefaultTransfer"))
1444 continue;
b423cd4c 1445
b94498cf 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);
323c5de1 1452 }
323c5de1 1453
ef416fc2 1454 ppdMarkDefaults(ppd);
1455 if (ppdConflicts(ppd))
1456 {
fa73b229 1457 _cupsLangPuts(stdout,
ef416fc2 1458 _(" WARN Default choices conflicting!\n"));
1459
1460 show_conflicts(ppd);
1461 }
1462
1463 if (ppdversion < 43)
1464 {
fa73b229 1465 _cupsLangPrintf(stdout,
ef416fc2 1466 _(" WARN Obsolete PPD version %.1f!\n"
1467 " REF: Page 42, section 5.2.\n"),
1468 0.1f * ppdversion);
1469 }
1470
1471 if (!ppd->lang_encoding && ppdversion < 41)
1472 {
fa73b229 1473 _cupsLangPuts(stdout,
ef416fc2 1474 _(" WARN LanguageEncoding required by PPD "
1475 "4.3 spec.\n"
1476 " REF: Pages 56-57, section 5.3.\n"));
1477 }
1478
1479 if (!ppd->manufacturer && ppdversion < 43)
1480 {
fa73b229 1481 _cupsLangPuts(stdout,
ef416fc2 1482 _(" WARN Manufacturer required by PPD "
1483 "4.3 spec.\n"
1484 " REF: Pages 58-59, section 5.3.\n"));
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 {
fa73b229 1494 _cupsLangPuts(stdout,
ef416fc2 1495 _(" WARN PCFileName longer than 8.3 in "
1496 "violation of PPD spec.\n"
1497 " REF: Pages 61-62, section 5.3.\n"));
1498 }
1499
1500 if (!ppd->shortnickname && ppdversion < 43)
1501 {
fa73b229 1502 _cupsLangPuts(stdout,
ef416fc2 1503 _(" WARN ShortNickName required by PPD "
1504 "4.3 spec.\n"
1505 " REF: Pages 64-65, section 5.3.\n"));
1506 }
1507
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 {
fa73b229 1519 _cupsLangPuts(stdout,
ef416fc2 1520 _(" WARN Protocols contains both PJL "
1521 "and BCP; expected TBCP.\n"
1522 " REF: Pages 78-79, section 5.7.\n"));
1523 }
1524
1525 if (strstr(ppd->protocols, "PJL") &&
1526 (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
1527 {
fa73b229 1528 _cupsLangPuts(stdout,
ef416fc2 1529 _(" WARN Protocols contains PJL but JCL "
1530 "attributes are not set.\n"
1531 " REF: Pages 78-79, section 5.7.\n"));
1532 }
1533 }
1534
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 {
fa73b229 1556 _cupsLangPrintf(stdout,
ef416fc2 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);
1562 }
1563 }
1564 }
1565
1566 if (verbose > 0)
1567 {
1568 if (errors)
f7deaa1a 1569 _cupsLangPrintf(stdout, _(" %d ERRORS FOUND\n"), errors);
ef416fc2 1570 else
fa73b229 1571 _cupsLangPuts(stdout, _(" NO ERRORS FOUND\n"));
ef416fc2 1572 }
1573
ef416fc2 1574 /*
1575 * Then list the options, if "-v" was provided...
1576 */
1577
1578 if (verbose > 1)
1579 {
fa73b229 1580 _cupsLangPrintf(stdout,
ef416fc2 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);
1590
1591 switch (ppd->colorspace)
1592 {
1593 case PPD_CS_CMYK :
fa73b229 1594 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMYK\n");
ef416fc2 1595 break;
1596 case PPD_CS_CMY :
fa73b229 1597 _cupsLangPuts(stdout, " colorspace = PPD_CS_CMY\n");
ef416fc2 1598 break;
1599 case PPD_CS_GRAY :
fa73b229 1600 _cupsLangPuts(stdout, " colorspace = PPD_CS_GRAY\n");
ef416fc2 1601 break;
1602 case PPD_CS_RGB :
fa73b229 1603 _cupsLangPuts(stdout, " colorspace = PPD_CS_RGB\n");
ef416fc2 1604 break;
1605 default :
fa73b229 1606 _cupsLangPuts(stdout, " colorspace = <unknown>\n");
ef416fc2 1607 break;
1608 }
1609
fa73b229 1610 _cupsLangPrintf(stdout, " num_emulations = %d\n",
ef416fc2 1611 ppd->num_emulations);
1612 for (j = 0; j < ppd->num_emulations; j ++)
fa73b229 1613 _cupsLangPrintf(stdout, " emulations[%d] = %s\n",
ef416fc2 1614 j, ppd->emulations[j].name);
1615
fa73b229 1616 _cupsLangPrintf(stdout, " lang_encoding = %s\n",
ef416fc2 1617 ppd->lang_encoding);
fa73b229 1618 _cupsLangPrintf(stdout, " lang_version = %s\n",
ef416fc2 1619 ppd->lang_version);
fa73b229 1620 _cupsLangPrintf(stdout, " modelname = %s\n", ppd->modelname);
1621 _cupsLangPrintf(stdout, " ttrasterizer = %s\n",
ef416fc2 1622 ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
fa73b229 1623 _cupsLangPrintf(stdout, " manufacturer = %s\n",
ef416fc2 1624 ppd->manufacturer);
fa73b229 1625 _cupsLangPrintf(stdout, " product = %s\n", ppd->product);
1626 _cupsLangPrintf(stdout, " nickname = %s\n", ppd->nickname);
1627 _cupsLangPrintf(stdout, " shortnickname = %s\n",
ef416fc2 1628 ppd->shortnickname);
fa73b229 1629 _cupsLangPrintf(stdout, " patches = %d bytes\n",
ef416fc2 1630 ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
1631
fa73b229 1632 _cupsLangPrintf(stdout, " num_groups = %d\n", ppd->num_groups);
ef416fc2 1633 for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
1634 {
fa73b229 1635 _cupsLangPrintf(stdout, " group[%d] = %s\n",
ef416fc2 1636 j, group->text);
1637
1638 for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
1639 {
fa73b229 1640 _cupsLangPrintf(stdout,
ef416fc2 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"))
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)
fa73b229 1657 _cupsLangPrintf(stdout,
ef416fc2 1658 " %s (%s) = ERROR",
1659 choice->choice, choice->text);
1660 else
fa73b229 1661 _cupsLangPrintf(stdout,
ef416fc2 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))
fa73b229 1670 _cupsLangPuts(stdout, " *\n");
ef416fc2 1671 else
fa73b229 1672 _cupsLangPuts(stdout, "\n");
ef416fc2 1673 }
1674 }
1675 else
1676 {
1677 for (m = option->num_choices, choice = option->choices;
1678 m > 0;
1679 m --, choice ++)
1680 {
fa73b229 1681 _cupsLangPrintf(stdout, " %s (%s)",
ef416fc2 1682 choice->choice, choice->text);
1683
1684 if (!strcmp(option->defchoice, choice->choice))
fa73b229 1685 _cupsLangPuts(stdout, " *\n");
ef416fc2 1686 else
fa73b229 1687 _cupsLangPuts(stdout, "\n");
ef416fc2 1688 }
1689 }
1690 }
1691 }
1692
09a101d6 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
fa73b229 1701 _cupsLangPrintf(stdout, " num_profiles = %d\n",
ef416fc2 1702 ppd->num_profiles);
1703 for (j = 0; j < ppd->num_profiles; j ++)
fa73b229 1704 _cupsLangPrintf(stdout,
ef416fc2 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
fa73b229 1720 _cupsLangPrintf(stdout, " num_fonts = %d\n", ppd->num_fonts);
ef416fc2 1721 for (j = 0; j < ppd->num_fonts; j ++)
fa73b229 1722 _cupsLangPrintf(stdout, " fonts[%d] = %s\n",
ef416fc2 1723 j, ppd->fonts[j]);
1724
fa73b229 1725 _cupsLangPrintf(stdout, " num_attrs = %d\n", ppd->num_attrs);
ef416fc2 1726 for (j = 0; j < ppd->num_attrs; j ++)
fa73b229 1727 _cupsLangPrintf(stdout,
ef416fc2 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)");
1734 }
1735
1736 ppdClose(ppd);
1737 }
1738
1739 if (!files)
1740 usage();
1741
1742 return (status);
1743}
1744
1745
fa73b229 1746/*
1747 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1748 */
1749
3d8365b8 1750static void
fa73b229 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;
1f0275e3 1790 else if (eol != EOL_CRLF)
fa73b229 1791 mixed = 1;
1792 }
1793 else if (eol == EOL_NONE)
1794 eol = EOL_CR;
1f0275e3 1795 else if (eol != EOL_CR)
fa73b229 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
b94498cf 1831/*
1832 * 'check_constraints()' - Check UIConstraints in the PPD file.
1833 */
1834
3d8365b8 1835static int /* O - Errors found */
b94498cf 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{
66ab9486
MS
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 */
b94498cf 1854
1855
1856 prefix = warn ? " WARN " : "**FAIL**";
1857
66ab9486
MS
1858
1859 /*
1860 * See what kind of constraint data we have in the PPD...
1861 */
1862
1863 if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
1864 {
1865 /*
1866 * Check new-style cupsUIConstraints data...
1867 */
1868
1869 for (; constattr;
1870 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
1871 {
1872 if (!constattr->value)
1873 {
1874 if (!warn && !errors && !verbose)
1875 _cupsLangPuts(stdout, _(" FAIL\n"));
1876
1877 _cupsLangPrintf(stdout,
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
1896 _cupsLangPrintf(stdout,
1897 _(" %s Bad cupsUIConstraints %s: \"%s\"!\n"),
1898 prefix, constattr->spec, constattr->value);
1899
1900 if (!warn)
1901 errors ++;
1902
1903 continue;
1904 }
1905
1906 cupsArraySave(ppd->sorted_attrs);
1907
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);
1917
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;
005dd1eb 1929 vptr = strchr(vptr, '*'))
66ab9486
MS
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 == '*')
66ab9486 1945 choice[0] = '\0';
66ab9486
MS
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
1955 if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
1956 {
1957 _cups_strcpy(option, option + 6);
1958 strcpy(choice, "Custom");
1959 }
1960
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
1340db2d
MS
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
66ab9486
MS
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);
2046 }
2047 }
2048 else
b94498cf 2049 {
66ab9486
MS
2050 /*
2051 * Check old-style [Non]UIConstraints data...
2052 */
b94498cf 2053
66ab9486 2054 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
b94498cf 2055 {
66ab9486
MS
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)
2069 {
2070 if (!warn && !errors && !verbose)
2071 _cupsLangPuts(stdout, _(" FAIL\n"));
b94498cf 2072
b94498cf 2073 _cupsLangPrintf(stdout,
2074 _(" %s Missing option %s in "
66ab9486 2075 "UIConstraints \"*%s %s *%s %s\"!\n"),
b94498cf 2076 prefix, c->option1,
2077 c->option1, c->choice1, c->option2, c->choice2);
66ab9486
MS
2078
2079 if (!warn)
2080 errors ++;
2081 }
2082 else if (choice[0] && !ppdFindChoice(o, choice))
2083 {
2084 if (!warn && !errors && !verbose)
2085 _cupsLangPuts(stdout, _(" FAIL\n"));
2086
b94498cf 2087 _cupsLangPrintf(stdout,
66ab9486
MS
2088 _(" %s Missing choice *%s %s in "
2089 "UIConstraints \"*%s %s *%s %s\"!\n"),
2090 prefix, c->option1, c->choice1,
b94498cf 2091 c->option1, c->choice1, c->option2, c->choice2);
2092
66ab9486
MS
2093 if (!warn)
2094 errors ++;
2095 }
b94498cf 2096
66ab9486
MS
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 }
b94498cf 2108
66ab9486
MS
2109 if ((o = ppdFindOption(ppd, option)) == NULL)
2110 {
2111 if (!warn && !errors && !verbose)
2112 _cupsLangPuts(stdout, _(" FAIL\n"));
b94498cf 2113
66ab9486
MS
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);
b94498cf 2119
66ab9486
MS
2120 if (!warn)
2121 errors ++;
2122 }
2123 else if (choice[0] && !ppdFindChoice(o, choice))
2124 {
2125 if (!warn && !errors && !verbose)
2126 _cupsLangPuts(stdout, _(" FAIL\n"));
b94498cf 2127
66ab9486
MS
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);
b94498cf 2133
66ab9486
MS
2134 if (!warn)
2135 errors ++;
2136 }
b94498cf 2137 }
2138 }
2139
2140 return (errors);
2141}
2142
2143
749b1e90
MS
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
b94498cf 2254/*
2255 * 'check_defaults()' - Check default option keywords in the PPD file.
2256 */
2257
3d8365b8 2258static int /* O - Errors found */
b94498cf 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") ||
09a101d6 2278 !strcmp(attr->name, "DefaultHalftoneType") ||
b94498cf 2279 !strcmp(attr->name, "DefaultImageableArea") ||
09a101d6 2280 !strcmp(attr->name, "DefaultLeadingEdge") ||
b94498cf 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
749b1e90
MS
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
b94498cf 2391/*
2392 * 'check_filters()' - Check filters in the PPD file.
2393 */
2394
3d8365b8 2395static int /* O - Errors found */
b94498cf 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{
1f0275e3 2402 int i; /* Looping var */
b94498cf 2403 ppd_attr_t *attr; /* PPD attribute */
2404 const char *ptr; /* Pointer into string */
b94498cf 2405 char super[16], /* Super-type for filter */
2406 type[256], /* Type for filter */
1f0275e3 2407 program[1024], /* Program/filter name */
b94498cf 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
1340db2d
MS
2415 /*
2416 * cupsFilter
2417 */
2418
1f0275e3 2419 for (i = 0; i < ppd->num_filters; i ++)
b94498cf 2420 {
1f0275e3
MS
2421 if (sscanf(ppd->filters[i], "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
2422 &cost, program) != 4)
b94498cf 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"),
1f0275e3 2430 prefix, ppd->filters[i]);
b94498cf 2431
2432 if (!warn)
2433 errors ++;
2434 }
749b1e90 2435 else if (strcmp(program, "-"))
b94498cf 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
a603edef 2452 if (access(pathprog, X_OK))
b94498cf 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 }
1340db2d
MS
2464 else
2465 errors = valid_path("cupsFilter", pathprog, errors, verbose, warn);
b94498cf 2466 }
2467 }
2468
1340db2d
MS
2469 /*
2470 * cupsPreFilter
2471 */
2472
b94498cf 2473 for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
2474 attr;
2475 attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
2476 {
2477 if (!attr->value ||
1f0275e3
MS
2478 sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
2479 &cost, program) != 4)
b94498cf 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 }
749b1e90 2492 else if (strcmp(program, "-"))
b94498cf 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
a603edef 2509 if (access(pathprog, X_OK))
b94498cf 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 }
1340db2d
MS
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 ++;
2571 }
2572 else
2573 errors = valid_path("APPrinterIconPath", attr->value, errors, verbose,
2574 warn);
2575 }
2576
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 ++;
b94498cf 2619 }
1340db2d
MS
2620 else
2621 errors = valid_path("APPrinterUtilityPath", attr->value, errors, verbose,
2622 warn);
b94498cf 2623 }
1340db2d 2624#endif /* __APPLE__ */
b94498cf 2625
2626 return (errors);
2627}
2628
2629
a603edef
MS
2630/*
2631 * 'check_profiles()' - Check ICC color profiles in the PPD file.
2632 */
2633
2634static int /* O - Errors found */
2635check_profiles(ppd_file_t *ppd, /* I - PPD file */
2636 const char *root, /* I - Root directory */
2637 int errors, /* I - Errors found */
2638 int verbose, /* I - Verbosity level */
2639 int warn) /* I - Warnings only? */
2640{
2641 int i; /* Looping var */
2642 ppd_attr_t *attr; /* PPD attribute */
2643 const char *ptr; /* Pointer into string */
2644 const char *prefix; /* WARN/FAIL prefix */
2645 char filename[1024]; /* Profile filename */
2646 int num_profiles = 0; /* Number of profiles */
2647 unsigned hash, /* Current hash value */
2648 hashes[1000]; /* Hash values of profile names */
2649 const char *specs[1000]; /* Specifiers for profiles */
2650
2651
2652 prefix = warn ? " WARN " : "**FAIL**";
2653
2654 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
2655 attr;
2656 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
2657 {
2658 /*
2659 * Check for valid selector...
2660 */
2661
2662 for (i = 0, ptr = strchr(attr->spec, '.'); ptr; ptr = strchr(ptr + 1, '.'))
2663 i ++;
2664
2665 if (!attr->value || i < 2)
2666 {
2667 if (!warn && !errors && !verbose)
2668 _cupsLangPuts(stdout, _(" FAIL\n"));
2669
2670 if (verbose >= 0)
2671 _cupsLangPrintf(stdout,
2672 _(" %s Bad cupsICCProfile %s!\n"),
2673 prefix, attr->spec);
2674
2675 if (!warn)
2676 errors ++;
2677
2678 continue;
2679 }
2680
2681 /*
2682 * Check for valid profile filename...
2683 */
2684
2685 if (attr->value[0] == '/')
2686 snprintf(filename, sizeof(filename), "%s%s", root, attr->value);
2687 else
2688 {
2689 if ((ptr = getenv("CUPS_DATADIR")) == NULL)
2690 ptr = CUPS_DATADIR;
2691
2692 if (*ptr == '/' || !*root)
2693 snprintf(filename, sizeof(filename), "%s%s/profiles/%s", root, ptr,
2694 attr->value);
2695 else
2696 snprintf(filename, sizeof(filename), "%s/%s/profiles/%s", root, ptr,
2697 attr->value);
2698 }
2699
2700 if (access(filename, 0))
2701 {
2702 if (!warn && !errors && !verbose)
2703 _cupsLangPuts(stdout, _(" FAIL\n"));
2704
2705 if (verbose >= 0)
2706 _cupsLangPrintf(stdout, _(" %s Missing cupsICCProfile "
2707 "file \"%s\"!\n"), prefix, attr->value);
2708
2709 if (!warn)
2710 errors ++;
2711 }
1340db2d
MS
2712 else
2713 errors = valid_path("cupsICCProfile", filename, errors, verbose, warn);
a603edef
MS
2714
2715 /*
2716 * Check for hash collisions...
2717 */
2718
2719 hash = _ppdHashName(attr->spec);
2720
2721 if (num_profiles > 0)
2722 {
2723 for (i = 0; i < num_profiles; i ++)
2724 if (hashes[i] == hash)
2725 break;
2726
2727 if (i < num_profiles)
2728 {
2729 if (!warn && !errors && !verbose)
2730 _cupsLangPuts(stdout, _(" FAIL\n"));
2731
2732 if (verbose >= 0)
2733 _cupsLangPrintf(stdout,
2734 _(" %s cupsICCProfile %s hash value "
2735 "collides with %s!\n"), prefix, attr->spec,
2736 specs[i]);
2737
2738 if (!warn)
2739 errors ++;
2740 }
2741 }
2742
2743 /*
2744 * Remember up to 1000 profiles...
2745 */
2746
2747 if (num_profiles < 1000)
2748 {
2749 hashes[num_profiles] = hash;
2750 specs[num_profiles] = attr->spec;
2751 num_profiles ++;
2752 }
2753 }
2754
2755 return (errors);
2756}
2757
2758
1340db2d
MS
2759/*
2760 * 'check_sizes()' - Check media sizes in the PPD file.
2761 */
2762
2763static int /* O - Errors found */
2764check_sizes(ppd_file_t *ppd, /* I - PPD file */
2765 int errors, /* I - Errors found */
2766 int verbose, /* I - Verbosity level */
2767 int warn) /* I - Warnings only? */
2768{
2769 int i; /* Looping vars */
2770 ppd_size_t *size; /* Current size */
2771 int width, /* Custom width */
2772 length; /* Custom length */
2773 char name[PPD_MAX_NAME], /* Size name without dot suffix */
2774 *nameptr; /* Pointer into name */
2775 const char *prefix; /* WARN/FAIL prefix */
2776 ppd_option_t *page_size, /* PageSize option */
2777 *page_region; /* PageRegion option */
2778
2779
2780 prefix = warn ? " WARN " : "**FAIL**";
2781
2782 if ((page_size = ppdFindOption(ppd, "PageSize")) == NULL && warn != 2)
2783 {
2784 if (!warn && !errors && !verbose)
2785 _cupsLangPuts(stdout, _(" FAIL\n"));
2786
2787 if (verbose >= 0)
2788 _cupsLangPrintf(stdout,
2789 _(" %s Missing REQUIRED PageSize option!\n"
2790 " REF: Page 99, section 5.14.\n"),
2791 prefix);
2792
2793 if (!warn)
2794 errors ++;
2795 }
2796
2797 if ((page_region = ppdFindOption(ppd, "PageRegion")) == NULL && warn != 2)
2798 {
2799 if (!warn && !errors && !verbose)
2800 _cupsLangPuts(stdout, _(" FAIL\n"));
2801
2802 if (verbose >= 0)
2803 _cupsLangPrintf(stdout,
2804 _(" %s Missing REQUIRED PageRegion option!\n"
2805 " REF: Page 100, section 5.14.\n"),
2806 prefix);
2807
2808 if (!warn)
2809 errors ++;
2810 }
2811
2812 for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
2813 {
2814 /*
2815 * Check that the size name is standard...
2816 */
2817
2818 if (!strcmp(size->name, "Custom"))
2819 {
2820 /*
2821 * Skip custom page size...
2822 */
2823
2824 continue;
2825 }
2826 else if (warn != 2 && size->name[0] == 'w' &&
2827 sscanf(size->name, "w%dh%d", &width, &length) == 2)
2828 {
2829 /*
2830 * Validate device-specific size wNNNhNNN should have proper width and
2831 * length...
2832 */
2833
2834 if (fabs(width - size->width) >= 1.0 ||
2835 fabs(length - size->length) >= 1.0)
2836 {
2837 if (!warn && !errors && !verbose)
2838 _cupsLangPuts(stdout, _(" FAIL\n"));
2839
2840 if (verbose >= 0)
2841 _cupsLangPrintf(stdout,
2842 _(" %s Size \"%s\" has unexpected dimensions "
2843 "(%gx%g)!\n"),
2844 prefix, size->name, size->width, size->length);
2845
2846 if (!warn)
2847 errors ++;
2848 }
2849 }
2850 else if (warn && verbose >= 0)
2851 {
2852 /*
2853 * Lookup the size name in the standard size table...
2854 */
2855
2856 strlcpy(name, size->name, sizeof(name));
2857 if ((nameptr = strchr(name, '.')) != NULL)
2858 *nameptr = '\0';
2859
2860 if (!bsearch(name, adobe_size_names,
2861 sizeof(adobe_size_names) /
2862 sizeof(adobe_size_names[0]),
2863 sizeof(adobe_size_names[0]),
2864 (int (*)(const void *, const void *))strcmp))
2865 {
2866 _cupsLangPrintf(stdout,
2867 _(" %s Non-standard size name \"%s\"!\n"
2868 " REF: Page 187, section B.2.\n"),
2869 prefix, size->name);
2870 }
2871 }
2872
2873 /*
2874 * Verify that the size is defined for both PageSize and PageRegion...
2875 */
2876
2877 if (warn != 2 && !ppdFindChoice(page_size, size->name))
2878 {
2879 if (!warn && !errors && !verbose)
2880 _cupsLangPuts(stdout, _(" FAIL\n"));
2881
2882 if (verbose >= 0)
2883 _cupsLangPrintf(stdout,
2884 _(" %s Size \"%s\" defined for %s but not for "
2885 "%s!\n"),
2886 prefix, size->name, "PageRegion", "PageSize");
2887
2888 if (!warn)
2889 errors ++;
2890 }
2891 else if (warn != 2 && !ppdFindChoice(page_region, size->name))
2892 {
2893 if (!warn && !errors && !verbose)
2894 _cupsLangPuts(stdout, _(" FAIL\n"));
2895
2896 if (verbose >= 0)
2897 _cupsLangPrintf(stdout,
2898 _(" %s Size \"%s\" defined for %s but not for "
2899 "%s!\n"),
2900 prefix, size->name, "PageSize", "PageRegion");
2901
2902 if (!warn)
2903 errors ++;
2904 }
2905 }
2906
2907 return (errors);
2908}
2909
2910
b94498cf 2911/*
2912 * 'check_translations()' - Check translations in the PPD file.
2913 */
2914
3d8365b8 2915static int /* O - Errors found */
b94498cf 2916check_translations(ppd_file_t *ppd, /* I - PPD file */
2917 int errors, /* I - Errors found */
2918 int verbose, /* I - Verbosity level */
2919 int warn) /* I - Warnings only? */
2920{
2921 int j; /* Looping var */
2922 ppd_attr_t *attr; /* PPD attribute */
a603edef
MS
2923 cups_array_t *languages; /* Array of languages */
2924 int langlen; /* Length of language */
2925 char *language, /* Current language */
b94498cf 2926 keyword[PPD_MAX_NAME], /* Localization keyword (full) */
2927 llkeyword[PPD_MAX_NAME],/* Localization keyword (base) */
2928 ckeyword[PPD_MAX_NAME], /* Custom option keyword (full) */
2929 cllkeyword[PPD_MAX_NAME];
2930 /* Custom option keyword (base) */
2931 ppd_option_t *option; /* Standard UI option */
2932 ppd_coption_t *coption; /* Custom option */
2933 ppd_cparam_t *cparam; /* Custom parameter */
b94498cf 2934 char ll[3]; /* Base language */
2935 const char *prefix; /* WARN/FAIL prefix */
749b1e90 2936 const char *text; /* Pointer into UI text */
b94498cf 2937
2938
2939 prefix = warn ? " WARN " : "**FAIL**";
2940
a603edef 2941 if ((languages = _ppdGetLanguages(ppd)) != NULL)
b94498cf 2942 {
2943 /*
2944 * This file contains localizations, check them...
2945 */
2946
a603edef
MS
2947 for (language = (char *)cupsArrayFirst(languages);
2948 language;
2949 language = (char *)cupsArrayNext(languages))
b94498cf 2950 {
a603edef
MS
2951 langlen = strlen(language);
2952 if (langlen != 2 && langlen != 5)
b94498cf 2953 {
2954 if (!warn && !errors && !verbose)
2955 _cupsLangPuts(stdout, _(" FAIL\n"));
2956
2957 if (verbose >= 0)
2958 _cupsLangPrintf(stdout,
2959 _(" %s Bad language \"%s\"!\n"),
a603edef 2960 prefix, language);
b94498cf 2961
2962 if (!warn)
2963 errors ++;
2964
2965 continue;
2966 }
2967
a603edef 2968 if (!strcmp(language, "en"))
0a682745
MS
2969 continue;
2970
a603edef 2971 strlcpy(ll, language, sizeof(ll));
b94498cf 2972
2973 /*
2974 * Loop through all options and choices...
2975 */
2976
2977 for (option = ppdFirstOption(ppd);
2978 option;
2979 option = ppdNextOption(ppd))
2980 {
09a101d6 2981 if (!strcmp(option->keyword, "PageRegion"))
2982 continue;
2983
a603edef 2984 snprintf(keyword, sizeof(keyword), "%s.Translation", language);
b94498cf 2985 snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll);
2986
2987 if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL &&
2988 (attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL)
2989 {
2990 if (!warn && !errors && !verbose)
2991 _cupsLangPuts(stdout, _(" FAIL\n"));
2992
2993 if (verbose >= 0)
2994 _cupsLangPrintf(stdout,
2995 _(" %s Missing \"%s\" translation "
2996 "string for option %s!\n"),
a603edef 2997 prefix, language, option->keyword);
b94498cf 2998
2999 if (!warn)
3000 errors ++;
3001 }
3002 else if (!valid_utf8(attr->text))
3003 {
3004 if (!warn && !errors && !verbose)
3005 _cupsLangPuts(stdout, _(" FAIL\n"));
3006
3007 if (verbose >= 0)
3008 _cupsLangPrintf(stdout,
3009 _(" %s Bad UTF-8 \"%s\" translation "
3010 "string for option %s!\n"),
a603edef 3011 prefix, language, option->keyword);
b94498cf 3012
3013 if (!warn)
3014 errors ++;
3015 }
3016
a603edef 3017 snprintf(keyword, sizeof(keyword), "%s.%s", language,
b94498cf 3018 option->keyword);
3019 snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll,
3020 option->keyword);
3021
3022 for (j = 0; j < option->num_choices; j ++)
3023 {
749b1e90
MS
3024 /*
3025 * First see if this choice is a number; if so, don't require
3026 * translation...
3027 */
3028
3029 for (text = option->choices[j].text; *text; text ++)
3030 if (!strchr("0123456789-+.", *text))
3031 break;
3032
3033 if (!*text)
3034 continue;
3035
3036 /*
3037 * Check custom choices differently...
3038 */
3039
b94498cf 3040 if (!strcasecmp(option->choices[j].choice, "Custom") &&
3041 (coption = ppdFindCustomOption(ppd,
3042 option->keyword)) != NULL)
3043 {
3044 snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
a603edef 3045 language, option->keyword);
b94498cf 3046
3047 if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL &&
3048 !valid_utf8(attr->text))
3049 {
3050 if (!warn && !errors && !verbose)
3051 _cupsLangPuts(stdout, _(" FAIL\n"));
3052
3053 if (verbose >= 0)
3054 _cupsLangPrintf(stdout,
3055 _(" %s Bad UTF-8 \"%s\" "
3056 "translation string for option %s, "
3057 "choice %s!\n"),
a603edef
MS
3058 prefix, language,
3059 ckeyword + 1 + strlen(language),
b94498cf 3060 "True");
3061
3062 if (!warn)
3063 errors ++;
3064 }
3065
3066 if (strcasecmp(option->keyword, "PageSize"))
3067 {
3068 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
3069 cparam;
3070 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
3071 {
3072 snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
a603edef 3073 language, option->keyword);
b94498cf 3074 snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s",
3075 ll, option->keyword);
3076
3077 if ((attr = ppdFindAttr(ppd, ckeyword,
3078 cparam->name)) == NULL &&
3079 (attr = ppdFindAttr(ppd, cllkeyword,
3080 cparam->name)) == NULL)
3081 {
3082 if (!warn && !errors && !verbose)
3083 _cupsLangPuts(stdout, _(" FAIL\n"));
3084
3085 if (verbose >= 0)
3086 _cupsLangPrintf(stdout,
3087 _(" %s Missing \"%s\" "
3088 "translation string for option %s, "
3089 "choice %s!\n"),
a603edef
MS
3090 prefix, language,
3091 ckeyword + 1 + strlen(language),
b94498cf 3092 cparam->name);
3093
3094 if (!warn)
3095 errors ++;
3096 }
3097 else if (!valid_utf8(attr->text))
3098 {
3099 if (!warn && !errors && !verbose)
3100 _cupsLangPuts(stdout, _(" FAIL\n"));
3101
3102 if (verbose >= 0)
3103 _cupsLangPrintf(stdout,
3104 _(" %s Bad UTF-8 \"%s\" "
3105 "translation string for option %s, "
3106 "choice %s!\n"),
a603edef
MS
3107 prefix, language,
3108 ckeyword + 1 + strlen(language),
b94498cf 3109 cparam->name);
3110
3111 if (!warn)
3112 errors ++;
3113 }
3114 }
3115 }
3116 }
3117 else if ((attr = ppdFindAttr(ppd, keyword,
3118 option->choices[j].choice)) == NULL &&
3119 (attr = ppdFindAttr(ppd, llkeyword,
3120 option->choices[j].choice)) == NULL)
3121 {
3122 if (!warn && !errors && !verbose)
3123 _cupsLangPuts(stdout, _(" FAIL\n"));
3124
3125 if (verbose >= 0)
3126 _cupsLangPrintf(stdout,
3127 _(" %s Missing \"%s\" "
3128 "translation string for option %s, "
3129 "choice %s!\n"),
a603edef 3130 prefix, language, option->keyword,
b94498cf 3131 option->choices[j].choice);
3132
3133 if (!warn)
3134 errors ++;
3135 }
3136 else if (!valid_utf8(attr->text))
3137 {
3138 if (!warn && !errors && !verbose)
3139 _cupsLangPuts(stdout, _(" FAIL\n"));
3140
3141 if (verbose >= 0)
3142 _cupsLangPrintf(stdout,
3143 _(" %s Bad UTF-8 \"%s\" "
3144 "translation string for option %s, "
3145 "choice %s!\n"),
a603edef 3146 prefix, language, option->keyword,
b94498cf 3147 option->choices[j].choice);
3148
3149 if (!warn)
3150 errors ++;
3151 }
3152 }
3153 }
3154 }
3155
3156 /*
3157 * Verify that we have the base language for each localized one...
3158 */
3159
a603edef
MS
3160 for (language = (char *)cupsArrayFirst(languages);
3161 language;
3162 language = (char *)cupsArrayNext(languages))
3163 if (language[2])
b94498cf 3164 {
3165 /*
3166 * Lookup the base language...
3167 */
3168
a603edef 3169 cupsArraySave(languages);
b94498cf 3170
a603edef 3171 strlcpy(ll, language, sizeof(ll));
b94498cf 3172
005dd1eb
MS
3173 if (!cupsArrayFind(languages, ll) &&
3174 strcmp(ll, "zh") && strcmp(ll, "en"))
b94498cf 3175 {
3176 if (!warn && !errors && !verbose)
3177 _cupsLangPuts(stdout, _(" FAIL\n"));
3178
3179 if (verbose >= 0)
3180 _cupsLangPrintf(stdout,
3181 _(" %s No base translation \"%s\" "
3182 "is included in file!\n"), prefix, ll);
3183
3184 if (!warn)
3185 errors ++;
3186 }
3187
a603edef 3188 cupsArrayRestore(languages);
b94498cf 3189 }
3190
3191 /*
3192 * Free memory used for the languages...
3193 */
3194
a603edef 3195 _ppdFreeLanguages(languages);
b94498cf 3196 }
3197
3198 return (errors);
3199}
3200
3201
ef416fc2 3202/*
3203 * 'show_conflicts()' - Show option conflicts in a PPD file.
3204 */
3205
3d8365b8 3206static void
ef416fc2 3207show_conflicts(ppd_file_t *ppd) /* I - PPD to check */
3208{
3209 int i, j; /* Looping variables */
3210 ppd_const_t *c; /* Current constraint */
3211 ppd_option_t *o1, *o2; /* Options */
3212 ppd_choice_t *c1, *c2; /* Choices */
3213
3214
3215 /*
3216 * Loop through all of the UI constraints and report any options
3217 * that conflict...
3218 */
3219
3220 for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
3221 {
3222 /*
3223 * Grab pointers to the first option...
3224 */
3225
3226 o1 = ppdFindOption(ppd, c->option1);
3227
3228 if (o1 == NULL)
3229 continue;
3230 else if (c->choice1[0] != '\0')
3231 {
3232 /*
3233 * This constraint maps to a specific choice.
3234 */
3235
3236 c1 = ppdFindChoice(o1, c->choice1);
3237 }
3238 else
3239 {
3240 /*
3241 * This constraint applies to any choice for this option.
3242 */
3243
3244 for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
3245 if (c1->marked)
3246 break;
3247
3248 if (j == 0 ||
3249 !strcasecmp(c1->choice, "None") ||
3250 !strcasecmp(c1->choice, "Off") ||
3251 !strcasecmp(c1->choice, "False"))
3252 c1 = NULL;
3253 }
3254
3255 /*
3256 * Grab pointers to the second option...
3257 */
3258
3259 o2 = ppdFindOption(ppd, c->option2);
3260
3261 if (o2 == NULL)
3262 continue;
3263 else if (c->choice2[0] != '\0')
3264 {
3265 /*
3266 * This constraint maps to a specific choice.
3267 */
3268
3269 c2 = ppdFindChoice(o2, c->choice2);
3270 }
3271 else
3272 {
3273 /*
3274 * This constraint applies to any choice for this option.
3275 */
3276
3277 for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
3278 if (c2->marked)
3279 break;
3280
3281 if (j == 0 ||
3282 !strcasecmp(c2->choice, "None") ||
3283 !strcasecmp(c2->choice, "Off") ||
3284 !strcasecmp(c2->choice, "False"))
3285 c2 = NULL;
3286 }
3287
3288 /*
3289 * If both options are marked then there is a conflict...
3290 */
3291
3292 if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
fa73b229 3293 _cupsLangPrintf(stdout,
ef416fc2 3294 _(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
3295 " (constraint=\"%s %s %s %s\")\n"),
3296 o1->keyword, c1->choice, o2->keyword, c2->choice,
3297 c->option1, c->choice1, c->option2, c->choice2);
3298 }
3299}
3300
3301
09a101d6 3302/*
3303 * 'test_raster()' - Test PostScript commands for raster printers.
3304 */
3305
3306static int /* O - 1 on success, 0 on failure */
3307test_raster(ppd_file_t *ppd, /* I - PPD file */
3308 int verbose) /* I - Verbosity */
3309{
3310 cups_page_header2_t header; /* Page header */
3311
3312
3313 ppdMarkDefaults(ppd);
3314 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
3315 {
3316 if (!verbose)
3317 _cupsLangPuts(stdout, _(" FAIL\n"));
3318
3319 if (verbose >= 0)
3320 _cupsLangPrintf(stdout,
3321 _(" **FAIL** Default option code cannot be "
3322 "interpreted: %s\n"), cupsRasterErrorString());
3323
3324 return (0);
3325 }
db1f069b
MS
3326
3327 /*
3328 * Try a test of custom page size code, if available...
3329 */
3330
3331 if (!ppdPageSize(ppd, "Custom.612x792"))
09a101d6 3332 return (1);
db1f069b
MS
3333
3334 ppdMarkOption(ppd, "PageSize", "Custom.612x792");
3335
3336 if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
3337 {
3338 if (!verbose)
3339 _cupsLangPuts(stdout, _(" FAIL\n"));
3340
3341 if (verbose >= 0)
3342 _cupsLangPrintf(stdout,
3343 _(" **FAIL** Default option code cannot be "
3344 "interpreted: %s\n"), cupsRasterErrorString());
3345
3346 return (0);
3347 }
3348
3349 return (1);
09a101d6 3350}
3351
3352
ef416fc2 3353/*
3354 * 'usage()' - Show program usage...
3355 */
3356
3d8365b8 3357static void
ef416fc2 3358usage(void)
3359{
fa73b229 3360 _cupsLangPuts(stdout,
b94498cf 3361 _("Usage: cupstestppd [options] filename1.ppd[.gz] "
3362 "[... filenameN.ppd[.gz]]\n"
3363 " program | cupstestppd [options] -\n"
3364 "\n"
3365 "Options:\n"
3366 "\n"
3367 " -R root-directory Set alternate root\n"
749b1e90 3368 " -W {all,none,constraints,defaults,duplex,filters,"
1340db2d 3369 "profiles,sizes,translations}\n"
b94498cf 3370 " Issue warnings instead of errors\n"
3371 " -q Run silently\n"
3372 " -r Use 'relaxed' open mode\n"
3373 " -v Be slightly verbose\n"
3374 " -vv Be very verbose\n"));
ef416fc2 3375
3376 exit(ERROR_USAGE);
3377}
3378
3379
1340db2d
MS
3380/*
3381 * 'valid_path()' - Check whether a path has the correct capitalization.
3382 */
3383
3384static int /* O - Errors found */
3385valid_path(const char *keyword, /* I - Keyword using path */
3386 const char *path, /* I - Path to check */
3387 int errors, /* I - Errors found */
3388 int verbose, /* I - Verbosity level */
3389 int warn) /* I - Warnings only? */
3390{
3391 cups_dir_t *dir; /* Current directory */
3392 cups_dentry_t *dentry; /* Current directory entry */
3393 char temp[1024], /* Temporary path */
3394 *ptr; /* Pointer into temporary path */
3395 const char *prefix; /* WARN/FAIL prefix */
3396
3397
3398 prefix = warn ? " WARN " : "**FAIL**";
3399
3400 /*
3401 * Loop over the components of the path, checking that the entry exists with
3402 * the same capitalization...
3403 */
3404
3405 strlcpy(temp, path, sizeof(temp));
3406
3407 while ((ptr = strrchr(temp, '/')) != NULL)
3408 {
3409 /*
3410 * Chop off the trailing component so temp == dirname and ptr == basename.
3411 */
3412
3413 *ptr++ = '\0';
3414
3415 /*
3416 * Try opening the directory containing the base name...
3417 */
3418
3419 if (temp[0])
3420 dir = cupsDirOpen(temp);
3421 else
3422 dir = cupsDirOpen("/");
3423
3424 if (!dir)
3425 dentry = NULL;
3426 else
3427 {
3428 while ((dentry = cupsDirRead(dir)) != NULL)
3429 {
3430 if (!strcmp(dentry->filename, ptr))
3431 break;
3432 }
3433
3434 cupsDirClose(dir);
3435 }
3436
3437 /*
3438 * Display an error if the filename doesn't exist with the same
3439 * capitalization...
3440 */
3441
3442 if (!dentry)
3443 {
3444 if (!warn && !errors && !verbose)
3445 _cupsLangPuts(stdout, _(" FAIL\n"));
3446
3447 if (verbose >= 0)
3448 _cupsLangPrintf(stdout,
3449 _(" %s %s file \"%s\" has the wrong "
3450 "capitalization!\n"), prefix, keyword, path);
3451
3452 if (!warn)
3453 errors ++;
3454
3455 break;
3456 }
3457 }
3458
3459 return (errors);
3460}
3461
3462
ef416fc2 3463/*
f7deaa1a 3464 * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
3465 */
3466
3d8365b8 3467static int /* O - 1 if valid, 0 if not */
f7deaa1a 3468valid_utf8(const char *s) /* I - String to check */
3469{
3470 while (*s)
3471 {
3472 if (*s & 0x80)
3473 {
3474 /*
3475 * Check for valid UTF-8 sequence...
3476 */
3477
3478 if ((*s & 0xc0) == 0x80)
3479 return (0); /* Illegal suffix byte */
3480 else if ((*s & 0xe0) == 0xc0)
3481 {
3482 /*
3483 * 2-byte sequence...
3484 */
3485
3486 s ++;
3487
3488 if ((*s & 0xc0) != 0x80)
3489 return (0); /* Missing suffix byte */
3490 }
3491 else if ((*s & 0xf0) == 0xe0)
3492 {
3493 /*
3494 * 3-byte sequence...
3495 */
3496
3497 s ++;
3498
3499 if ((*s & 0xc0) != 0x80)
3500 return (0); /* Missing suffix byte */
3501
3502 s ++;
3503
3504 if ((*s & 0xc0) != 0x80)
3505 return (0); /* Missing suffix byte */
3506 }
3507 else if ((*s & 0xf8) == 0xf0)
3508 {
3509 /*
3510 * 4-byte sequence...
3511 */
3512
3513 s ++;
3514
3515 if ((*s & 0xc0) != 0x80)
3516 return (0); /* Missing suffix byte */
3517
3518 s ++;
3519
3520 if ((*s & 0xc0) != 0x80)
3521 return (0); /* Missing suffix byte */
3522
3523 s ++;
3524
3525 if ((*s & 0xc0) != 0x80)
3526 return (0); /* Missing suffix byte */
3527 }
3528 else
3529 return (0); /* Bad sequence */
3530 }
3531
3532 s ++;
3533 }
3534
3535 return (1);
3536}
3537
3538
3539/*
b19ccc9e 3540 * End of "$Id: cupstestppd.c 7807 2008-07-28 21:54:24Z mike $".
ef416fc2 3541 */