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