static int check_constraints()

in systemv/cupstestppd.c [91:1644]


static int	check_constraints(ppd_file_t *ppd, int errors, int verbose,
		                  int warn);
static int	check_case(ppd_file_t *ppd, int errors, int verbose);
static int	check_defaults(ppd_file_t *ppd, int errors, int verbose,
		               int warn);
static int	check_duplex(ppd_file_t *ppd, int errors, int verbose,
		             int warn);
static int	check_filters(ppd_file_t *ppd, const char *root, int errors,
		              int verbose, int warn);
static int	check_profiles(ppd_file_t *ppd, const char *root, int errors,
		               int verbose, int warn);
static int	check_sizes(ppd_file_t *ppd, int errors, int verbose, int warn);
static int	check_translations(ppd_file_t *ppd, int errors, int verbose,
		                   int warn);
static void	show_conflicts(ppd_file_t *ppd, const char *prefix);
static int	test_raster(ppd_file_t *ppd, int verbose);
static void	usage(void) _CUPS_NORETURN;
static int	valid_path(const char *keyword, const char *path, int errors,
		           int verbose, int warn);
static int	valid_utf8(const char *s);


/*
 * 'main()' - Main entry for test program.
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line arguments */
{
  int		i, j, k, m, n;		/* Looping vars */
  size_t	len;			/* Length of option name */
  char		*opt;			/* Option character */
  const char	*ptr;			/* Pointer into string */
  cups_file_t	*fp;			/* PPD file */
  int		files;			/* Number of files */
  int		verbose;		/* Want verbose output? */
  int		warn;			/* Which errors to just warn about */
  int		ignore;			/* Which errors to ignore */
  int		status;			/* Exit status */
  int		errors;			/* Number of conformance errors */
  int		ppdversion;		/* PPD spec version in PPD file */
  ppd_status_t	error;			/* Status of ppdOpen*() */
  int		line;			/* Line number for error */
  char		*root;			/* Root directory */
  int		xdpi,			/* X resolution */
		ydpi;			/* Y resolution */
  ppd_file_t	*ppd;			/* PPD file record */
  ppd_attr_t	*attr;			/* PPD attribute */
  ppd_size_t	*size;			/* Size record */
  ppd_group_t	*group;			/* UI group */
  ppd_option_t	*option;		/* Standard UI option */
  ppd_group_t	*group2;		/* UI group */
  ppd_option_t	*option2;		/* Standard UI option */
  ppd_choice_t	*choice;		/* Standard UI option choice */
  struct lconv	*loc;			/* Locale data */
  static char	*uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
  static char	*sections[] = { "ANY", "DOCUMENT", "EXIT",
                                "JCL", "PAGE", "PROLOG" };


  _cupsSetLocale(argv);
  loc = localeconv();

 /*
  * Display PPD files for each file listed on the command-line...
  */

  ppdSetConformance(PPD_CONFORM_STRICT);

  verbose = 0;
  ppd     = NULL;
  files   = 0;
  status  = ERROR_NONE;
  root    = "";
  warn    = WARN_NONE;
  ignore  = WARN_NONE;

  for (i = 1; i < argc; i ++)
    if (!strcmp(argv[i], "--help"))
      usage();
    else if (argv[i][0] == '-' && argv[i][1])
    {
      for (opt = argv[i] + 1; *opt; opt ++)
        switch (*opt)
	{
	  case 'I' :			/* Ignore errors */
	      i ++;

	      if (i >= argc)
	        usage();

              if (!strcmp(argv[i], "none"))
	        ignore = WARN_NONE;
	      else if (!strcmp(argv[i], "filename"))
	        ignore |= WARN_FILENAME;
	      else if (!strcmp(argv[i], "filters"))
	        ignore |= WARN_FILTERS;
	      else if (!strcmp(argv[i], "profiles"))
	        ignore |= WARN_PROFILES;
	      else if (!strcmp(argv[i], "all"))
	        ignore = WARN_FILTERS | WARN_PROFILES;
	      else
	        usage();
	      break;

	  case 'R' :			/* Alternate root directory */
	      i ++;

	      if (i >= argc)
	        usage();

              root = argv[i];
	      break;

	  case 'W' :			/* Turn errors into warnings */
	      i ++;

	      if (i >= argc)
	        usage();

              if (!strcmp(argv[i], "none"))
	        warn = WARN_NONE;
	      else if (!strcmp(argv[i], "constraints"))
	        warn |= WARN_CONSTRAINTS;
	      else if (!strcmp(argv[i], "defaults"))
	        warn |= WARN_DEFAULTS;
	      else if (!strcmp(argv[i], "duplex"))
	        warn |= WARN_DUPLEX;
	      else if (!strcmp(argv[i], "filters"))
	        warn |= WARN_FILTERS;
	      else if (!strcmp(argv[i], "profiles"))
	        warn |= WARN_PROFILES;
	      else if (!strcmp(argv[i], "sizes"))
	        warn |= WARN_SIZES;
	      else if (!strcmp(argv[i], "translations"))
	        warn |= WARN_TRANSLATIONS;
	      else if (!strcmp(argv[i], "all"))
	        warn = WARN_ALL;
	      else
	        usage();
	      break;

	  case 'q' :			/* Quiet mode */
	      if (verbose > 0)
	      {
        	_cupsLangPuts(stderr,
		              _("cupstestppd: The -q option is incompatible "
			        "with the -v option."));
		return (1);
	      }

	      verbose --;
	      break;

	  case 'r' :			/* Relaxed mode */
              ppdSetConformance(PPD_CONFORM_RELAXED);
	      break;

	  case 'v' :			/* Verbose mode */
	      if (verbose < 0)
	      {
        	_cupsLangPuts(stderr,
		              _("cupstestppd: The -v option is incompatible "
			        "with the -q option."));
		return (1);
	      }

	      verbose ++;
	      break;

	  default :
	      usage();
	}
    }
    else
    {
     /*
      * Open the PPD file...
      */

      if (files && verbose >= 0)
        puts("");

      files ++;

      if (argv[i][0] == '-')
      {
       /*
        * Read from stdin...
	*/

        ppd = _ppdOpen(cupsFileStdin(), _PPD_LOCALIZATION_ALL);

        if (verbose >= 0)
          printf("%s:", (ppd && ppd->pcfilename) ? ppd->pcfilename : "(stdin)");
      }
      else
      {
       /*
        * Read from a file...
	*/

        if (verbose >= 0)
          printf("%s:", argv[i]);

        if ((fp = cupsFileOpen(argv[i], "r")) != NULL)
        {
          ppd = _ppdOpen(fp, _PPD_LOCALIZATION_ALL);
          cupsFileClose(fp);
        }
        else
        {
	  status = ERROR_FILE_OPEN;

	  if (verbose >= 0)
          {
            _cupsLangPuts(stdout, _(" FAIL"));
            _cupsLangPrintf(stdout,
	                    _("      **FAIL**  Unable to open PPD file - %s on "
	                      "line %d."), strerror(errno), 0);
	    continue;
          }
        }
      }

      if (ppd == NULL)
      {
        error = ppdLastError(&line);

	if (error <= PPD_ALLOC_ERROR)
	{
	  status = ERROR_FILE_OPEN;

	  if (verbose >= 0)
          {
            _cupsLangPuts(stdout, _(" FAIL"));
            _cupsLangPrintf(stdout,
	                    _("      **FAIL**  Unable to open PPD file - %s on "
	                      "line %d."), strerror(errno), 0);
          }
	}
	else
	{
	  status = ERROR_PPD_FORMAT;

          if (verbose >= 0)
          {
            _cupsLangPuts(stdout, _(" FAIL"));
            _cupsLangPrintf(stdout,
	                    _("      **FAIL**  Unable to open PPD file - "
			      "%s on line %d."),
			    ppdErrorString(error), line);

            switch (error)
	    {
	      case PPD_MISSING_PPDADOBE4 :
	          _cupsLangPuts(stdout,
		                _("                REF: Page 42, section "
				  "5.2."));
	          break;
	      case PPD_MISSING_VALUE :
	          _cupsLangPuts(stdout,
		                _("                REF: Page 20, section "
				  "3.4."));
	          break;
	      case PPD_BAD_OPEN_GROUP :
	      case PPD_NESTED_OPEN_GROUP :
	          _cupsLangPuts(stdout,
		                _("                REF: Pages 45-46, section "
				  "5.2."));
	          break;
	      case PPD_BAD_OPEN_UI :
	      case PPD_NESTED_OPEN_UI :
	          _cupsLangPuts(stdout,
		                _("                REF: Pages 42-45, section "
				  "5.2."));
	          break;
	      case PPD_BAD_ORDER_DEPENDENCY :
	          _cupsLangPuts(stdout,
		                _("                REF: Pages 48-49, section "
				  "5.2."));
	          break;
	      case PPD_BAD_UI_CONSTRAINTS :
	          _cupsLangPuts(stdout,
		                _("                REF: Pages 52-54, section "
				  "5.2."));
	          break;
	      case PPD_MISSING_ASTERISK :
	          _cupsLangPuts(stdout,
		                _("                REF: Page 15, section "
				  "3.2."));
	          break;
	      case PPD_LINE_TOO_LONG :
	          _cupsLangPuts(stdout,
		                _("                REF: Page 15, section "
				  "3.1."));
	          break;
	      case PPD_ILLEGAL_CHARACTER :
	          _cupsLangPuts(stdout,
		                _("                REF: Page 15, section "
				  "3.1."));
	          break;
	      case PPD_ILLEGAL_MAIN_KEYWORD :
	          _cupsLangPuts(stdout,
		                _("                REF: Pages 16-17, section "
				  "3.2."));
	          break;
	      case PPD_ILLEGAL_OPTION_KEYWORD :
	          _cupsLangPuts(stdout,
		                _("                REF: Page 19, section "
				  "3.3."));
	          break;
	      case PPD_ILLEGAL_TRANSLATION :
	          _cupsLangPuts(stdout,
		                _("                REF: Page 27, section "
				  "3.5."));
	          break;
              default :
	          break;
	    }

	    check_basics(argv[i]);
	  }
        }

	continue;
      }

     /*
      * Show the header and then perform basic conformance tests (limited
      * only by what the CUPS PPD functions actually load...)
      */

      errors     = 0;
      ppdversion = 43;

      if (verbose > 0)
        _cupsLangPuts(stdout,
	              _("\n    DETAILED CONFORMANCE TEST RESULTS"));

      if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
          attr->value)
        ppdversion = (int)(10 * _cupsStrScand(attr->value, NULL, loc) + 0.5);

      if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) != NULL)
      {
        do
        {
	  if (strstr(attr->value, "application/vnd.cups-raster"))
	  {
	    if (!test_raster(ppd, verbose))
	      errors ++;
	    break;
	  }
	}
	while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
      }
      else
      {
	for (j = 0; j < ppd->num_filters; j ++)
	  if (strstr(ppd->filters[j], "application/vnd.cups-raster"))
	  {
	    if (!test_raster(ppd, verbose))
	      errors ++;
	    break;
	  }
      }

     /*
      * Look for default keywords with no matching option...
      */

      if (!(warn & WARN_DEFAULTS))
        errors = check_defaults(ppd, errors, verbose, 0);

      if ((attr = ppdFindAttr(ppd, "DefaultImageableArea", NULL)) == NULL)
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
			_("      **FAIL**  REQUIRED DefaultImageableArea\n"
			  "                REF: Page 102, section 5.15."));
	}

	errors ++;
      }
      else if (ppdPageSize(ppd, attr->value) == NULL &&
	       strcmp(attr->value, "Unknown"))
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPrintf(stdout,
			  _("      **FAIL**  Bad DefaultImageableArea %s\n"
			    "                REF: Page 102, section 5.15."),
			  attr->value);
	}

	errors ++;
      }
      else
      {
	if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    DefaultImageableArea"));
      }

      if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
			_("      **FAIL**  REQUIRED DefaultPaperDimension\n"
			  "                REF: Page 103, section 5.15."));
	}

	errors ++;
      }
      else if (ppdPageSize(ppd, attr->value) == NULL &&
	       strcmp(attr->value, "Unknown"))
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPrintf(stdout,
			  _("      **FAIL**  Bad DefaultPaperDimension %s\n"
			    "                REF: Page 103, section 5.15."),
			  attr->value);
	}

	errors ++;
      }
      else if (verbose > 0)
	_cupsLangPuts(stdout, _("        PASS    DefaultPaperDimension"));

      for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
	for (k = 0, option = group->options;
	     k < group->num_options;
	     k ++, option ++)
	{
	 /*
	  * Verify that we have a default choice...
	  */

	  if (option->defchoice[0])
	  {
	    if (ppdFindChoice(option, option->defchoice) == NULL &&
		strcmp(option->defchoice, "Unknown"))
	    {
	      if (verbose >= 0)
	      {
		if (!errors && !verbose)
		  _cupsLangPuts(stdout, _(" FAIL"));

		_cupsLangPrintf(stdout,
				_("      **FAIL**  Bad Default%s %s\n"
				  "                REF: Page 40, section 4.5."),
				option->keyword, option->defchoice);
	      }

	      errors ++;
	    }
	    else if (verbose > 0)
	      _cupsLangPrintf(stdout,
			      _("        PASS    Default%s"),
			      option->keyword);
	  }
	  else
	  {
	    if (verbose >= 0)
	    {
	      if (!errors && !verbose)
		_cupsLangPuts(stdout, _(" FAIL"));

	      _cupsLangPrintf(stdout,
			      _("      **FAIL**  REQUIRED Default%s\n"
				"                REF: Page 40, section 4.5."),
			      option->keyword);
	    }

	    errors ++;
	  }
	}

      if ((attr = ppdFindAttr(ppd, "FileVersion", NULL)) != NULL)
      {
        for (ptr = attr->value; *ptr; ptr ++)
	  if (!isdigit(*ptr & 255) && *ptr != '.')
	    break;

	if (*ptr)
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPrintf(stdout,
			    _("      **FAIL**  Bad FileVersion \"%s\"\n"
			      "                REF: Page 56, section 5.3."),
			    attr->value);
	  }

	  errors ++;
	}
	else if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    FileVersion"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED FileVersion\n"
			  "                REF: Page 56, section 5.3."));
        }

	errors ++;
      }

      if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL)
      {
        ptr = attr->value;
	if (*ptr == '4' && ptr[1] == '.')
	{

	  for (ptr += 2; *ptr; ptr ++)
	    if (!isdigit(*ptr & 255))
	      break;
        }

	if (*ptr)
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPrintf(stdout,
			    _("      **FAIL**  Bad FormatVersion \"%s\"\n"
			      "                REF: Page 56, section 5.3."),
			    attr->value);
	  }

	  errors ++;
	}
	else if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    FormatVersion"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED FormatVersion\n"
			  "                REF: Page 56, section 5.3."));
        }

	errors ++;
      }

      if (ppd->lang_encoding != NULL)
      {
	if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    LanguageEncoding"));
      }
      else if (ppdversion > 40)
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED LanguageEncoding\n"
			  "                REF: Pages 56-57, section 5.3."));
        }

	errors ++;
      }

      if (ppd->lang_version != NULL)
      {
	if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    LanguageVersion"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED LanguageVersion\n"
			  "                REF: Pages 57-58, section 5.3."));
        }

	errors ++;
      }

      if (ppd->manufacturer != NULL)
      {
        if (!_cups_strncasecmp(ppd->manufacturer, "Hewlett-Packard", 15) ||
	    !_cups_strncasecmp(ppd->manufacturer, "Hewlett Packard", 15))
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPrintf(stdout,
			    _("      **FAIL**  Bad Manufacturer (should be "
			      "\"%s\")\n"
			      "                REF: Page 211, table D.1."),
			    "HP");
          }

	  errors ++;
	}
        else if (!_cups_strncasecmp(ppd->manufacturer, "OkiData", 7) ||
	         !_cups_strncasecmp(ppd->manufacturer, "Oki Data", 8))
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPrintf(stdout,
	                    _("      **FAIL**  Bad Manufacturer (should be "
			      "\"%s\")\n"
			      "                REF: Page 211, table D.1."),
			    "Oki");
          }

	  errors ++;
	}
	else if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    Manufacturer"));
      }
      else if (ppdversion >= 43)
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED Manufacturer\n"
			  "                REF: Pages 58-59, section 5.3."));
        }

	errors ++;
      }

      if (ppd->modelname != NULL)
      {
        for (ptr = ppd->modelname; *ptr; ptr ++)
	  if (!isalnum(*ptr & 255) && !strchr(" ./-+", *ptr))
	    break;

	if (*ptr)
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPrintf(stdout,
	                    _("      **FAIL**  Bad ModelName - \"%c\" not "
			      "allowed in string.\n"
			      "                REF: Pages 59-60, section 5.3."),
	                    *ptr);
          }

	  errors ++;
	}
	else if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    ModelName"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED ModelName\n"
			  "                REF: Pages 59-60, section 5.3."));
        }

	errors ++;
      }

      if (ppd->nickname != NULL)
      {
	if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    NickName"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED NickName\n"
	                  "                REF: Page 60, section 5.3."));
        }

	errors ++;
      }

      if (ppdFindOption(ppd, "PageSize") != NULL)
      {
	if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    PageSize"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED PageSize\n"
			  "                REF: Pages 99-100, section 5.14."));
        }

	errors ++;
      }

      if (ppdFindOption(ppd, "PageRegion") != NULL)
      {
	if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    PageRegion"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED PageRegion\n"
			  "                REF: Page 100, section 5.14."));
        }

	errors ++;
      }

      if (ppd->pcfilename != NULL)
      {
	if (verbose > 0)
          _cupsLangPuts(stdout, _("        PASS    PCFileName"));
      }
      else if (!(ignore & WARN_FILENAME))
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED PCFileName\n"
			  "                REF: Pages 61-62, section 5.3."));
        }

	errors ++;
      }

      if (ppd->product != NULL)
      {
        if (ppd->product[0] != '(' ||
	    ppd->product[strlen(ppd->product) - 1] != ')')
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPuts(stdout,
	                  _("      **FAIL**  Bad Product - not \"(string)\".\n"
			    "                REF: Page 62, section 5.3."));
          }

	  errors ++;
	}
	else if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    Product"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED Product\n"
			  "                REF: Page 62, section 5.3."));
        }

	errors ++;
      }

      if ((attr = ppdFindAttr(ppd, "PSVersion", NULL)) != NULL &&
          attr->value != NULL)
      {
        char	junkstr[255];			/* Temp string */
	int	junkint;			/* Temp integer */


        if (sscanf(attr->value, "(%254[^)\n])%d", junkstr, &junkint) != 2)
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPuts(stdout,
	                  _("      **FAIL**  Bad PSVersion - not \"(string) "
			    "int\".\n"
			    "                REF: Pages 62-64, section 5.3."));
          }

	  errors ++;
	}
	else if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    PSVersion"));
      }
      else
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED PSVersion\n"
			  "                REF: Pages 62-64, section 5.3."));
        }

	errors ++;
      }

      if (ppd->shortnickname != NULL)
      {
        if (strlen(ppd->shortnickname) > 31)
	{
	  if (verbose >= 0)
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

	    _cupsLangPuts(stdout,
	                  _("      **FAIL**  Bad ShortNickName - longer "
			    "than 31 chars.\n"
			    "                REF: Pages 64-65, section 5.3."));
          }

	  errors ++;
	}
	else if (verbose > 0)
	  _cupsLangPuts(stdout, _("        PASS    ShortNickName"));
      }
      else if (ppdversion >= 43)
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED ShortNickName\n"
			  "                REF: Page 64-65, section 5.3."));
        }

	errors ++;
      }

      if (ppd->patches != NULL && strchr(ppd->patches, '\"') &&
          strstr(ppd->patches, "*End"))
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  Bad JobPatchFile attribute in file\n"
	                  "                REF: Page 24, section 3.4."));
        }

	errors ++;
      }

     /*
      * Check for page sizes without the corresponding ImageableArea or
      * PaperDimension values...
      */

      if (ppd->num_sizes == 0)
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPuts(stdout,
	                _("      **FAIL**  REQUIRED PageSize\n"
			  "                REF: Page 41, section 5.\n"
			  "                REF: Page 99, section 5.14."));
        }

	errors ++;
      }
      else
      {
	for (j = 0, size = ppd->sizes; j < ppd->num_sizes; j ++, size ++)
	{
	 /*
	  * Don't check custom size...
	  */

	  if (!strcmp(size->name, "Custom"))
	    continue;

	 /*
	  * Check for ImageableArea...
	  */

          if (size->left == 0.0 && size->bottom == 0.0 &&
	      size->right == 0.0 && size->top == 0.0)
	  {
	    if (verbose >= 0)
	    {
	      if (!errors && !verbose)
		_cupsLangPuts(stdout, _(" FAIL"));

	      _cupsLangPrintf(stdout,
	                      _("      **FAIL**  REQUIRED ImageableArea for "
			        "PageSize %s\n"
				"                REF: Page 41, section 5.\n"
				"                REF: Page 102, section 5.15."),
	        	      size->name);
            }

	    errors ++;
	  }

	 /*
	  * Check for PaperDimension...
	  */

          if (size->width <= 0.0 && size->length <= 0.0)
	  {
	    if (verbose >= 0)
	    {
	      if (!errors && !verbose)
		_cupsLangPuts(stdout, _(" FAIL"));

	      _cupsLangPrintf(stdout,
	                      _("      **FAIL**  REQUIRED PaperDimension "
			        "for PageSize %s\n"
				"                REF: Page 41, section 5.\n"
				"                REF: Page 103, section 5.15."),
	                      size->name);
            }

	    errors ++;
	  }
	}
      }

     /*
      * Check for valid Resolution, JCLResolution, or SetResolution values...
      */

      if ((option = ppdFindOption(ppd, "Resolution")) == NULL)
	if ((option = ppdFindOption(ppd, "JCLResolution")) == NULL)
          option = ppdFindOption(ppd, "SetResolution");

      if (option != NULL)
      {
        for (j = option->num_choices, choice = option->choices;
	     j > 0;
	     j --, choice ++)
        {
	 /*
	  * Verify that all resolution options are of the form NNNdpi
	  * or NNNxNNNdpi...
	  */

          xdpi = strtol(choice->choice, (char **)&ptr, 10);
	  if (ptr > choice->choice && xdpi > 0)
	  {
	    if (*ptr == 'x')
	      ydpi = strtol(ptr + 1, (char **)&ptr, 10);
	    else
	      ydpi = xdpi;
	  }
	  else
	    ydpi = xdpi;

	  if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 ||
	      strcmp(ptr, "dpi"))
	  {
	    if (verbose >= 0)
	    {
	      if (!errors && !verbose)
		_cupsLangPuts(stdout, _(" FAIL"));

	      _cupsLangPrintf(stdout,
	                      _("      **FAIL**  Bad option %s choice %s\n"
			        "                REF: Page 84, section 5.9"),
	                      option->keyword, choice->choice);
            }

	    errors ++;
	  }
	}
      }

      if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
          strcmp(attr->name, "1284DeviceID"))
      {
	if (verbose >= 0)
	{
	  if (!errors && !verbose)
	    _cupsLangPuts(stdout, _(" FAIL"));

	  _cupsLangPrintf(stdout,
	                  _("      **FAIL**  %s must be 1284DeviceID\n"
			    "                REF: Page 72, section 5.5"),
			  attr->name);
        }

	errors ++;
      }

      errors = check_case(ppd, errors, verbose);

      if (!(warn & WARN_CONSTRAINTS))
        errors = check_constraints(ppd, errors, verbose, 0);

      if (!(warn & WARN_FILTERS) && !(ignore & WARN_FILTERS))
        errors = check_filters(ppd, root, errors, verbose, 0);

      if (!(warn & WARN_PROFILES) && !(ignore & WARN_PROFILES))
        errors = check_profiles(ppd, root, errors, verbose, 0);

      if (!(warn & WARN_SIZES))
	errors = check_sizes(ppd, errors, verbose, 0);

      if (!(warn & WARN_TRANSLATIONS))
        errors = check_translations(ppd, errors, verbose, 0);

      if (!(warn & WARN_DUPLEX))
        errors = check_duplex(ppd, errors, verbose, 0);

      if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
	  attr->value)
      {
       /*
	* This file contains localizations, check for conformance of the
	* base translation...
	*/

        if ((attr = ppdFindAttr(ppd, "LanguageEncoding", NULL)) != NULL)
	{
	  if (!attr->value || strcmp(attr->value, "ISOLatin1"))
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

            if (verbose >= 0)
	      _cupsLangPrintf(stdout,
	                      _("      **FAIL**  Bad LanguageEncoding %s - "
			        "must be ISOLatin1."),
	                      attr->value ? attr->value : "(null)");

            errors ++;
	  }

          if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
	  {
	    if (!errors && !verbose)
	      _cupsLangPuts(stdout, _(" FAIL"));

            if (verbose >= 0)
	      _cupsLangPrintf(stdout,
	                      _("      **FAIL**  Bad LanguageVersion %s - "
			        "must be English."),
	                      ppd->lang_version ? ppd->lang_version : "(null)");

            errors ++;
	  }

	 /*
	  * Loop through all options and choices...
	  */

	  for (option = ppdFirstOption(ppd);
	       option;
	       option = ppdNextOption(ppd))
	  {
	   /*
	    * Check for special characters outside A0 to BF, F7, or F8
	    * that are used for languages other than English.
	    */

	    for (ptr = option->text; *ptr; ptr ++)
	      if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
		  (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
		break;

	    if (*ptr)
	    {
	      if (!errors && !verbose)
		_cupsLangPuts(stdout, _(" FAIL"));

	      if (verbose >= 0)
		_cupsLangPrintf(stdout,
				_("      **FAIL**  Default translation "
				  "string for option %s contains 8-bit "
				  "characters."),
				option->keyword);

	      errors ++;
	    }

	    for (j = 0; j < option->num_choices; j ++)
	    {
	     /*
	      * Check for special characters outside A0 to BF, F7, or F8
	      * that are used for languages other than English.
	      */

	      for (ptr = option->choices[j].text; *ptr; ptr ++)
		if ((*ptr & 0x80) && (*ptr & 0xe0) != 0xa0 &&
		    (*ptr & 0xff) != 0xf7 && (*ptr & 0xff) != 0xf8)
		  break;

	      if (*ptr)
	      {
		if (!errors && !verbose)
		  _cupsLangPuts(stdout, _(" FAIL"));

		if (verbose >= 0)
		  _cupsLangPrintf(stdout,
				  _("      **FAIL**  Default translation "
				    "string for option %s choice %s contains "
				    "8-bit characters."),
				  option->keyword,
				  option->choices[j].choice);

		errors ++;
	      }
	    }
	  }
	}
      }

     /*
      * Final pass/fail notification...
      */

      if (errors)
	status = ERROR_CONFORMANCE;
      else if (!verbose)
	_cupsLangPuts(stdout, _(" PASS"));

      if (verbose >= 0)
      {
        check_basics(argv[i]);

	if (warn & WARN_DEFAULTS)
	  errors = check_defaults(ppd, errors, verbose, 1);

	if (warn & WARN_CONSTRAINTS)
	  errors = check_constraints(ppd, errors, verbose, 1);

	if ((warn & WARN_FILTERS) && !(ignore & WARN_FILTERS))
	  errors = check_filters(ppd, root, errors, verbose, 1);

	if ((warn & WARN_PROFILES) && !(ignore & WARN_PROFILES))
	  errors = check_profiles(ppd, root, errors, verbose, 1);

        if (warn & WARN_SIZES)
	  errors = check_sizes(ppd, errors, verbose, 1);
        else
	  errors = check_sizes(ppd, errors, verbose, 2);

	if (warn & WARN_TRANSLATIONS)
	  errors = check_translations(ppd, errors, verbose, 1);

	if (warn & WARN_DUPLEX)
	  errors = check_duplex(ppd, errors, verbose, 1);

       /*
        * Look for legacy duplex keywords...
	*/

	if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
	  if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
	    option = ppdFindOption(ppd, "KD03Duplex");

	if (option)
	  _cupsLangPrintf(stdout,
			  _("        WARN    Duplex option keyword %s may not "
			    "work as expected and should be named Duplex.\n"
			    "                REF: Page 122, section 5.17"),
			  option->keyword);

       /*
	* Look for default keywords with no corresponding option...
	*/

	for (j = 0; j < ppd->num_attrs; j ++)
	{
	  attr = ppd->attrs[j];

          if (!strcmp(attr->name, "DefaultColorSpace") ||
	      !strcmp(attr->name, "DefaultColorSep") ||
	      !strcmp(attr->name, "DefaultFont") ||
	      !strcmp(attr->name, "DefaultHalftoneType") ||
	      !strcmp(attr->name, "DefaultImageableArea") ||
	      !strcmp(attr->name, "DefaultLeadingEdge") ||
	      !strcmp(attr->name, "DefaultOutputOrder") ||
	      !strcmp(attr->name, "DefaultPaperDimension") ||
	      !strcmp(attr->name, "DefaultResolution") ||
	      !strcmp(attr->name, "DefaultScreenProc") ||
	      !strcmp(attr->name, "DefaultTransfer"))
	    continue;

	  if (!strncmp(attr->name, "Default", 7) &&
	      !ppdFindOption(ppd, attr->name + 7))
            _cupsLangPrintf(stdout,
	                    _("        WARN    %s has no corresponding "
			      "options."),
	                    attr->name);
	}

        if (ppdversion < 43)
	{
          _cupsLangPrintf(stdout,
	                  _("        WARN    Obsolete PPD version %.1f.\n"
			    "                REF: Page 42, section 5.2."),
	        	  0.1f * ppdversion);
	}

        if (!ppd->lang_encoding && ppdversion < 41)
	{
	  _cupsLangPuts(stdout,
	                _("        WARN    LanguageEncoding required by PPD "
			  "4.3 spec.\n"
			  "                REF: Pages 56-57, section 5.3."));
	}

        if (!ppd->manufacturer && ppdversion < 43)
	{
	  _cupsLangPuts(stdout,
	                _("        WARN    Manufacturer required by PPD "
			  "4.3 spec.\n"
			  "                REF: Pages 58-59, section 5.3."));
	}

       /*
	* Treat a PCFileName attribute longer than 12 characters as
	* a warning and not a hard error...
	*/

        if (!(ignore & WARN_FILENAME) && ppd->pcfilename)
        {
	  if (strlen(ppd->pcfilename) > 12)
	  {
	    _cupsLangPuts(stdout,
			  _("        WARN    PCFileName longer than 8.3 in "
			    "violation of PPD spec.\n"
			    "                REF: Pages 61-62, section "
			    "5.3."));
	  }

	  if (!_cups_strcasecmp(ppd->pcfilename, "unused.ppd"))
	    _cupsLangPuts(stdout,
	                  _("        WARN    PCFileName should contain a "
	                    "unique filename.\n"
			    "                REF: Pages 61-62, section "
			    "5.3."));
        }

        if (!ppd->shortnickname && ppdversion < 43)
	{
	  _cupsLangPuts(stdout,
	                _("        WARN    ShortNickName required by PPD "
			  "4.3 spec.\n"
			  "                REF: Pages 64-65, section 5.3."));
	}

       /*
        * Check the Protocols line and flag PJL + BCP since TBCP is
	* usually used with PJL...
	*/

        if (ppd->protocols)
	{
	  if (strstr(ppd->protocols, "PJL") &&
	      strstr(ppd->protocols, "BCP") &&
	      !strstr(ppd->protocols, "TBCP"))
	  {
	    _cupsLangPuts(stdout,
	                  _("        WARN    Protocols contains both PJL "
			    "and BCP; expected TBCP.\n"
			    "                REF: Pages 78-79, section 5.7."));
	  }

	  if (strstr(ppd->protocols, "PJL") &&
	      (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
	  {
	    _cupsLangPuts(stdout,
	                  _("        WARN    Protocols contains PJL but JCL "
			    "attributes are not set.\n"
			    "                REF: Pages 78-79, section 5.7."));
	  }
	}

       /*
        * Check for options with a common prefix, e.g. Duplex and Duplexer,
	* which are errors according to the spec but won't cause problems
	* with CUPS specifically...
	*/

	for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
	  for (k = 0, option = group->options;
	       k < group->num_options;
	       k ++, option ++)
	  {
	    len = strlen(option->keyword);

	    for (m = 0, group2 = ppd->groups;
		 m < ppd->num_groups;
		 m ++, group2 ++)
	      for (n = 0, option2 = group2->options;
	           n < group2->num_options;
		   n ++, option2 ++)
		if (option != option2 &&
	            len < strlen(option2->keyword) &&
	            !strncmp(option->keyword, option2->keyword, len))
		{
		  _cupsLangPrintf(stdout,
		                  _("        WARN    %s shares a common "
				    "prefix with %s\n"
				    "                REF: Page 15, section "
				    "3.2."),
		                  option->keyword, option2->keyword);
        	}
	  }
      }

      if (verbose > 0)
      {
        if (errors)
          _cupsLangPrintf(stdout, _("    %d ERRORS FOUND"), errors);
	else
	  _cupsLangPuts(stdout, _("    NO ERRORS FOUND"));
      }

     /*
      * Then list the options, if "-v" was provided...
      */

      if (verbose > 1)
      {
	_cupsLangPrintf(stdout,
                        "\n"
		        "    language_level = %d\n"
			"    color_device = %s\n"
			"    variable_sizes = %s\n"
			"    landscape = %d",
			ppd->language_level,
			ppd->color_device ? "TRUE" : "FALSE",
			ppd->variable_sizes ? "TRUE" : "FALSE",
			ppd->landscape);

	switch (ppd->colorspace)
	{
	  case PPD_CS_CMYK :
              _cupsLangPuts(stdout, "    colorspace = PPD_CS_CMYK");
	      break;
	  case PPD_CS_CMY :
              _cupsLangPuts(stdout, "    colorspace = PPD_CS_CMY");
	      break;
	  case PPD_CS_GRAY :
              _cupsLangPuts(stdout, "    colorspace = PPD_CS_GRAY");
	      break;
	  case PPD_CS_RGB :
              _cupsLangPuts(stdout, "    colorspace = PPD_CS_RGB");
	      break;
	  default :
              _cupsLangPuts(stdout, "    colorspace = <unknown>");
	      break;
	}

	_cupsLangPrintf(stdout, "    num_emulations = %d",
			ppd->num_emulations);
	for (j = 0; j < ppd->num_emulations; j ++)
	  _cupsLangPrintf(stdout, "        emulations[%d] = %s",
	                  j, ppd->emulations[j].name);

	_cupsLangPrintf(stdout, "    lang_encoding = %s",
	                ppd->lang_encoding);
	_cupsLangPrintf(stdout, "    lang_version = %s",
	                ppd->lang_version);
	_cupsLangPrintf(stdout, "    modelname = %s", ppd->modelname);
	_cupsLangPrintf(stdout, "    ttrasterizer = %s",
        		ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
	_cupsLangPrintf(stdout, "    manufacturer = %s",
	                ppd->manufacturer);
	_cupsLangPrintf(stdout, "    product = %s", ppd->product);
	_cupsLangPrintf(stdout, "    nickname = %s", ppd->nickname);
	_cupsLangPrintf(stdout, "    shortnickname = %s",
	                ppd->shortnickname);
	_cupsLangPrintf(stdout, "    patches = %d bytes",
        		ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));

	_cupsLangPrintf(stdout, "    num_groups = %d", ppd->num_groups);
	for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
	{
	  _cupsLangPrintf(stdout, "        group[%d] = %s",
	                  j, group->text);

	  for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
	  {
	    _cupsLangPrintf(stdout,
	                    "            options[%d] = %s (%s) %s %s %.0f "
			    "(%d choices)",
	        	    k, option->keyword, option->text, uis[option->ui],
			    sections[option->section], option->order,
			    option->num_choices);

            if (!strcmp(option->keyword, "PageSize") ||
        	!strcmp(option->keyword, "PageRegion"))
            {
              for (m = option->num_choices, choice = option->choices;
		   m > 0;
		   m --, choice ++)
	      {
		size = ppdPageSize(ppd, choice->choice);

		if (size == NULL)
		  _cupsLangPrintf(stdout,
                                  "                %s (%s) = ERROR%s",
				  choice->choice, choice->text,
				  !strcmp(option->defchoice, choice->choice)
				      ? " *" : "");
        	else
		  _cupsLangPrintf(stdout,
                                  "                %s (%s) = %.2fx%.2fin "
				  "(%.1f,%.1f,%.1f,%.1f)%s",
		        	  choice->choice, choice->text,
				  size->width / 72.0, size->length / 72.0,
				  size->left / 72.0, size->bottom / 72.0,
				  size->right / 72.0, size->top / 72.0,
				  !strcmp(option->defchoice, choice->choice)
				      ? " *" : "");
              }
	    }
	    else
	    {
	      for (m = option->num_choices, choice = option->choices;
		   m > 0;
		   m --, choice ++)
	      {
		_cupsLangPrintf(stdout, "                %s (%s)%s",
		                choice->choice, choice->text,
				!strcmp(option->defchoice, choice->choice)
				    ? " *" : "");
	      }
            }
	  }
	}

	_cupsLangPrintf(stdout, "    num_consts = %d",
	                ppd->num_consts);
	for (j = 0; j < ppd->num_consts; j ++)
	  _cupsLangPrintf(stdout,
                	  "        consts[%d] = *%s %s *%s %s",
        		  j, ppd->consts[j].option1, ppd->consts[j].choice1,
			  ppd->consts[j].option2, ppd->consts[j].choice2);

	_cupsLangPrintf(stdout, "    num_profiles = %d",
	                ppd->num_profiles);
	for (j = 0; j < ppd->num_profiles; j ++)
	  _cupsLangPrintf(stdout,
                	  "        profiles[%d] = %s/%s %.3f %.3f "
			  "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]",
        		  j, ppd->profiles[j].resolution,
			  ppd->profiles[j].media_type,
			  ppd->profiles[j].gamma, ppd->profiles[j].density,
			  ppd->profiles[j].matrix[0][0],
			  ppd->profiles[j].matrix[0][1],
			  ppd->profiles[j].matrix[0][2],
			  ppd->profiles[j].matrix[1][0],
			  ppd->profiles[j].matrix[1][1],
			  ppd->profiles[j].matrix[1][2],
			  ppd->profiles[j].matrix[2][0],
			  ppd->profiles[j].matrix[2][1],
			  ppd->profiles[j].matrix[2][2]);

	_cupsLangPrintf(stdout, "    num_fonts = %d", ppd->num_fonts);
	for (j = 0; j < ppd->num_fonts; j ++)
	  _cupsLangPrintf(stdout, "        fonts[%d] = %s",
	                  j, ppd->fonts[j]);

	_cupsLangPrintf(stdout, "    num_attrs = %d", ppd->num_attrs);
	for (j = 0; j < ppd->num_attrs; j ++)
	  _cupsLangPrintf(stdout,
	                  "        attrs[%d] = %s %s%s%s: \"%s\"", j,
	        	  ppd->attrs[j]->name, ppd->attrs[j]->spec,
			  ppd->attrs[j]->text[0] ? "/" : "",
			  ppd->attrs[j]->text,
			  ppd->attrs[j]->value ?
			      ppd->attrs[j]->value : "(null)");
      }

      ppdClose(ppd);
    }

  if (!files)
    usage();

  return (status);
}