]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - systemv/cupstestppd.c
Import CUPS trunk (1.4svn) r7116.
[thirdparty/cups.git] / systemv / cupstestppd.c
index cb16ec5b28ecbd2684b053ff88f61ed9af7e57d8..8e7201f407eb1b9514be4220d9b264fd36277229 100644 (file)
@@ -1,25 +1,16 @@
 /*
- * "$Id: cupstestppd.c 4906 2006-01-10 20:53:28Z mike $"
+ * "$Id: cupstestppd.c 6927 2007-09-07 16:51:00Z mike $"
  *
  *   PPD test program for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *   Copyright 2007 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   PostScript is a trademark of Adobe Systems, Inc.
  *
  *
  * Contents:
  *
- *   main()           - Main entry for test program.
- *   show_conflicts() - Show option conflicts in a PPD file.
- *   usage()          - Show program usage...
+ *   main()               - Main entry for test program.
+ *   check_basics()       - Check for CR LF, mixed line endings, and blank lines.
+ *   check_constraints()  - Check UIConstraints in the PPD file.
+ *   check_defaults()     - Check default option keywords in the PPD file.
+ *   check_filters()      - Check filters in the PPD file.
+ *   check_translations() - Check translations in the PPD file.
+ *   show_conflicts()     - Show option conflicts in a PPD file.
+ *   test_raster()        - Test PostScript commands for raster printers.
+ *   usage()              - Show program usage...
+ *   valid_utf8()         - Check whether a string contains valid UTF-8 text.
  */
 
 /*
 #include <cups/string.h>
 #include <cups/cups.h>
 #include <cups/i18n.h>
+#include <filter/raster.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <sys/stat.h>
+
+
+/*
+ * Error warning overrides...
+ */
+
+enum
+{
+  WARN_NONE = 0,
+  WARN_CONSTRAINTS = 1,
+  WARN_DEFAULTS = 2,
+  WARN_FILTERS = 4,
+  WARN_TRANSLATIONS = 8,
+  WARN_ALL = 15
+};
 
 
 /*
  * Error codes...
  */
 
-#define ERROR_NONE             0
-#define ERROR_USAGE            1
-#define ERROR_FILE_OPEN                2
-#define ERROR_PPD_FORMAT       3
-#define ERROR_CONFORMANCE      4
+enum
+{
+  ERROR_NONE = 0,
+  ERROR_USAGE,
+  ERROR_FILE_OPEN,
+  ERROR_PPD_FORMAT,
+  ERROR_CONFORMANCE
+};
+
+
+/*
+ * Line endings...
+ */
+
+enum
+{
+  EOL_NONE = 0,
+  EOL_CR,
+  EOL_LF,
+  EOL_CRLF
+};
 
 
 /*
  * Local functions...
  */
 
-void   show_conflicts(ppd_file_t *ppd);
-void   usage(void);
+static void    check_basics(const char *filename);
+static int     check_constraints(ppd_file_t *ppd, int errors, int verbose,
+                                 int warn);
+static int     check_defaults(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_translations(ppd_file_t *ppd, int errors, int verbose,\
+                                  int warn);
+static void    show_conflicts(ppd_file_t *ppd);
+static int     test_raster(ppd_file_t *ppd, int verbose);
+static void    usage(void);
+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 arguments */
-     char *argv[])             /* I - Command-line arguments */
+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 */
-  int          len;            /* Length of option name */
-  char         *opt;           /* Option character */
-  const char   *ptr;           /* Pointer into string */
-  int          files;          /* Number of files */
-  int          verbose;        /* Want verbose output? */
-  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 */
-  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 */
+  int          i, j, k, m, n;          /* Looping vars */
+  int          len;                    /* Length of option name */
+  char         *opt;                   /* Option character */
+  const char   *ptr;                   /* Pointer into string */
+  int          files;                  /* Number of files */
+  int          verbose;                /* Want verbose output? */
+  int          warn;                   /* Which errors to just warn about */
+  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 */
+  struct stat  statbuf;                /* File information */
+  char         pathprog[1024],         /* Complete path to program/filter */
+               *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 */
   static char  *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
   static char  *sections[] = { "ANY", "DOCUMENT", "EXIT",
                                 "JCL", "PAGE", "PROLOG" };
 
 
+  _cupsSetLocale(argv);
+
  /*
   * Display PPD files for each file listed on the command-line...
   */
@@ -106,6 +154,8 @@ main(int  argc,                     /* I - Number of command-line arguments */
   ppd     = NULL;
   files   = 0;
   status  = ERROR_NONE;
+  root    = "";
+  warn    = WARN_NONE;
 
   for (i = 1; i < argc; i ++)
     if (argv[i][0] == '-' && argv[i][1])
@@ -113,10 +163,41 @@ main(int  argc,                   /* I - Number of command-line arguments */
       for (opt = argv[i] + 1; *opt; opt ++)
         switch (*opt)
        {
+         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], "filters"))
+               warn |= WARN_FILTERS;
+             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, NULL,
+               _cupsLangPuts(stderr,
                              _("cupstestppd: The -q option is incompatible "
                                "with the -v option.\n"));
                return (1);
@@ -132,7 +213,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
          case 'v' :                    /* Verbose mode */
              if (verbose < 0)
              {
-               _cupsLangPuts(stderr, NULL,
+               _cupsLangPuts(stderr,
                              _("cupstestppd: The -v option is incompatible "
                                "with the -q option.\n"));
                return (1);
@@ -153,7 +234,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
       */
 
       if (files && verbose >= 0)
-        _cupsLangPuts(stdout, NULL, "\n");
+        _cupsLangPuts(stdout, "\n");
 
       files ++;
 
@@ -189,7 +270,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
          status = ERROR_FILE_OPEN;
 
          if (verbose >= 0)
-            _cupsLangPrintf(stdout, NULL,
+            _cupsLangPrintf(stdout,
                            _(" FAIL\n"
                              "      **FAIL**  Unable to open PPD file - %s\n"),
                            strerror(errno));
@@ -200,7 +281,7 @@ main(int  argc,                     /* I - Number of command-line arguments */
 
           if (verbose >= 0)
          {
-            _cupsLangPrintf(stdout, NULL,
+            _cupsLangPrintf(stdout,
                            _(" FAIL\n"
                              "      **FAIL**  Unable to open PPD file - "
                              "%s on line %d.\n"),
@@ -209,58 +290,60 @@ main(int  argc,                   /* I - Number of command-line arguments */
             switch (error)
            {
              case PPD_MISSING_PPDADOBE4 :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Page 42, section 5.2.\n"));
                  break;
              case PPD_MISSING_VALUE :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Page 20, section 3.4.\n"));
                  break;
              case PPD_BAD_OPEN_GROUP :
              case PPD_NESTED_OPEN_GROUP :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Pages 45-46, section 5.2.\n"));
                  break;
              case PPD_BAD_OPEN_UI :
              case PPD_NESTED_OPEN_UI :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Pages 42-45, section 5.2.\n"));
                  break;
              case PPD_BAD_ORDER_DEPENDENCY :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Pages 48-49, section 5.2.\n"));
                  break;
              case PPD_BAD_UI_CONSTRAINTS :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Pages 52-54, section 5.2.\n"));
                  break;
              case PPD_MISSING_ASTERISK :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Page 15, section 3.2.\n"));
                  break;
              case PPD_LINE_TOO_LONG :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Page 15, section 3.1.\n"));
                  break;
              case PPD_ILLEGAL_CHARACTER :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Page 15, section 3.1.\n"));
                  break;
              case PPD_ILLEGAL_MAIN_KEYWORD :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Pages 16-17, section 3.2.\n"));
                  break;
              case PPD_ILLEGAL_OPTION_KEYWORD :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Page 19, section 3.3.\n"));
                  break;
              case PPD_ILLEGAL_TRANSLATION :
-                 _cupsLangPuts(stdout, NULL,
+                 _cupsLangPuts(stdout,
                                _("                REF: Page 27, section 3.5.\n"));
                  break;
               default :
                  break;
            }
+
+           check_basics(argv[i]);
          }
         }
 
@@ -276,74 +359,62 @@ main(int  argc,                   /* I - Number of command-line arguments */
       ppdversion = 43;
 
       if (verbose > 0)
-        _cupsLangPuts(stdout, NULL,
+        _cupsLangPuts(stdout,
                      _("\n    DETAILED CONFORMANCE TEST RESULTS\n"));
 
       if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL &&
           attr->value)
         ppdversion = (int)(10 * atof(attr->value) + 0.5);
 
-      if (verbose > 0)
-      {
-       /*
-        * Look for default keywords with no matching option...
-       */
-
-        for (i = 0; i < ppd->num_attrs; i ++)
+      for (j = 0; j < ppd->num_filters; j ++)
+        if (strstr(ppd->filters[j], "application/vnd.cups-raster"))
        {
-         attr = ppd->attrs[i];
+         if (!test_raster(ppd, verbose))
+           errors ++;
+         break;
+       }
 
-          if (!strcmp(attr->name, "DefaultColorSpace") ||
-             !strcmp(attr->name, "DefaultFont") ||
-             !strcmp(attr->name, "DefaultImageableArea") ||
-             !strcmp(attr->name, "DefaultOutputOrder") ||
-             !strcmp(attr->name, "DefaultPaperDimension") ||
-             !strcmp(attr->name, "DefaultTransfer"))
-           continue;
-             
-         if (!strncmp(attr->name, "Default", 7) &&
-             !ppdFindOption(ppd, attr->name + 7))
-            _cupsLangPrintf(stdout, NULL,
-                           _("        WARN    %s has no corresponding "
-                             "options!\n"),
-                           attr->name);
-        }
-      }
+     /*
+      * 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, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
-                       _("      **FAIL**  REQUIRED DefaultImageableArea\n"
+         _cupsLangPuts(stdout,
+                       _("      **FAIL**  REQUIRED DefaultImageableArea\n"
                          "                REF: Page 102, section 5.15.\n"));
-        }
+       }
 
        errors ++;
       }
       else if (ppdPageSize(ppd, attr->value) == NULL &&
-               strcmp(attr->value, "Unknown"))
+              strcmp(attr->value, "Unknown"))
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPrintf(stdout, NULL,
-                         _("      **FAIL**  BAD DefaultImageableArea %s!\n"
+         _cupsLangPrintf(stdout,
+                         _("      **FAIL**  BAD DefaultImageableArea %s!\n"
                            "                REF: Page 102, section 5.15.\n"),
                          attr->value);
-        }
+       }
 
        errors ++;
       }
       else
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    DefaultImageableArea\n"));
+         _cupsLangPuts(stdout, _("        PASS    DefaultImageableArea\n"));
       }
 
       if ((attr = ppdFindAttr(ppd, "DefaultPaperDimension", NULL)) == NULL)
@@ -351,33 +422,33 @@ main(int  argc,                   /* I - Number of command-line arguments */
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
-                       _("      **FAIL**  REQUIRED DefaultPaperDimension\n"
+         _cupsLangPuts(stdout,
+                       _("      **FAIL**  REQUIRED DefaultPaperDimension\n"
                          "                REF: Page 103, section 5.15.\n"));
-        }
+       }
 
        errors ++;
       }
       else if (ppdPageSize(ppd, attr->value) == NULL &&
-               strcmp(attr->value, "Unknown"))
+              strcmp(attr->value, "Unknown"))
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPrintf(stdout, NULL,
-                         _("      **FAIL**  BAD DefaultPaperDimension %s!\n"
+         _cupsLangPrintf(stdout,
+                         _("      **FAIL**  BAD DefaultPaperDimension %s!\n"
                            "                REF: Page 103, section 5.15.\n"),
                          attr->value);
-        }
+       }
 
        errors ++;
       }
       else if (verbose > 0)
-       _cupsLangPuts(stdout, NULL, _("        PASS    DefaultPaperDimension\n"));
+       _cupsLangPuts(stdout, _("        PASS    DefaultPaperDimension\n"));
 
       for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
        for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
@@ -388,25 +459,25 @@ main(int  argc,                   /* I - Number of command-line arguments */
 
          if (option->defchoice[0])
          {
-            if (ppdFindChoice(option, option->defchoice) == NULL &&
-               strcmp(option->defchoice, "Unknown"))
+           if (ppdFindChoice(option, option->defchoice) == NULL &&
+               strcmp(option->defchoice, "Unknown"))
            {
              if (verbose >= 0)
              {
                if (!errors && !verbose)
-                 _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+                 _cupsLangPuts(stdout, _(" FAIL\n"));
 
-               _cupsLangPrintf(stdout, NULL,
-                               _("      **FAIL**  BAD Default%s %s\n"
+               _cupsLangPrintf(stdout,
+                               _("      **FAIL**  BAD Default%s %s\n"
                                  "                REF: Page 40, section 4.5.\n"),
                                option->keyword, option->defchoice);
-              }
+             }
 
              errors ++;
            }
            else if (verbose > 0)
-             _cupsLangPrintf(stdout, NULL,
-                             _("        PASS    Default%s\n"),
+             _cupsLangPrintf(stdout,
+                             _("        PASS    Default%s\n"),
                              option->keyword);
          }
          else
@@ -414,13 +485,13 @@ main(int  argc,                   /* I - Number of command-line arguments */
            if (verbose >= 0)
            {
              if (!errors && !verbose)
-               _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+               _cupsLangPuts(stdout, _(" FAIL\n"));
 
-             _cupsLangPrintf(stdout, NULL,
-                             _("      **FAIL**  REQUIRED Default%s\n"
-                               "                REF: Page 40, section 4.5.\n"),
-                             option->keyword);
-            }
+             _cupsLangPrintf(stdout,
+                             _("      **FAIL**  REQUIRED Default%s\n"
+                               "                REF: Page 40, section 4.5.\n"),
+                             option->keyword);
+           }
 
            errors ++;
          }
@@ -429,16 +500,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppdFindAttr(ppd, "FileVersion", NULL) != NULL)
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    FileVersion\n"));
+         _cupsLangPuts(stdout, _("        PASS    FileVersion\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED FileVersion\n"
                          "                REF: Page 56, section 5.3.\n"));
         }
@@ -449,16 +520,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppdFindAttr(ppd, "FormatVersion", NULL) != NULL)
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    FormatVersion\n"));
+         _cupsLangPuts(stdout, _("        PASS    FormatVersion\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED FormatVersion\n"
                          "                REF: Page 56, section 5.3.\n"));
         }
@@ -469,16 +540,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppd->lang_encoding != NULL)
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    LanguageEncoding\n"));
+         _cupsLangPuts(stdout, _("        PASS    LanguageEncoding\n"));
       }
       else if (ppdversion > 40)
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED LanguageEncoding\n"
                          "                REF: Pages 56-57, section 5.3.\n"));
         }
@@ -489,16 +560,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppd->lang_version != NULL)
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    LanguageVersion\n"));
+         _cupsLangPuts(stdout, _("        PASS    LanguageVersion\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED LanguageVersion\n"
                          "                REF: Pages 57-58, section 5.3.\n"));
         }
@@ -514,9 +585,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
          if (verbose >= 0)
          {
            if (!errors && !verbose)
-             _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+             _cupsLangPuts(stdout, _(" FAIL\n"));
 
-           _cupsLangPuts(stdout, NULL,
+           _cupsLangPuts(stdout,
                          _("      **FAIL**  BAD Manufacturer (should be "
                            "\"HP\")\n"
                            "                REF: Page 211, table D.1.\n"));
@@ -524,17 +595,33 @@ main(int  argc,                   /* I - Number of command-line arguments */
 
          errors ++;
        }
+        else if (!strncasecmp(ppd->manufacturer, "OkiData", 7) ||
+                !strncasecmp(ppd->manufacturer, "Oki Data", 8))
+       {
+         if (verbose >= 0)
+         {
+           if (!errors && !verbose)
+             _cupsLangPuts(stdout, _(" FAIL\n"));
+
+           _cupsLangPuts(stdout,
+                         _("      **FAIL**  BAD Manufacturer (should be "
+                           "\"Oki\")\n"
+                           "                REF: Page 211, table D.1.\n"));
+          }
+
+         errors ++;
+       }
        else if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    Manufacturer\n"));
+         _cupsLangPuts(stdout, _("        PASS    Manufacturer\n"));
       }
       else if (ppdversion >= 43)
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED Manufacturer\n"
                          "                REF: Pages 58-59, section 5.3.\n"));
         }
@@ -553,9 +640,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
          if (verbose >= 0)
          {
            if (!errors && !verbose)
-             _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+             _cupsLangPuts(stdout, _(" FAIL\n"));
 
-           _cupsLangPrintf(stdout, NULL,
+           _cupsLangPrintf(stdout,
                            _("      **FAIL**  BAD ModelName - \"%c\" not "
                              "allowed in string.\n"
                              "                REF: Pages 59-60, section 5.3.\n"),
@@ -565,16 +652,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
          errors ++;
        }
        else if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    ModelName\n"));
+         _cupsLangPuts(stdout, _("        PASS    ModelName\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED ModelName\n"
                          "                REF: Pages 59-60, section 5.3.\n"));
         }
@@ -585,16 +672,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppd->nickname != NULL)
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    NickName\n"));
+         _cupsLangPuts(stdout, _("        PASS    NickName\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED NickName\n"
                          "                REF: Page 60, section 5.3.\n"));
         }
@@ -605,16 +692,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppdFindOption(ppd, "PageSize") != NULL)
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    PageSize\n"));
+         _cupsLangPuts(stdout, _("        PASS    PageSize\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED PageSize\n"
                          "                REF: Pages 99-100, section 5.14.\n"));
         }
@@ -625,16 +712,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppdFindOption(ppd, "PageRegion") != NULL)
       {
        if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    PageRegion\n"));
+         _cupsLangPuts(stdout, _("        PASS    PageRegion\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED PageRegion\n"
                          "                REF: Page 100, section 5.14.\n"));
         }
@@ -645,16 +732,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
       if (ppd->pcfilename != NULL)
       {
        if (verbose > 0)
-          _cupsLangPuts(stdout, NULL, _("        PASS    PCFileName\n"));
+          _cupsLangPuts(stdout, _("        PASS    PCFileName\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED PCFileName\n"
                          "                REF: Pages 61-62, section 5.3.\n"));
         }
@@ -670,9 +757,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
          if (verbose >= 0)
          {
            if (!errors && !verbose)
-             _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+             _cupsLangPuts(stdout, _(" FAIL\n"));
 
-           _cupsLangPuts(stdout, NULL,
+           _cupsLangPuts(stdout,
                          _("      **FAIL**  BAD Product - not \"(string)\".\n"
                            "                REF: Page 62, section 5.3.\n"));
           }
@@ -680,16 +767,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
          errors ++;
        }
        else if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    Product\n"));
+         _cupsLangPuts(stdout, _("        PASS    Product\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED Product\n"
                          "                REF: Page 62, section 5.3.\n"));
         }
@@ -709,9 +796,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
          if (verbose >= 0)
          {
            if (!errors && !verbose)
-             _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+             _cupsLangPuts(stdout, _(" FAIL\n"));
 
-           _cupsLangPuts(stdout, NULL,
+           _cupsLangPuts(stdout,
                          _("      **FAIL**  BAD PSVersion - not \"(string) "
                            "int\".\n"
                            "                REF: Pages 62-64, section 5.3.\n"));
@@ -720,16 +807,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
          errors ++;
        }
        else if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    PSVersion\n"));
+         _cupsLangPuts(stdout, _("        PASS    PSVersion\n"));
       }
       else
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED PSVersion\n"
                          "                REF: Pages 62-64, section 5.3.\n"));
         }
@@ -744,9 +831,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
          if (verbose >= 0)
          {
            if (!errors && !verbose)
-             _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+             _cupsLangPuts(stdout, _(" FAIL\n"));
 
-           _cupsLangPuts(stdout, NULL,
+           _cupsLangPuts(stdout,
                          _("      **FAIL**  BAD ShortNickName - longer "
                            "than 31 chars.\n"
                            "                REF: Pages 64-65, section 5.3.\n"));
@@ -755,16 +842,16 @@ main(int  argc,                   /* I - Number of command-line arguments */
          errors ++;
        }
        else if (verbose > 0)
-         _cupsLangPuts(stdout, NULL, _("        PASS    ShortNickName\n"));
+         _cupsLangPuts(stdout, _("        PASS    ShortNickName\n"));
       }
       else if (ppdversion >= 43)
       {
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED ShortNickName\n"
                          "                REF: Page 64-65, section 5.3.\n"));
         }
@@ -778,9 +865,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  BAD JobPatchFile attribute in file\n"
                          "                REF: Page 24, section 3.4.\n"));
         }
@@ -798,9 +885,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
        if (verbose >= 0)
        {
          if (!errors && !verbose)
-           _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+           _cupsLangPuts(stdout, _(" FAIL\n"));
 
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("      **FAIL**  REQUIRED PageSize\n"
                          "                REF: Page 41, section 5.\n"
                          "                REF: Page 99, section 5.14.\n"));
@@ -829,9 +916,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
            if (verbose >= 0)
            {
              if (!errors && !verbose)
-               _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+               _cupsLangPuts(stdout, _(" FAIL\n"));
 
-             _cupsLangPrintf(stdout, NULL,
+             _cupsLangPrintf(stdout,
                              _("      **FAIL**  REQUIRED ImageableArea for "
                                "PageSize %s\n"
                                "                REF: Page 41, section 5.\n"
@@ -851,9 +938,9 @@ main(int  argc,                     /* I - Number of command-line arguments */
            if (verbose >= 0)
            {
              if (!errors && !verbose)
-               _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+               _cupsLangPuts(stdout, _(" FAIL\n"));
 
-             _cupsLangPrintf(stdout, NULL,
+             _cupsLangPrintf(stdout,
                              _("      **FAIL**  REQUIRED PaperDimension "
                                "for PageSize %s\n"
                                "                REF: Page 41, section 5.\n"
@@ -894,14 +981,15 @@ main(int  argc,                   /* I - Number of command-line arguments */
          else
            ydpi = xdpi;
 
-         if (xdpi <= 0 || ydpi <= 0 || strcmp(ptr, "dpi"))
+         if (xdpi <= 0 || xdpi > 99999 || ydpi <= 0 || ydpi > 99999 ||
+             strcmp(ptr, "dpi"))
          {
            if (verbose >= 0)
            {
              if (!errors && !verbose)
-               _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+               _cupsLangPuts(stdout, _(" FAIL\n"));
 
-             _cupsLangPrintf(stdout, NULL,
+             _cupsLangPrintf(stdout,
                              _("      **FAIL**  Bad %s choice %s!\n"
                                "                REF: Page 84, section 5.9\n"),
                              option->keyword, choice->choice);
@@ -928,9 +1016,9 @@ main(int  argc,                    /* I - Number of command-line arguments */
          if (verbose >= 0)
          {
            if (!errors && !verbose)
-             _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+             _cupsLangPuts(stdout, _(" FAIL\n"));
 
-           _cupsLangPrintf(stdout, NULL,
+           _cupsLangPrintf(stdout,
                            _("      **FAIL**  REQUIRED %s does not define "
                              "choice None!\n"
                              "                REF: Page 122, section 5.17\n"),
@@ -949,9 +1037,9 @@ main(int  argc,                    /* I - Number of command-line arguments */
            if (verbose >= 0)
            {
              if (!errors && !verbose)
-               _cupsLangPuts(stdout, NULL, _(" FAIL\n"));
+               _cupsLangPuts(stdout, _(" FAIL\n"));
 
-             _cupsLangPrintf(stdout, NULL,
+             _cupsLangPrintf(stdout,
                              _("      **FAIL**  Bad %s choice %s!\n"
                                "                REF: Page 122, section 5.17\n"),
                              option->keyword, choice->choice);
@@ -961,18 +1049,199 @@ main(int  argc,                 /* I - Number of command-line arguments */
          }
       }
 
+      if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
+          strcmp(attr->name, "1284DeviceID"))
+      {
+       if (verbose >= 0)
+       {
+         if (!errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         _cupsLangPrintf(stdout,
+                         _("      **FAIL**  %s must be 1284DeviceID!\n"
+                           "                REF: Page 72, section 5.5\n"),
+                         attr->name);
+        }
+
+       errors ++;
+      }
+
+      if (!(warn & WARN_CONSTRAINTS))
+        errors = check_constraints(ppd, errors, verbose, 0);
+
+      if (!(warn & WARN_FILTERS))
+        errors = check_filters(ppd, root, errors, verbose, 0);
+
+      if (!(warn & WARN_TRANSLATIONS))
+        errors = check_translations(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\n"));
+
+            if (verbose >= 0)
+             _cupsLangPrintf(stdout,
+                             _("      **FAIL**  Bad LanguageEncoding %s - "
+                               "must be ISOLatin1!\n"),
+                             attr->value ? attr->value : "(null)");
+
+            errors ++;
+         }
+
+          if (!ppd->lang_version || strcmp(ppd->lang_version, "English"))
+         {
+           if (!errors && !verbose)
+             _cupsLangPuts(stdout, _(" FAIL\n"));
+
+            if (verbose >= 0)
+             _cupsLangPrintf(stdout,
+                             _("      **FAIL**  Bad LanguageVersion %s - "
+                               "must be English!\n"),
+                             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\n"));
+
+             if (verbose >= 0)
+               _cupsLangPrintf(stdout,
+                               _("      **FAIL**  Default translation "
+                                 "string for option %s contains 8-bit "
+                                 "characters!\n"),
+                               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\n"));
+
+               if (verbose >= 0)
+                 _cupsLangPrintf(stdout,
+                                 _("      **FAIL**  Default translation "
+                                   "string for option %s choice %s contains "
+                                   "8-bit characters!\n"),
+                                 option->keyword,
+                                 option->choices[j].choice);
+
+               errors ++;
+             }
+           }
+         }
+       }
+      }
+
+     /*
+      * Final pass/fail notification...
+      */
+
       if (errors)
        status = ERROR_CONFORMANCE;
       else if (!verbose)
-       _cupsLangPuts(stdout, NULL, _(" PASS\n"));
-        
+       _cupsLangPuts(stdout, _(" PASS\n"));
+
       if (verbose >= 0)
       {
-        if (option &&
-           strcmp(option->keyword, "Duplex") &&
-           strcmp(option->keyword, "JCLDuplex"))
+        check_basics(argv[i]);
+
+       if (warn & WARN_CONSTRAINTS)
+         errors = check_constraints(ppd, errors, verbose, 1);
+
+       if (warn & WARN_DEFAULTS)
+         errors = check_defaults(ppd, errors, verbose, 1);
+
+       if (warn & WARN_FILTERS)
+         errors = check_filters(ppd, root, errors, verbose, 1);
+
+       if (warn & WARN_TRANSLATIONS)
+         errors = check_translations(ppd, errors, verbose, 1);
+
+       /*
+       * Look for default keywords with no corresponding option...
+       */
+
+       for (j = 0; j < ppd->num_attrs; j ++)
        {
-         _cupsLangPrintf(stdout, NULL,
+         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!\n"),
+                           attr->name);
+       }
+
+       /*
+        * Check for old Duplex option names...
+       */
+
+       if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
+          option = ppdFindOption(ppd, "KD03Duplex");
+
+        if (option)
+       {
+         _cupsLangPrintf(stdout,
                          _("        WARN    Duplex option keyword %s "
                            "should be named Duplex or JCLDuplex!\n"
                            "                REF: Page 122, section 5.17\n"),
@@ -982,7 +1251,7 @@ main(int  argc,                    /* I - Number of command-line arguments */
         ppdMarkDefaults(ppd);
        if (ppdConflicts(ppd))
        {
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("        WARN    Default choices conflicting!\n"));
 
           show_conflicts(ppd);
@@ -990,7 +1259,7 @@ main(int  argc,                    /* I - Number of command-line arguments */
 
         if (ppdversion < 43)
        {
-          _cupsLangPrintf(stdout, NULL,
+          _cupsLangPrintf(stdout,
                          _("        WARN    Obsolete PPD version %.1f!\n"
                            "                REF: Page 42, section 5.2.\n"),
                          0.1f * ppdversion);
@@ -998,7 +1267,7 @@ main(int  argc,                    /* I - Number of command-line arguments */
 
         if (!ppd->lang_encoding && ppdversion < 41)
        {
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("        WARN    LanguageEncoding required by PPD "
                          "4.3 spec.\n"
                          "                REF: Pages 56-57, section 5.3.\n"));
@@ -1006,7 +1275,7 @@ main(int  argc,                   /* I - Number of command-line arguments */
 
         if (!ppd->manufacturer && ppdversion < 43)
        {
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("        WARN    Manufacturer required by PPD "
                          "4.3 spec.\n"
                          "                REF: Pages 58-59, section 5.3.\n"));
@@ -1019,7 +1288,7 @@ main(int  argc,                   /* I - Number of command-line arguments */
 
        if (ppd->pcfilename && strlen(ppd->pcfilename) > 12)
        {
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("        WARN    PCFileName longer than 8.3 in "
                          "violation of PPD spec.\n"
                          "                REF: Pages 61-62, section 5.3.\n"));
@@ -1027,7 +1296,7 @@ main(int  argc,                   /* I - Number of command-line arguments */
 
         if (!ppd->shortnickname && ppdversion < 43)
        {
-         _cupsLangPuts(stdout, NULL,
+         _cupsLangPuts(stdout,
                        _("        WARN    ShortNickName required by PPD "
                          "4.3 spec.\n"
                          "                REF: Pages 64-65, section 5.3.\n"));
@@ -1044,7 +1313,7 @@ main(int  argc,                   /* I - Number of command-line arguments */
              strstr(ppd->protocols, "BCP") &&
              !strstr(ppd->protocols, "TBCP"))
          {
-           _cupsLangPuts(stdout, NULL,
+           _cupsLangPuts(stdout,
                          _("        WARN    Protocols contains both PJL "
                            "and BCP; expected TBCP.\n"
                            "                REF: Pages 78-79, section 5.7.\n"));
@@ -1053,7 +1322,7 @@ main(int  argc,                   /* I - Number of command-line arguments */
          if (strstr(ppd->protocols, "PJL") &&
              (!ppd->jcl_begin || !ppd->jcl_end || !ppd->jcl_ps))
          {
-           _cupsLangPuts(stdout, NULL,
+           _cupsLangPuts(stdout,
                          _("        WARN    Protocols contains PJL but JCL "
                            "attributes are not set.\n"
                            "                REF: Pages 78-79, section 5.7.\n"));
@@ -1081,7 +1350,7 @@ main(int  argc,                   /* I - Number of command-line arguments */
                    len < strlen(option2->keyword) &&
                    !strncmp(option->keyword, option2->keyword, len))
                {
-                 _cupsLangPrintf(stdout, NULL,
+                 _cupsLangPrintf(stdout,
                                  _("        WARN    %s shares a common "
                                    "prefix with %s\n"
                                    "                REF: Page 15, section "
@@ -1091,23 +1360,88 @@ main(int  argc,                 /* I - Number of command-line arguments */
          }
       }
 
+     /*
+      * cupsICCProfile
+      */
+
+      for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); 
+          attr != NULL; 
+          attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+      {
+       if (attr->value)
+       {
+         if (attr->value[0] == '/')
+           snprintf(pathprog, sizeof(pathprog), "%s%s", root, attr->value);
+         else
+         {
+           if ((ptr = getenv("CUPS_DATADIR")) == NULL)
+             ptr = CUPS_DATADIR;
+
+            if (*ptr == '/' || !*root)
+             snprintf(pathprog, sizeof(pathprog), "%s%s/profiles/%s", root,
+                      ptr, attr->value);
+            else
+             snprintf(pathprog, sizeof(pathprog), "%s/%s/profiles/%s", root,
+                      ptr, attr->value);
+          }
+       }
+
+       if (!attr->value || !attr->value[0] || stat(pathprog, &statbuf))
+       {
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("        WARN    Missing cupsICCProfile "
+                             "file \"%s\"\n"),
+                           !attr->value || !attr->value[0] ? "<NULL>" :
+                                                             attr->value);
+       }
+      }
+
+#ifdef __APPLE__
+     /*
+      * APDialogExtension
+      */
+
+      for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL); 
+          attr != NULL; 
+          attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
+      {
+       if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
+         _cupsLangPrintf(stdout, _("        WARN    Missing "
+                                   "APDialogExtension file \"%s\"\n"),
+                         attr->value ? attr->value : "<NULL>");
+      }
+
+     /*
+      * APPrinterIconPath
+      */
+
+      for (attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL); 
+          attr != NULL; 
+          attr = ppdFindNextAttr(ppd, "APPrinterIconPath", NULL))
+      {
+       if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0)
+         _cupsLangPrintf(stdout, _("        WARN    Missing "
+                                   "APPrinterIconPath file \"%s\"\n"),
+                         attr->value ? attr->value : "<NULL>");
+      }
+#endif /* __APPLE__ */
+
       if (verbose > 0)
       {
         if (errors)
-          _cupsLangPrintf(stdout, NULL, _("    %d ERROR%s FOUND\n"),
-                         errors, errors == 1 ? "" : "S");
+          _cupsLangPrintf(stdout, _("    %d ERRORS FOUND\n"), errors);
        else
-         _cupsLangPuts(stdout, NULL, _("    NO ERRORS FOUND\n"));
+         _cupsLangPuts(stdout, _("    NO ERRORS FOUND\n"));
       }
 
-
      /*
       * Then list the options, if "-v" was provided...
       */ 
 
       if (verbose > 1)
       {
-       _cupsLangPrintf(stdout, NULL,
+       _cupsLangPrintf(stdout,
                         "\n"
                        "    language_level = %d\n"
                        "    color_device = %s\n"
@@ -1121,53 +1455,53 @@ main(int  argc,                 /* I - Number of command-line arguments */
        switch (ppd->colorspace)
        {
          case PPD_CS_CMYK :
-              _cupsLangPuts(stdout, NULL, "    colorspace = PPD_CS_CMYK\n");
+              _cupsLangPuts(stdout, "    colorspace = PPD_CS_CMYK\n");
              break;
          case PPD_CS_CMY :
-              _cupsLangPuts(stdout, NULL, "    colorspace = PPD_CS_CMY\n");
+              _cupsLangPuts(stdout, "    colorspace = PPD_CS_CMY\n");
              break;
          case PPD_CS_GRAY :
-              _cupsLangPuts(stdout, NULL, "    colorspace = PPD_CS_GRAY\n");
+              _cupsLangPuts(stdout, "    colorspace = PPD_CS_GRAY\n");
              break;
          case PPD_CS_RGB :
-              _cupsLangPuts(stdout, NULL, "    colorspace = PPD_CS_RGB\n");
+              _cupsLangPuts(stdout, "    colorspace = PPD_CS_RGB\n");
              break;
          default :
-              _cupsLangPuts(stdout, NULL, "    colorspace = <unknown>\n");
+              _cupsLangPuts(stdout, "    colorspace = <unknown>\n");
              break;
        }
 
-       _cupsLangPrintf(stdout, NULL, "    num_emulations = %d\n",
+       _cupsLangPrintf(stdout, "    num_emulations = %d\n",
                        ppd->num_emulations);
        for (j = 0; j < ppd->num_emulations; j ++)
-         _cupsLangPrintf(stdout, NULL, "        emulations[%d] = %s\n",
+         _cupsLangPrintf(stdout, "        emulations[%d] = %s\n",
                          j, ppd->emulations[j].name);
 
-       _cupsLangPrintf(stdout, NULL, "    lang_encoding = %s\n",
+       _cupsLangPrintf(stdout, "    lang_encoding = %s\n",
                        ppd->lang_encoding);
-       _cupsLangPrintf(stdout, NULL, "    lang_version = %s\n",
+       _cupsLangPrintf(stdout, "    lang_version = %s\n",
                        ppd->lang_version);
-       _cupsLangPrintf(stdout, NULL, "    modelname = %s\n", ppd->modelname);
-       _cupsLangPrintf(stdout, NULL, "    ttrasterizer = %s\n",
+       _cupsLangPrintf(stdout, "    modelname = %s\n", ppd->modelname);
+       _cupsLangPrintf(stdout, "    ttrasterizer = %s\n",
                        ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
-       _cupsLangPrintf(stdout, NULL, "    manufacturer = %s\n",
+       _cupsLangPrintf(stdout, "    manufacturer = %s\n",
                        ppd->manufacturer);
-       _cupsLangPrintf(stdout, NULL, "    product = %s\n", ppd->product);
-       _cupsLangPrintf(stdout, NULL, "    nickname = %s\n", ppd->nickname);
-       _cupsLangPrintf(stdout, NULL, "    shortnickname = %s\n",
+       _cupsLangPrintf(stdout, "    product = %s\n", ppd->product);
+       _cupsLangPrintf(stdout, "    nickname = %s\n", ppd->nickname);
+       _cupsLangPrintf(stdout, "    shortnickname = %s\n",
                        ppd->shortnickname);
-       _cupsLangPrintf(stdout, NULL, "    patches = %d bytes\n",
+       _cupsLangPrintf(stdout, "    patches = %d bytes\n",
                        ppd->patches == NULL ? 0 : (int)strlen(ppd->patches));
 
-       _cupsLangPrintf(stdout, NULL, "    num_groups = %d\n", ppd->num_groups);
+       _cupsLangPrintf(stdout, "    num_groups = %d\n", ppd->num_groups);
        for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
        {
-         _cupsLangPrintf(stdout, NULL, "        group[%d] = %s\n",
+         _cupsLangPrintf(stdout, "        group[%d] = %s\n",
                          j, group->text);
 
          for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
          {
-           _cupsLangPrintf(stdout, NULL,
+           _cupsLangPrintf(stdout,
                            "            options[%d] = %s (%s) %s %s %.0f "
                            "(%d choices)\n",
                            k, option->keyword, option->text, uis[option->ui],
@@ -1184,11 +1518,11 @@ main(int  argc,                 /* I - Number of command-line arguments */
                size = ppdPageSize(ppd, choice->choice);
 
                if (size == NULL)
-                 _cupsLangPrintf(stdout, NULL,
+                 _cupsLangPrintf(stdout,
                                   "                %s (%s) = ERROR",
                                  choice->choice, choice->text);
                else
-                 _cupsLangPrintf(stdout, NULL,
+                 _cupsLangPrintf(stdout,
                                   "                %s (%s) = %.2fx%.2fin "
                                  "(%.1f,%.1f,%.1f,%.1f)",
                                  choice->choice, choice->text,
@@ -1197,9 +1531,9 @@ main(int  argc,                   /* I - Number of command-line arguments */
                                  size->right / 72.0, size->top / 72.0);
 
                if (!strcmp(option->defchoice, choice->choice))
-                 _cupsLangPuts(stdout, NULL, " *\n");
+                 _cupsLangPuts(stdout, " *\n");
                else
-                 _cupsLangPuts(stdout, NULL, "\n");
+                 _cupsLangPuts(stdout, "\n");
               }
            }
            else
@@ -1208,22 +1542,30 @@ main(int  argc,                 /* I - Number of command-line arguments */
                   m > 0;
                   m --, choice ++)
              {
-               _cupsLangPrintf(stdout, NULL, "                %s (%s)",
+               _cupsLangPrintf(stdout, "                %s (%s)",
                                choice->choice, choice->text);
 
                if (!strcmp(option->defchoice, choice->choice))
-                 _cupsLangPuts(stdout, NULL, " *\n");
+                 _cupsLangPuts(stdout, " *\n");
                else
-                 _cupsLangPuts(stdout, NULL, "\n");
+                 _cupsLangPuts(stdout, "\n");
              }
             }
          }
        }
 
-       _cupsLangPrintf(stdout, NULL, "    num_profiles = %d\n",
+       _cupsLangPrintf(stdout, "    num_consts = %d\n",
+                       ppd->num_consts);
+       for (j = 0; j < ppd->num_consts; j ++)
+         _cupsLangPrintf(stdout,
+                         "        consts[%d] = *%s %s *%s %s\n",
+                         j, ppd->consts[j].option1, ppd->consts[j].choice1,
+                         ppd->consts[j].option2, ppd->consts[j].choice2);
+
+       _cupsLangPrintf(stdout, "    num_profiles = %d\n",
                        ppd->num_profiles);
        for (j = 0; j < ppd->num_profiles; j ++)
-         _cupsLangPrintf(stdout, NULL,
+         _cupsLangPrintf(stdout,
                          "        profiles[%d] = %s/%s %.3f %.3f "
                          "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
                          j, ppd->profiles[j].resolution,
@@ -1239,14 +1581,14 @@ main(int  argc,                 /* I - Number of command-line arguments */
                          ppd->profiles[j].matrix[2][1],
                          ppd->profiles[j].matrix[2][2]);
 
-       _cupsLangPrintf(stdout, NULL, "    num_fonts = %d\n", ppd->num_fonts);
+       _cupsLangPrintf(stdout, "    num_fonts = %d\n", ppd->num_fonts);
        for (j = 0; j < ppd->num_fonts; j ++)
-         _cupsLangPrintf(stdout, NULL, "        fonts[%d] = %s\n",
+         _cupsLangPrintf(stdout, "        fonts[%d] = %s\n",
                          j, ppd->fonts[j]);
 
-       _cupsLangPrintf(stdout, NULL, "    num_attrs = %d\n", ppd->num_attrs);
+       _cupsLangPrintf(stdout, "    num_attrs = %d\n", ppd->num_attrs);
        for (j = 0; j < ppd->num_attrs; j ++)
-         _cupsLangPrintf(stdout, NULL,
+         _cupsLangPrintf(stdout,
                          "        attrs[%d] = %s %s%s%s: \"%s\"\n", j,
                          ppd->attrs[j]->name, ppd->attrs[j]->spec,
                          ppd->attrs[j]->text[0] ? "/" : "",
@@ -1265,11 +1607,677 @@ main(int  argc,                        /* I - Number of command-line arguments */
 }
 
 
+/*
+ * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
+ */
+
+static void
+check_basics(const char *filename)     /* I - PPD file to check */
+{
+  cups_file_t  *fp;                    /* File pointer */
+  int          ch;                     /* Current character */
+  int          col,                    /* Current column */
+               whitespace;             /* Only seen whitespace? */
+  int          eol;                    /* Line endings */
+  int          linenum;                /* Line number */
+  int          mixed;                  /* Mixed line endings? */
+
+
+  if ((fp = cupsFileOpen(filename, "r")) == NULL)
+    return;
+
+  linenum    = 1;
+  col        = 0;
+  eol        = EOL_NONE;
+  mixed      = 0;
+  whitespace = 1;
+
+  while ((ch = cupsFileGetChar(fp)) != EOF)
+  {
+    if (ch == '\r' || ch == '\n')
+    {
+      if (ch == '\n')
+      {
+       if (eol == EOL_NONE)
+         eol = EOL_LF;
+       else if (eol != EOL_LF)
+         mixed = 1;
+      }
+      else if (ch == '\r')
+      {
+       if (cupsFilePeekChar(fp) == '\n')
+       {
+         cupsFileGetChar(fp);
+
+          if (eol == EOL_NONE)
+           eol = EOL_CRLF;
+         else
+           mixed = 1;
+       }
+       else if (eol == EOL_NONE)
+         eol = EOL_CR;
+        else
+         mixed = 1;
+      }
+      
+      if (col > 0 && whitespace)
+       _cupsLangPrintf(stdout,
+                       _("        WARN    Line %d only contains whitespace!\n"),
+                       linenum);
+
+      linenum ++;
+      col        = 0;
+      whitespace = 1;
+    }
+    else
+    {
+      if (ch != ' ' && ch != '\t')
+        whitespace = 0;
+
+      col ++;
+    }
+  }
+
+  if (mixed)
+    _cupsLangPuts(stdout,
+                 _("        WARN    File contains a mix of CR, LF, and "
+                   "CR LF line endings!\n"));
+
+  if (eol == EOL_CRLF)
+    _cupsLangPuts(stdout,
+                 _("        WARN    Non-Windows PPD files should use lines "
+                   "ending with only LF, not CR LF!\n"));
+
+  cupsFileClose(fp);
+}
+
+
+/*
+ * 'check_constraints()' - Check UIConstraints in the PPD file.
+ */
+
+static int                             /* O - Errors found */
+check_constraints(ppd_file_t *ppd,     /* I - PPD file */
+                  int        errors,   /* I - Errors found */
+                  int        verbose,  /* I - Verbosity level */
+                  int        warn)     /* I - Warnings only? */
+{
+  int          j;                      /* Looping var */
+  ppd_const_t  *c;                     /* Current constraint */
+  ppd_option_t *option;                /* Standard UI option */
+  ppd_option_t *option2;               /* Standard UI option */
+  const char   *prefix;                /* WARN/FAIL prefix */
+
+
+  prefix = warn ? "  WARN  " : "**FAIL**";
+
+  for (j = ppd->num_consts, c = ppd->consts; j > 0; j --, c ++)
+  {
+    option  = ppdFindOption(ppd, c->option1);
+    option2 = ppdFindOption(ppd, c->option2);
+
+    if (!option || !option2)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (!option)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Missing option %s in "
+                         "UIConstraint \"*%s %s *%s %s\"!\n"),
+                       prefix, c->option1,
+                       c->option1, c->choice1, c->option2, c->choice2);
+      
+      if (!option2)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Missing option %s in "
+                         "UIConstraint \"*%s %s *%s %s\"!\n"),
+                       prefix, c->option2,
+                       c->option1, c->choice1, c->option2, c->choice2);
+
+      if (!warn)
+        errors ++;
+
+      continue;
+    }
+
+    if (c->choice1[0] && !ppdFindChoice(option, c->choice1))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      _cupsLangPrintf(stdout,
+                     _("      %s  Missing choice *%s %s in "
+                       "UIConstraint \"*%s %s *%s %s\"!\n"),
+                     prefix, c->option1, c->choice1,
+                     c->option1, c->choice1, c->option2, c->choice2);
+
+      if (!warn)
+        errors ++;
+    }
+
+    if (c->choice2[0] && !ppdFindChoice(option2, c->choice2))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      _cupsLangPrintf(stdout,
+                     _("      %s  Missing choice *%s %s in "
+                       "UIConstraint \"*%s %s *%s %s\"!\n"),
+                     prefix, c->option2, c->choice2,
+                     c->option1, c->choice1, c->option2, c->choice2);
+
+      if (!warn)
+        errors ++;
+    }
+  }
+
+  return (errors);
+}
+
+
+/*
+ * 'check_defaults()' - Check default option keywords in the PPD file.
+ */
+
+static int                             /* O - Errors found */
+check_defaults(ppd_file_t *ppd,                /* I - PPD file */
+              int        errors,       /* I - Errors found */
+              int        verbose,      /* I - Verbosity level */
+              int        warn)         /* I - Warnings only? */
+{
+  int          j, k;                   /* Looping vars */
+  ppd_attr_t   *attr;                  /* PPD attribute */
+  ppd_option_t *option;                /* Standard UI option */
+  const char   *prefix;                /* WARN/FAIL prefix */
+
+
+  prefix = warn ? "  WARN  " : "**FAIL**";
+
+  for (j = 0; j < ppd->num_attrs; j ++)
+  {
+    attr = ppd->attrs[j];
+
+    if (!strcmp(attr->name, "DefaultColorSpace") ||
+       !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, "DefaultTransfer"))
+      continue;
+
+    if (!strncmp(attr->name, "Default", 7))
+    {
+      if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
+         strcmp(attr->value, "Unknown"))
+      {
+       /*
+       * Check that the default option value matches a choice...
+       */
+
+       for (k = 0; k < option->num_choices; k ++)
+         if (!strcmp(option->choices[k].choice, attr->value))
+           break;
+
+       if (k >= option->num_choices)
+       {
+         if (!warn && !errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("      %s  %s %s does not exist!\n"),
+                           prefix, attr->name, attr->value);
+
+          if (!warn)
+           errors ++;
+       }
+      }
+    }
+  }
+
+  return (errors);
+}
+
+
+/*
+ * 'check_filters()' - Check filters in the PPD file.
+ */
+
+static int                             /* O - Errors found */
+check_filters(ppd_file_t *ppd,         /* I - PPD file */
+              const char *root,                /* I - Root directory */
+             int        errors,        /* I - Errors found */
+             int        verbose,       /* I - Verbosity level */
+             int        warn)          /* I - Warnings only? */
+{
+  ppd_attr_t   *attr;                  /* PPD attribute */
+  const char   *ptr;                   /* Pointer into string */
+  struct stat  statbuf;                /* File information */
+  char         super[16],              /* Super-type for filter */
+               type[256],              /* Type for filter */
+               program[256],           /* Program/filter name */
+               pathprog[1024];         /* Complete path to program/filter */
+  int          cost;                   /* Cost of filter */
+  const char   *prefix;                /* WARN/FAIL prefix */
+
+
+  prefix = warn ? "  WARN  " : "**FAIL**";
+
+  for (attr = ppdFindAttr(ppd, "cupsFilter", NULL);
+       attr;
+       attr = ppdFindNextAttr(ppd, "cupsFilter", NULL))
+  {
+    if (!attr->value ||
+       sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
+              program) != 4)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad cupsFilter value \"%s\"!\n"),
+                       prefix, attr->value ? attr->value : "");
+
+      if (!warn)
+        errors ++;
+    }
+    else
+    {
+      if (program[0] == '/')
+       snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
+      else
+      {
+       if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
+         ptr = CUPS_SERVERBIN;
+
+       if (*ptr == '/' || !*root)
+         snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
+                  program);
+       else
+         snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
+                  program);
+      }
+
+      if (stat(pathprog, &statbuf))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout, _("      %s  Missing cupsFilter "
+                                   "file \"%s\"\n"), prefix, program);
+
+       if (!warn)
+         errors ++;
+      }
+    }
+  }
+
+  for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
+       attr;
+       attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
+  {
+    if (!attr->value ||
+       sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost,
+              program) != 4)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad cupsPreFilter value \"%s\"!\n"),
+                       prefix, attr->value ? attr->value : "");
+
+      if (!warn)
+        errors ++;
+    }
+    else
+    {
+      if (program[0] == '/')
+       snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
+      else
+      {
+       if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
+         ptr = CUPS_SERVERBIN;
+
+       if (*ptr == '/' || !*root)
+         snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr,
+                  program);
+       else
+         snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr,
+                  program);
+      }
+
+      if (stat(pathprog, &statbuf))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout, _("      %s  Missing cupsPreFilter "
+                                   "file \"%s\"\n"), prefix, program);
+
+        if (!warn)
+         errors ++;
+      }
+    }
+  }
+
+  return (errors);
+}
+
+
+/*
+ * 'check_translations()' - Check translations in the PPD file.
+ */
+
+static int                             /* O - Errors found */
+check_translations(ppd_file_t *ppd,    /* I - PPD file */
+                   int        errors,  /* I - Errors found */
+                   int        verbose, /* I - Verbosity level */
+                   int        warn)    /* I - Warnings only? */
+{
+  int          j;                      /* Looping var */
+  ppd_attr_t   *attr;                  /* PPD attribute */
+  char         *languages,             /* Copy of attribute value */
+               *langstart,             /* Start of current language */
+               *langptr,               /* Pointer into languages */
+               keyword[PPD_MAX_NAME],  /* Localization keyword (full) */
+               llkeyword[PPD_MAX_NAME],/* Localization keyword (base) */
+               ckeyword[PPD_MAX_NAME], /* Custom option keyword (full) */
+               cllkeyword[PPD_MAX_NAME];
+                                       /* Custom option keyword (base) */
+  ppd_option_t *option;                /* Standard UI option */
+  ppd_coption_t        *coption;               /* Custom option */
+  ppd_cparam_t *cparam;                /* Custom parameter */
+  cups_array_t *langlist;              /* List of languages so far */
+  char         ll[3];                  /* Base language */
+  const char   *prefix;                /* WARN/FAIL prefix */
+
+
+  prefix = warn ? "  WARN  " : "**FAIL**";
+
+  if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) != NULL &&
+      attr->value)
+  {
+   /*
+    * This file contains localizations, check them...
+    */
+
+    languages = strdup(attr->value);
+    langlist  = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+    for (langptr = languages; *langptr;)
+    {
+     /*
+      * Skip leading whitespace...
+      */
+
+      while (isspace(*langptr & 255))
+       langptr ++;
+
+      if (!*langptr)
+       break;
+
+     /*
+      * Find the end of this language name...
+      */
+
+      for (langstart = langptr;
+          *langptr && !isspace(*langptr & 255);
+          langptr ++);
+
+      if (*langptr)
+       *langptr++ = '\0';
+
+      j = strlen(langstart);
+      if (j != 2 && j != 5)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout,
+                         _("      %s  Bad language \"%s\"!\n"),
+                         prefix, langstart);
+
+       if (!warn)
+         errors ++;
+
+       continue;
+      }
+
+      if (!strcmp(langstart, "en"))
+        continue;
+
+      cupsArrayAdd(langlist, langstart);
+
+      strlcpy(ll, langstart, sizeof(ll));
+
+     /*
+      * Loop through all options and choices...
+      */
+
+      for (option = ppdFirstOption(ppd);
+          option;
+          option = ppdNextOption(ppd))
+      {
+        if (!strcmp(option->keyword, "PageRegion"))
+         continue;
+
+       snprintf(keyword, sizeof(keyword), "%s.Translation", langstart);
+       snprintf(llkeyword, sizeof(llkeyword), "%s.Translation", ll);
+
+       if ((attr = ppdFindAttr(ppd, keyword, option->keyword)) == NULL &&
+           (attr = ppdFindAttr(ppd, llkeyword, option->keyword)) == NULL)
+       {
+         if (!warn && !errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("      %s  Missing \"%s\" translation "
+                             "string for option %s!\n"),
+                           prefix, langstart, option->keyword);
+
+          if (!warn)
+           errors ++;
+       }
+       else if (!valid_utf8(attr->text))
+       {
+         if (!warn && !errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("      %s  Bad UTF-8 \"%s\" translation "
+                             "string for option %s!\n"),
+                           prefix, langstart, option->keyword);
+
+         if (!warn)
+           errors ++;
+       }
+
+       snprintf(keyword, sizeof(keyword), "%s.%s", langstart,
+                option->keyword);
+       snprintf(llkeyword, sizeof(llkeyword), "%s.%s", ll,
+                option->keyword);
+
+       for (j = 0; j < option->num_choices; j ++)
+       {
+         if (!strcasecmp(option->choices[j].choice, "Custom") &&
+             (coption = ppdFindCustomOption(ppd,
+                                            option->keyword)) != NULL)
+         {
+           snprintf(ckeyword, sizeof(ckeyword), "%s.Custom%s",
+                    langstart, option->keyword);
+
+           if ((attr = ppdFindAttr(ppd, ckeyword, "True")) != NULL &&
+               !valid_utf8(attr->text))
+           {
+             if (!warn && !errors && !verbose)
+               _cupsLangPuts(stdout, _(" FAIL\n"));
+
+             if (verbose >= 0)
+               _cupsLangPrintf(stdout,
+                               _("      %s  Bad UTF-8 \"%s\" "
+                                 "translation string for option %s, "
+                                 "choice %s!\n"),
+                               prefix, langstart,
+                               ckeyword + 1 + strlen(langstart),
+                               "True");
+
+              if (!warn)
+               errors ++;
+           }
+
+           if (strcasecmp(option->keyword, "PageSize"))
+           {
+             for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+                  cparam;
+                  cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+             {
+               snprintf(ckeyword, sizeof(ckeyword), "%s.ParamCustom%s",
+                        langstart, option->keyword);
+               snprintf(cllkeyword, sizeof(cllkeyword), "%s.ParamCustom%s",
+                        ll, option->keyword);
+
+               if ((attr = ppdFindAttr(ppd, ckeyword,
+                                       cparam->name)) == NULL &&
+                   (attr = ppdFindAttr(ppd, cllkeyword,
+                                       cparam->name)) == NULL)
+               {
+                 if (!warn && !errors && !verbose)
+                   _cupsLangPuts(stdout, _(" FAIL\n"));
+
+                 if (verbose >= 0)
+                   _cupsLangPrintf(stdout,
+                                   _("      %s  Missing \"%s\" "
+                                     "translation string for option %s, "
+                                     "choice %s!\n"),
+                                   prefix, langstart,
+                                   ckeyword + 1 + strlen(langstart),
+                                   cparam->name);
+
+                  if (!warn)
+                   errors ++;
+               }
+               else if (!valid_utf8(attr->text))
+               {
+                 if (!warn && !errors && !verbose)
+                   _cupsLangPuts(stdout, _(" FAIL\n"));
+
+                 if (verbose >= 0)
+                   _cupsLangPrintf(stdout,
+                                   _("      %s  Bad UTF-8 \"%s\" "
+                                     "translation string for option %s, "
+                                     "choice %s!\n"),
+                                   prefix, langstart,
+                                   ckeyword + 1 + strlen(langstart),
+                                   cparam->name);
+
+                 if (!warn)
+                   errors ++;
+               }
+             }
+           }
+         }
+         else if ((attr = ppdFindAttr(ppd, keyword,
+                                      option->choices[j].choice)) == NULL &&
+                  (attr = ppdFindAttr(ppd, llkeyword,
+                                      option->choices[j].choice)) == NULL)
+         {
+           if (!warn && !errors && !verbose)
+             _cupsLangPuts(stdout, _(" FAIL\n"));
+
+           if (verbose >= 0)
+             _cupsLangPrintf(stdout,
+                             _("      %s  Missing \"%s\" "
+                               "translation string for option %s, "
+                               "choice %s!\n"),
+                             prefix, langstart, option->keyword,
+                             option->choices[j].choice);
+
+           if (!warn)
+             errors ++;
+         }
+         else if (!valid_utf8(attr->text))
+         {
+           if (!warn && !errors && !verbose)
+             _cupsLangPuts(stdout, _(" FAIL\n"));
+
+           if (verbose >= 0)
+             _cupsLangPrintf(stdout,
+                             _("      %s  Bad UTF-8 \"%s\" "
+                               "translation string for option %s, "
+                               "choice %s!\n"),
+                             prefix, langstart, option->keyword,
+                             option->choices[j].choice);
+
+           if (!warn)
+             errors ++;
+         }
+       }
+      }
+    }
+
+   /*
+    * Verify that we have the base language for each localized one...
+    */
+
+    for (langptr = (char *)cupsArrayFirst(langlist);
+        langptr;
+        langptr = (char *)cupsArrayNext(langlist))
+      if (langptr[2])
+      {
+       /*
+       * Lookup the base language...
+       */
+
+       cupsArraySave(langlist);
+
+       strlcpy(ll, langptr, sizeof(ll));
+
+       if (!cupsArrayFind(langlist, ll) && strcmp(ll, "zh"))
+       {
+         if (!warn && !errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("      %s  No base translation \"%s\" "
+                             "is included in file!\n"), prefix, ll);
+
+         if (!warn)
+           errors ++;
+       }
+
+       cupsArrayRestore(langlist);
+      }
+
+   /*
+    * Free memory used for the languages...
+    */
+
+    cupsArrayDelete(langlist);
+    free(languages);
+  }
+
+  return (errors);
+}
+
+
 /*
  * 'show_conflicts()' - Show option conflicts in a PPD file.
  */
 
-void
+static void
 show_conflicts(ppd_file_t *ppd)                /* I - PPD to check */
 {
   int          i, j;                   /* Looping variables */
@@ -1356,7 +2364,7 @@ show_conflicts(ppd_file_t *ppd)           /* I - PPD to check */
     */
 
     if (c1 != NULL && c1->marked && c2 != NULL && c2->marked)
-      _cupsLangPrintf(stdout, NULL,
+      _cupsLangPrintf(stdout,
                       _("        WARN    \"%s %s\" conflicts with \"%s %s\"\n"
                         "                (constraint=\"%s %s %s %s\")\n"),
                      o1->keyword, c1->choice, o2->keyword, c2->choice,
@@ -1365,22 +2373,159 @@ show_conflicts(ppd_file_t *ppd)                /* I - PPD to check */
 }
 
 
+/*
+ * 'test_raster()' - Test PostScript commands for raster printers.
+ */
+
+static int                             /* O - 1 on success, 0 on failure */
+test_raster(ppd_file_t *ppd,           /* I - PPD file */
+            int        verbose)                /* I - Verbosity */
+{
+  cups_page_header2_t  header;         /* Page header */
+
+
+  ppdMarkDefaults(ppd);
+  if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
+  {
+    if (!verbose)
+      _cupsLangPuts(stdout, _(" FAIL\n"));
+
+    if (verbose >= 0)
+      _cupsLangPrintf(stdout,
+                     _("      **FAIL**  Default option code cannot be "
+                       "interpreted: %s\n"), cupsRasterErrorString());
+
+    return (0);
+  }
+
+ /*
+  * Try a test of custom page size code, if available...
+  */
+
+  if (!ppdPageSize(ppd, "Custom.612x792"))
+    return (1);
+
+  ppdMarkOption(ppd, "PageSize", "Custom.612x792");
+
+  if (cupsRasterInterpretPPD(&header, ppd, 0, NULL, 0))
+  {
+    if (!verbose)
+      _cupsLangPuts(stdout, _(" FAIL\n"));
+
+    if (verbose >= 0)
+      _cupsLangPrintf(stdout,
+                     _("      **FAIL**  Default option code cannot be "
+                       "interpreted: %s\n"), cupsRasterErrorString());
+
+    return (0);
+  }
+
+  return (1);
+}
+
+
 /*
  * 'usage()' - Show program usage...
  */
 
-void
+static void
 usage(void)
 {
-  _cupsLangPuts(stdout, NULL,
-                _("Usage: cupstestppd [-q] [-r] [-v[v]] filename1.ppd[.gz] "
+  _cupsLangPuts(stdout,
+                _("Usage: cupstestppd [options] filename1.ppd[.gz] "
                  "[... filenameN.ppd[.gz]]\n"
-                 "       program | cupstestppd [-q] [-r] [-v[v]] -\n"));
+                 "       program | cupstestppd [options] -\n"
+                 "\n"
+                 "Options:\n"
+                 "\n"
+                 "    -R root-directory    Set alternate root\n"
+                 "    -W {all,none,constraints,defaults,filters,translations}\n"
+                 "                         Issue warnings instead of errors\n"
+                 "    -q                   Run silently\n"
+                 "    -r                   Use 'relaxed' open mode\n"
+                 "    -v                   Be slightly verbose\n"
+                 "    -vv                  Be very verbose\n"));
 
   exit(ERROR_USAGE);
 }
 
 
 /*
- * End of "$Id: cupstestppd.c 4906 2006-01-10 20:53:28Z mike $".
+ * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
+ */
+
+static int                             /* O - 1 if valid, 0 if not */
+valid_utf8(const char *s)              /* I - String to check */
+{
+  while (*s)
+  {
+    if (*s & 0x80)
+    {
+     /*
+      * Check for valid UTF-8 sequence...
+      */
+
+      if ((*s & 0xc0) == 0x80)
+        return (0);                    /* Illegal suffix byte */
+      else if ((*s & 0xe0) == 0xc0)
+      {
+       /*
+        * 2-byte sequence...
+        */
+
+        s ++;
+
+        if ((*s & 0xc0) != 0x80)
+          return (0);                  /* Missing suffix byte */
+      }
+      else if ((*s & 0xf0) == 0xe0)
+      {
+       /*
+        * 3-byte sequence...
+        */
+
+        s ++;
+
+        if ((*s & 0xc0) != 0x80)
+          return (0);                  /* Missing suffix byte */
+
+        s ++;
+
+        if ((*s & 0xc0) != 0x80)
+          return (0);                  /* Missing suffix byte */
+      }
+      else if ((*s & 0xf8) == 0xf0)
+      {
+       /*
+        * 4-byte sequence...
+        */
+
+        s ++;
+
+        if ((*s & 0xc0) != 0x80)
+          return (0);                  /* Missing suffix byte */
+
+        s ++;
+
+        if ((*s & 0xc0) != 0x80)
+          return (0);                  /* Missing suffix byte */
+
+        s ++;
+
+        if ((*s & 0xc0) != 0x80)
+          return (0);                  /* Missing suffix byte */
+      }
+      else
+        return (0);                    /* Bad sequence */
+    }
+
+    s ++;
+  }
+
+  return (1);
+}
+
+
+/*
+ * End of "$Id: cupstestppd.c 6927 2007-09-07 16:51:00Z mike $".
  */