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