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