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