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