]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - systemv/cupstestppd.c
Merge changes from CUPS 1.5svn-r8849.
[thirdparty/cups.git] / systemv / cupstestppd.c
index b2317564956ccae7c3a377628b322152731fd6d1..0a7ae4b3b6ca8eec34113d30f3434601d5db1636 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: cupstestppd.c 7637 2008-06-11 17:25:36Z mike $"
+ * "$Id: cupstestppd.c 7807 2008-07-28 21:54:24Z mike $"
  *
  *   PPD test program for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 2007-2009 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  *   check_basics()       - Check for CR LF, mixed line endings, and blank
  *                          lines.
  *   check_constraints()  - Check UIConstraints in the PPD file.
+ *   check_case()         - Check that there are no duplicate groups, options,
+ *                          or choices that differ only by case.
  *   check_defaults()     - Check default option keywords in the PPD file.
+ *   check_duplex()       - Check duplex keywords in the PPD file.
  *   check_filters()      - Check filters in the PPD file.
  *   check_profiles()     - Check ICC color profiles in the PPD file.
+ *   check_sizes()        - Check media sizes 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_path()         - Check whether a path has the correct capitalization.
  *   valid_utf8()         - Check whether a string contains valid UTF-8 text.
  */
 
 
 #include <cups/string.h>
 #include <cups/cups.h>
+#include <cups/dir.h>
 #include <cups/ppd-private.h>
 #include <cups/i18n.h>
 #include <cups/raster.h>
 #include <errno.h>
 #include <stdlib.h>
-#include <sys/stat.h>
+#include <math.h>
+#ifdef WIN32
+#  define X_OK 0
+#endif /* WIN32 */
 
 
 /*
@@ -58,7 +67,9 @@ enum
   WARN_FILTERS = 4,
   WARN_PROFILES = 8,
   WARN_TRANSLATIONS = 16,
-  WARN_ALL = 31
+  WARN_DUPLEX = 32,
+  WARN_SIZES = 64,
+  WARN_ALL = 127
 };
 
 
@@ -89,6 +100,179 @@ enum
 };
 
 
+/*
+ * File permissions...
+ */
+
+#define MODE_WRITE     0022            /* Group/other write */
+#define MODE_MASK      0555            /* Owner/group/other read+exec/search */
+#define MODE_DATAFILE  0444            /* Owner/group/other read */
+#define MODE_DIRECTORY 0555            /* Owner/group/other read+search */
+#define MODE_PROGRAM   0555            /* Owner/group/other read+exec */
+
+
+/*
+ * Standard Adobe media keywords (must remain sorted)...
+ */
+
+static const char adobe_size_names[][PPD_MAX_NAME] =
+{
+  "10x11",
+  "10x13",
+  "10x14",
+  "12x11",
+  "15x11",
+  "7x9",
+  "8x10",
+  "9x11",
+  "9x12",
+  "A0",
+  "A1",
+  "A10",
+  "A2",
+  "A3",
+  "A3Extra",
+  "A3Rotated",
+  "A4",
+  "A4Extra",
+  "A4Plus",
+  "A4Rotated",
+  "A4Small",
+  "A5",
+  "A5Extra",
+  "A5Rotated",
+  "A6",
+  "A6Rotated",
+  "A7",
+  "A8",
+  "A9",
+  "ARCHA",
+  "ARCHB",
+  "ARCHC",
+  "ARCHD",
+  "ARCHE",
+  "AnsiA",
+  "AnsiB",
+  "AnsiC",
+  "AnsiD",
+  "AnsiE",
+  "B0",
+  "B1",
+  "B1",
+  "B10",
+  "B2",
+  "B3",
+  "B4",
+  "B4Rotated",
+  "B5",
+  "B5Rotated",
+  "B6",
+  "B6Rotated",
+  "B7",
+  "B8",
+  "B9",
+  "C4",
+  "C5",
+  "C6",
+  "DL",
+  "DoublePostcard",
+  "DoublePostcardRotated",
+  "Env10",
+  "Env11",
+  "Env12",
+  "Env14",
+  "Env9",
+  "EnvC0",
+  "EnvC1",
+  "EnvC2",
+  "EnvC3",
+  "EnvC4",
+  "EnvC5",
+  "EnvC6",
+  "EnvC65",
+  "EnvC7",
+  "EnvChou3",
+  "EnvChou3Rotated",
+  "EnvChou4",
+  "EnvChou4Rotated",
+  "EnvDL",
+  "EnvISOB4",
+  "EnvISOB5",
+  "EnvISOB6",
+  "EnvInvite",
+  "EnvItalian",
+  "EnvKaku2",
+  "EnvKaku2Rotated",
+  "EnvKaku3",
+  "EnvKaku3Rotated",
+  "EnvMonarch",
+  "EnvPRC1",
+  "EnvPRC10",
+  "EnvPRC10Rotated",
+  "EnvPRC1Rotated",
+  "EnvPRC2",
+  "EnvPRC2Rotated",
+  "EnvPRC3",
+  "EnvPRC3Rotated",
+  "EnvPRC4",
+  "EnvPRC4Rotated",
+  "EnvPRC5",
+  "EnvPRC5Rotated",
+  "EnvPRC6",
+  "EnvPRC6Rotated",
+  "EnvPRC7",
+  "EnvPRC7Rotated",
+  "EnvPRC8",
+  "EnvPRC8Rotated",
+  "EnvPRC9",
+  "EnvPRC9Rotated",
+  "EnvPersonal",
+  "EnvYou4",
+  "EnvYou4Rotated",
+  "Executive",
+  "FanFoldGerman",
+  "FanFoldGermanLegal",
+  "FanFoldUS",
+  "Folio",
+  "ISOB0",
+  "ISOB1",
+  "ISOB10",
+  "ISOB2",
+  "ISOB3",
+  "ISOB4",
+  "ISOB5",
+  "ISOB5Extra",
+  "ISOB6",
+  "ISOB7",
+  "ISOB8",
+  "ISOB9",
+  "Ledger",
+  "Legal",
+  "LegalExtra",
+  "Letter",
+  "LetterExtra",
+  "LetterPlus",
+  "LetterRotated",
+  "LetterSmall",
+  "Monarch",
+  "Note",
+  "PRC16K",
+  "PRC16KRotated",
+  "PRC32K",
+  "PRC32KBig",
+  "PRC32KBigRotated",
+  "PRC32KRotated",
+  "Postcard",
+  "PostcardRotated",
+  "Quarto",
+  "Statement",
+  "SuperA",
+  "SuperB",
+  "Tabloid",
+  "TabloidExtra"
+};
+
+
 /*
  * Local functions...
  */
@@ -96,17 +280,23 @@ enum
 static void    check_basics(const char *filename);
 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_translations(ppd_file_t *ppd, int errors, int verbose,\
+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);
 static int     test_raster(ppd_file_t *ppd, int verbose);
 static void    usage(void);
+static int     valid_path(const char *keyword, const char *path, int errors,
+                          int verbose, int warn);
 static int     valid_utf8(const char *s);
 
 
@@ -125,6 +315,7 @@ main(int  argc,                             /* I - Number of command-line args */
   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 */
@@ -162,6 +353,7 @@ main(int  argc,                             /* I - Number of command-line args */
   status  = ERROR_NONE;
   root    = "";
   warn    = WARN_NONE;
+  ignore  = WARN_NONE;
 
   for (i = 1; i < argc; i ++)
     if (argv[i][0] == '-' && argv[i][1])
@@ -169,6 +361,24 @@ main(int  argc,                            /* I - Number of command-line args */
       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], "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 ++;
 
@@ -178,7 +388,7 @@ main(int  argc,                             /* I - Number of command-line args */
               root = argv[i];
              break;
 
-         case 'W' :                    /* Turn errors into warnings  */
+         case 'W' :                    /* Turn errors into warnings */
              i ++;
 
              if (i >= argc)
@@ -190,10 +400,14 @@ main(int  argc,                           /* I - Number of command-line args */
                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"))
@@ -412,7 +626,7 @@ main(int  argc,                             /* I - Number of command-line args */
            _cupsLangPuts(stdout, _(" FAIL\n"));
 
          _cupsLangPrintf(stdout,
-                         _("      **FAIL**  BAD DefaultImageableArea %s!\n"
+                         _("      **FAIL**  BAD DefaultImageableArea %s\n"
                            "                REF: Page 102, section 5.15.\n"),
                          attr->value);
        }
@@ -448,7 +662,7 @@ main(int  argc,                             /* I - Number of command-line args */
            _cupsLangPuts(stdout, _(" FAIL\n"));
 
          _cupsLangPrintf(stdout,
-                         _("      **FAIL**  BAD DefaultPaperDimension %s!\n"
+                         _("      **FAIL**  BAD DefaultPaperDimension %s\n"
                            "                REF: Page 103, section 5.15.\n"),
                          attr->value);
        }
@@ -505,9 +719,28 @@ main(int  argc,                            /* I - Number of command-line args */
          }
        }
 
-      if (ppdFindAttr(ppd, "FileVersion", NULL) != NULL)
+      if ((attr = ppdFindAttr(ppd, "FileVersion", NULL)) != NULL)
       {
-       if (verbose > 0)
+        for (ptr = attr->value; *ptr; ptr ++)
+         if (!isdigit(*ptr & 255) && *ptr != '.')
+           break;
+
+       if (*ptr)
+       {
+         if (verbose >= 0)
+         {
+           if (!errors && !verbose)
+             _cupsLangPuts(stdout, _(" FAIL\n"));
+
+           _cupsLangPrintf(stdout,
+                           _("      **FAIL**  Bad FileVersion \"%s\"\n"
+                             "                REF: Page 56, section 5.3.\n"),
+                           attr->value);
+         }
+
+         errors ++;
+       }
+       else if (verbose > 0)
          _cupsLangPuts(stdout, _("        PASS    FileVersion\n"));
       }
       else
@@ -525,9 +758,33 @@ main(int  argc,                            /* I - Number of command-line args */
        errors ++;
       }
 
-      if (ppdFindAttr(ppd, "FormatVersion", NULL) != NULL)
+      if ((attr = ppdFindAttr(ppd, "FormatVersion", NULL)) != NULL)
       {
-       if (verbose > 0)
+        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\n"));
+
+           _cupsLangPrintf(stdout,
+                           _("      **FAIL**  Bad FormatVersion \"%s\"\n"
+                             "                REF: Page 56, section 5.3.\n"),
+                           attr->value);
+         }
+
+         errors ++;
+       }
+       else if (verbose > 0)
          _cupsLangPuts(stdout, _("        PASS    FormatVersion\n"));
       }
       else
@@ -998,7 +1255,7 @@ main(int  argc,                            /* I - Number of command-line args */
                _cupsLangPuts(stdout, _(" FAIL\n"));
 
              _cupsLangPrintf(stdout,
-                             _("      **FAIL**  Bad %s choice %s!\n"
+                             _("      **FAIL**  Bad %s choice %s\n"
                                "                REF: Page 84, section 5.9\n"),
                              option->keyword, choice->choice);
             }
@@ -1008,55 +1265,6 @@ main(int  argc,                          /* I - Number of command-line args */
        }
       }
 
-     /*
-      * Check for a duplex option, and for standard values...
-      */
-
-      if ((option = ppdFindOption(ppd, "Duplex")) == NULL)
-       if ((option = ppdFindOption(ppd, "JCLDuplex")) == NULL)
-         if ((option = ppdFindOption(ppd, "EFDuplex")) == NULL)
-            option = ppdFindOption(ppd, "KD03Duplex");
-
-      if (option != NULL)
-      {
-        if (ppdFindChoice(option, "None") == NULL)
-       {
-         if (verbose >= 0)
-         {
-           if (!errors && !verbose)
-             _cupsLangPuts(stdout, _(" FAIL\n"));
-
-           _cupsLangPrintf(stdout,
-                           _("      **FAIL**  REQUIRED %s does not define "
-                             "choice None!\n"
-                             "                REF: Page 122, section 5.17\n"),
-                           option->keyword);
-          }
-
-         errors ++;
-       }
-
-        for (j = option->num_choices, choice = option->choices; j > 0; j --, choice ++)
-          if (strcmp(choice->choice, "None") &&
-             strcmp(choice->choice, "DuplexNoTumble") &&
-             strcmp(choice->choice, "DuplexTumble") &&
-             strcmp(choice->choice, "SimplexTumble"))
-         {
-           if (verbose >= 0)
-           {
-             if (!errors && !verbose)
-               _cupsLangPuts(stdout, _(" FAIL\n"));
-
-             _cupsLangPrintf(stdout,
-                             _("      **FAIL**  Bad %s choice %s!\n"
-                               "                REF: Page 122, section 5.17\n"),
-                             option->keyword, choice->choice);
-            }
-
-           errors ++;
-         }
-      }
-
       if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) &&
           strcmp(attr->name, "1284DeviceID"))
       {
@@ -1066,7 +1274,7 @@ main(int  argc,                           /* I - Number of command-line args */
            _cupsLangPuts(stdout, _(" FAIL\n"));
 
          _cupsLangPrintf(stdout,
-                         _("      **FAIL**  %s must be 1284DeviceID!\n"
+                         _("      **FAIL**  %s must be 1284DeviceID\n"
                            "                REF: Page 72, section 5.5\n"),
                          attr->name);
         }
@@ -1074,18 +1282,26 @@ main(int  argc,                         /* I - Number of command-line args */
        errors ++;
       }
 
+      errors = check_case(ppd, errors, verbose);
+
       if (!(warn & WARN_CONSTRAINTS))
         errors = check_constraints(ppd, errors, verbose, 0);
 
-      if (!(warn & WARN_FILTERS))
+      if (!(warn & WARN_FILTERS) && !(ignore & WARN_FILTERS))
         errors = check_filters(ppd, root, errors, verbose, 0);
 
-      if (!(warn & WARN_PROFILES))
+      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)
       {
@@ -1104,7 +1320,7 @@ main(int  argc,                           /* I - Number of command-line args */
             if (verbose >= 0)
              _cupsLangPrintf(stdout,
                              _("      **FAIL**  Bad LanguageEncoding %s - "
-                               "must be ISOLatin1!\n"),
+                               "must be ISOLatin1\n"),
                              attr->value ? attr->value : "(null)");
 
             errors ++;
@@ -1118,7 +1334,7 @@ main(int  argc,                           /* I - Number of command-line args */
             if (verbose >= 0)
              _cupsLangPrintf(stdout,
                              _("      **FAIL**  Bad LanguageVersion %s - "
-                               "must be English!\n"),
+                               "must be English\n"),
                              ppd->lang_version ? ppd->lang_version : "(null)");
 
             errors ++;
@@ -1151,7 +1367,7 @@ main(int  argc,                           /* I - Number of command-line args */
                _cupsLangPrintf(stdout,
                                _("      **FAIL**  Default translation "
                                  "string for option %s contains 8-bit "
-                                 "characters!\n"),
+                                 "characters\n"),
                                option->keyword);
 
              errors ++;
@@ -1178,7 +1394,7 @@ main(int  argc,                           /* I - Number of command-line args */
                  _cupsLangPrintf(stdout,
                                  _("      **FAIL**  Default translation "
                                    "string for option %s choice %s contains "
-                                   "8-bit characters!\n"),
+                                   "8-bit characters\n"),
                                  option->keyword,
                                  option->choices[j].choice);
 
@@ -1202,21 +1418,44 @@ main(int  argc,                         /* I - Number of command-line args */
       {
         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_DEFAULTS)
-         errors = check_defaults(ppd, errors, verbose, 1);
+       if ((warn & WARN_FILTERS) && !(ignore & WARN_FILTERS))
+         errors = check_filters(ppd, root, errors, verbose, 1);
 
-       if (warn & WARN_PROFILES)
+       if ((warn & WARN_PROFILES) && !(ignore & WARN_PROFILES))
          errors = check_profiles(ppd, root, errors, verbose, 1);
 
-       if (warn & WARN_FILTERS)
-         errors = check_filters(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\n"),
+                         option->keyword);
+
        /*
        * Look for default keywords with no corresponding option...
        */
@@ -1242,31 +1481,15 @@ main(int  argc,                         /* I - Number of command-line args */
              !ppdFindOption(ppd, attr->name + 7))
             _cupsLangPrintf(stdout,
                            _("        WARN    %s has no corresponding "
-                             "options!\n"),
+                             "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"),
-                         option->keyword);
-       }
-
         ppdMarkDefaults(ppd);
        if (ppdConflicts(ppd))
        {
          _cupsLangPuts(stdout,
-                       _("        WARN    Default choices conflicting!\n"));
+                       _("        WARN    Default choices conflicting\n"));
 
           show_conflicts(ppd);
         }
@@ -1274,7 +1497,7 @@ main(int  argc,                           /* I - Number of command-line args */
         if (ppdversion < 43)
        {
           _cupsLangPrintf(stdout,
-                         _("        WARN    Obsolete PPD version %.1f!\n"
+                         _("        WARN    Obsolete PPD version %.1f\n"
                            "                REF: Page 42, section 5.2.\n"),
                          0.1f * ppdversion);
        }
@@ -1352,7 +1575,7 @@ main(int  argc,                           /* I - Number of command-line args */
        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);
+           len = (int)strlen(option->keyword);
 
            for (m = 0, group2 = ppd->groups;
                 m < ppd->num_groups;
@@ -1361,7 +1584,7 @@ main(int  argc,                           /* I - Number of command-line args */
                   n < group2->num_options;
                   n ++, option2 ++)
                if (option != option2 &&
-                   len < strlen(option2->keyword) &&
+                   len < (int)strlen(option2->keyword) &&
                    !strncmp(option->keyword, option2->keyword, len))
                {
                  _cupsLangPrintf(stdout,
@@ -1374,36 +1597,6 @@ main(int  argc,                          /* I - Number of command-line args */
          }
       }
 
-#ifdef __APPLE__
-     /*
-      * APDialogExtension
-      */
-
-      for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL); 
-          attr != NULL; 
-          attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
-      {
-       if ((!attr->value || access(attr->value, 0)) && 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 || access(attr->value, 0)) && verbose >= 0)
-         _cupsLangPrintf(stdout, _("        WARN    Missing "
-                                   "APPrinterIconPath file \"%s\"\n"),
-                         attr->value ? attr->value : "<NULL>");
-      }
-#endif /* __APPLE__ */
-
       if (verbose > 0)
       {
         if (errors)
@@ -1639,7 +1832,7 @@ check_basics(const char *filename)        /* I - PPD file to check */
       
       if (col > 0 && whitespace)
        _cupsLangPrintf(stdout,
-                       _("        WARN    Line %d only contains whitespace!\n"),
+                       _("        WARN    Line %d only contains whitespace\n"),
                        linenum);
 
       linenum ++;
@@ -1658,12 +1851,12 @@ check_basics(const char *filename)      /* I - PPD file to check */
   if (mixed)
     _cupsLangPuts(stdout,
                  _("        WARN    File contains a mix of CR, LF, and "
-                   "CR LF line endings!\n"));
+                   "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"));
+                   "ending with only LF, not CR LF\n"));
 
   cupsFileClose(fp);
 }
@@ -1679,142 +1872,551 @@ check_constraints(ppd_file_t *ppd,    /* I - PPD file */
                   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 */
+  int                  i;              /* Looping var */
+  const char           *prefix;        /* WARN/FAIL prefix */
+  ppd_const_t          *c;             /* Current UIConstraints data */
+  ppd_attr_t           *constattr;     /* Current cupsUIConstraints attribute */
+  const char           *vptr;          /* Pointer into constraint value */
+  char                 option[PPD_MAX_NAME],
+                                       /* Option name/MainKeyword */
+                       choice[PPD_MAX_NAME],
+                                       /* Choice/OptionKeyword */
+                       *ptr;           /* Pointer into option or choice */
+  int                  num_options;    /* Number of options */
+  cups_option_t                *options;       /* Options */
+  ppd_option_t         *o;             /* PPD option */
 
 
   prefix = warn ? "  WARN  " : "**FAIL**";
 
-  for (j = ppd->num_consts, c = ppd->consts; j > 0; j --, c ++)
+
+ /*
+  * See what kind of constraint data we have in the PPD...
+  */
+
+  if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
   {
-    option  = ppdFindOption(ppd, c->option1);
-    option2 = ppdFindOption(ppd, c->option2);
+   /*
+    * Check new-style cupsUIConstraints data...
+    */
 
-    if (!option || !option2)
+    for (; constattr;
+         constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
     {
-      if (!warn && !errors && !verbose)
-       _cupsLangPuts(stdout, _(" FAIL\n"));
+      if (!constattr->value)
+      {
+       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"));
+                       _("      %s  Empty cupsUIConstraints %s\n"),
+                       prefix, constattr->spec);
 
-      _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 (!warn)
-        errors ++;
-    }
+        continue;
+      }
 
-    if (c->choice2[0] && !ppdFindChoice(option2, c->choice2))
-    {
-      if (!warn && !errors && !verbose)
-       _cupsLangPuts(stdout, _(" FAIL\n"));
+      for (i = 0, vptr = strchr(constattr->value, '*');
+           vptr;
+          i ++, vptr = strchr(vptr + 1, '*'));
 
-      _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 (i == 0)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
 
-      if (!warn)
-        errors ++;
-    }
-  }
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad cupsUIConstraints %s: \"%s\"\n"),
+                       prefix, constattr->spec, constattr->value);
 
-  return (errors);
-}
+       if (!warn)
+         errors ++;
 
+        continue;
+      }
 
-/*
- * 'check_defaults()' - Check default option keywords in the PPD file.
- */
+      cupsArraySave(ppd->sorted_attrs);
 
-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 */
+      if (constattr->spec[0] &&
+          !ppdFindAttr(ppd, "cupsUIResolver", constattr->spec))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
 
+       _cupsLangPrintf(stdout,
+                       _("      %s  Missing cupsUIResolver %s\n"),
+                       prefix, constattr->spec);
 
-  prefix = warn ? "  WARN  " : "**FAIL**";
+       if (!warn)
+         errors ++;
+      }
 
-  for (j = 0; j < ppd->num_attrs; j ++)
-  {
-    attr = ppd->attrs[j];
+      cupsArrayRestore(ppd->sorted_attrs);
 
-    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;
+      num_options = 0;
+      options     = NULL;
 
-    if (!strncmp(attr->name, "Default", 7))
-    {
-      if ((option = ppdFindOption(ppd, attr->name + 7)) != NULL &&
-         strcmp(attr->value, "Unknown"))
+      for (vptr = strchr(constattr->value, '*');
+           vptr;
+          vptr = strchr(vptr, '*'))
       {
        /*
-       * Check that the default option value matches a choice...
+        * Extract "*Option Choice" or just "*Option"...
        */
 
-       for (k = 0; k < option->num_choices; k ++)
-         if (!strcmp(option->choices[k].choice, attr->value))
-           break;
+        for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
+         if (ptr < (option + sizeof(option) - 1))
+           *ptr++ = *vptr;
 
-       if (k >= option->num_choices)
-       {
-         if (!warn && !errors && !verbose)
-           _cupsLangPuts(stdout, _(" FAIL\n"));
+        *ptr = '\0';
 
-         if (verbose >= 0)
-           _cupsLangPrintf(stdout,
-                           _("      %s  %s %s does not exist!\n"),
-                           prefix, attr->name, attr->value);
+        while (isspace(*vptr & 255))
+         vptr ++;
 
-          if (!warn)
-           errors ++;
+        if (*vptr == '*')
+         choice[0] = '\0';
+       else
+       {
+         for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
+           if (ptr < (choice + sizeof(choice) - 1))
+             *ptr++ = *vptr;
+
+         *ptr = '\0';
        }
-      }
-    }
-  }
+
+        if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
+       {
+         _cups_strcpy(option, option + 6);
+         strcpy(choice, "Custom");
+       }
+
+        if ((o = ppdFindOption(ppd, option)) == NULL)
+       {
+         if (!warn && !errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         _cupsLangPrintf(stdout,
+                         _("      %s  Missing option %s in "
+                           "cupsUIConstraints %s: \"%s\"\n"),
+                         prefix, option, constattr->spec, constattr->value);
+         
+         if (!warn)
+           errors ++;
+
+         continue;
+       }
+
+        if (choice[0] && !ppdFindChoice(o, choice))
+       {
+         if (!warn && !errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         _cupsLangPrintf(stdout,
+                         _("      %s  Missing choice *%s %s in "
+                           "cupsUIConstraints %s: \"%s\"\n"),
+                         prefix, option, choice, constattr->spec,
+                         constattr->value);
+
+         if (!warn)
+           errors ++;
+
+         continue;
+       }
+
+        if (choice[0])
+         num_options = cupsAddOption(option, choice, num_options, &options);
+       else
+       {
+         for (i = 0; i < o->num_choices; i ++)
+           if (strcasecmp(o->choices[i].choice, "None") &&
+               strcasecmp(o->choices[i].choice, "Off") &&
+               strcasecmp(o->choices[i].choice, "False"))
+            {
+             num_options = cupsAddOption(option, o->choices[i].choice,
+                                         num_options, &options);
+              break;
+           }
+       }
+      }
+
+     /*
+      * Resolvers must list at least two options...
+      */
+
+      if (num_options < 2)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       _cupsLangPrintf(stdout,
+                       _("      %s  cupsUIResolver %s does not list at least "
+                         "two different options\n"),
+                       prefix, constattr->spec);
+
+       if (!warn)
+         errors ++;
+      }
+
+     /*
+      * Test the resolver...
+      */
+
+      if (!cupsResolveConflicts(ppd, NULL, NULL, &num_options, &options))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       _cupsLangPrintf(stdout,
+                       _("      %s  cupsUIResolver %s causes a loop\n"),
+                       prefix, constattr->spec);
+
+       if (!warn)
+         errors ++;
+      }
+
+      cupsFreeOptions(num_options, options);
+    }
+  }
+  else
+  {
+   /*
+    * Check old-style [Non]UIConstraints data...
+    */
+
+    for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
+    {
+      if (!strncasecmp(c->option1, "Custom", 6) &&
+          !strcasecmp(c->choice1, "True"))
+      {
+       strcpy(option, c->option1 + 6);
+       strcpy(choice, "Custom");
+      }
+      else
+      {
+       strcpy(option, c->option1);
+       strcpy(choice, c->choice1);
+      }
+
+      if ((o = ppdFindOption(ppd, option)) == NULL)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       _cupsLangPrintf(stdout,
+                       _("      %s  Missing option %s in "
+                         "UIConstraints \"*%s %s *%s %s\"\n"),
+                       prefix, c->option1,
+                       c->option1, c->choice1, c->option2, c->choice2);
+
+       if (!warn)
+         errors ++;
+      }
+      else if (choice[0] && !ppdFindChoice(o, choice))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       _cupsLangPrintf(stdout,
+                       _("      %s  Missing choice *%s %s in "
+                         "UIConstraints \"*%s %s *%s %s\"\n"),
+                       prefix, c->option1, c->choice1,
+                       c->option1, c->choice1, c->option2, c->choice2);
+
+       if (!warn)
+         errors ++;
+      }
+
+      if (!strncasecmp(c->option2, "Custom", 6) &&
+          !strcasecmp(c->choice2, "True"))
+      {
+       strcpy(option, c->option2 + 6);
+       strcpy(choice, "Custom");
+      }
+      else
+      {
+       strcpy(option, c->option2);
+       strcpy(choice, c->choice2);
+      }
+
+      if ((o = ppdFindOption(ppd, option)) == NULL)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       _cupsLangPrintf(stdout,
+                       _("      %s  Missing option %s in "
+                         "UIConstraints \"*%s %s *%s %s\"\n"),
+                       prefix, c->option2,
+                       c->option1, c->choice1, c->option2, c->choice2);
+
+       if (!warn)
+         errors ++;
+      }
+      else if (choice[0] && !ppdFindChoice(o, choice))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       _cupsLangPrintf(stdout,
+                       _("      %s  Missing choice *%s %s in "
+                         "UIConstraints \"*%s %s *%s %s\"\n"),
+                       prefix, c->option2, c->choice2,
+                       c->option1, c->choice1, c->option2, c->choice2);
+
+       if (!warn)
+         errors ++;
+      }
+    }
+  }
+
+  return (errors);
+}
+
+
+/*
+ * 'check_case()' - Check that there are no duplicate groups, options,
+ *                  or choices that differ only by case.
+ */
+
+static int                             /* O - Errors found */
+check_case(ppd_file_t *ppd,            /* I - PPD file */
+           int        errors,          /* I - Errors found */
+          int        verbose)          /* I - Verbosity level */
+{
+  int          i, j;                   /* Looping vars */
+  ppd_group_t  *groupa,                /* First group */
+               *groupb;                /* Second group */
+  ppd_option_t *optiona,               /* First option */
+               *optionb;               /* Second option */
+  ppd_choice_t *choicea,               /* First choice */
+               *choiceb;               /* Second choice */
+
+
+ /*
+  * Check that the groups do not have any duplicate names...
+  */
+
+  for (i = ppd->num_groups, groupa = ppd->groups; i > 1; i --, groupa ++)
+    for (j = i - 1, groupb = groupa + 1; j > 0; j --, groupb ++)
+      if (!strcasecmp(groupa->name, groupb->name))
+      {
+       if (!errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout,
+                         _("      **FAIL**  Group names %s and %s differ only "
+                           "by case\n"),
+                         groupa->name, groupb->name);
+
+       errors ++;
+      }
+
+ /*
+  * Check that the options do not have any duplicate names...
+  */
+
+  for (optiona = ppdFirstOption(ppd); optiona; optiona = ppdNextOption(ppd))
+  {
+    cupsArraySave(ppd->options);
+    for (optionb = ppdNextOption(ppd); optionb; optionb = ppdNextOption(ppd))
+      if (!strcasecmp(optiona->keyword, optionb->keyword))
+      {
+       if (!errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout,
+                         _("      **FAIL**  Option names %s and %s differ only "
+                           "by case\n"),
+                         optiona->keyword, optionb->keyword);
+
+       errors ++;
+      }
+    cupsArrayRestore(ppd->options);
+
+   /*
+    * Then the choices...
+    */
+
+    for (i = optiona->num_choices, choicea = optiona->choices;
+         i > 1;
+        i --, choicea ++)
+      for (j = i - 1, choiceb = choicea + 1; j > 0; j --, choiceb ++)
+        if (!strcmp(choicea->choice, choiceb->choice))
+       {
+         if (!errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("      **FAIL**  Multiple occurrences of %s "
+                             "choice name %s\n"),
+                           optiona->keyword, choicea->choice);
+
+         errors ++;
+
+         choicea ++;
+         i --;
+         break;
+       }
+        else if (!strcasecmp(choicea->choice, choiceb->choice))
+       {
+         if (!errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         if (verbose >= 0)
+           _cupsLangPrintf(stdout,
+                           _("      **FAIL**  %s choice names %s and %s "
+                             "differ only by case\n"),
+                           optiona->keyword, choicea->choice, choiceb->choice);
+
+         errors ++;
+       }
+  }
+
+ /*
+  * Return the number of errors found...
+  */
+
+  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_duplex()' - Check duplex keywords in the PPD file.
+ */
+
+static int                             /* O - Errors found */
+check_duplex(ppd_file_t *ppd,          /* I - PPD file */
+             int        errors,                /* I - Error found */
+            int        verbose,        /* I - Verbosity level */
+             int        warn)          /* I - Warnings only? */
+{
+  int          i;                      /* Looping var */
+  ppd_option_t *option;                /* PPD option */
+  ppd_choice_t *choice;                /* Current choice */
+  const char   *prefix;                /* Message prefix */
+
+
+  prefix = warn ? "  WARN  " : "**FAIL**";
+
+ /*
+  * Check for a duplex option, and for standard values...
+  */
+
+  if ((option = ppdFindOption(ppd, "Duplex")) != NULL)
+  {
+    if (!ppdFindChoice(option, "None"))
+    {
+      if (verbose >= 0)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       _cupsLangPrintf(stdout,
+                       _("      %s  REQUIRED %s does not define "
+                         "choice None\n"
+                         "                REF: Page 122, section 5.17\n"),
+                       prefix, option->keyword);
+      }
+
+      if (!warn)
+       errors ++;
+    }
+
+    for (i = option->num_choices, choice = option->choices;
+        i > 0;
+        i --, choice ++)
+      if (strcmp(choice->choice, "None") &&
+         strcmp(choice->choice, "DuplexNoTumble") &&
+         strcmp(choice->choice, "DuplexTumble") &&
+         strcmp(choice->choice, "SimplexTumble"))
+      {
+       if (verbose >= 0)
+       {
+         if (!warn && !errors && !verbose)
+           _cupsLangPuts(stdout, _(" FAIL\n"));
+
+         _cupsLangPrintf(stdout,
+                         _("      %s  Bad %s choice %s\n"
+                           "                REF: Page 122, section 5.17\n"),
+                         prefix, option->keyword, choice->choice);
+       }
+
+       if (!warn)
+         errors ++;
+      }
+  }
 
   return (errors);
 }
@@ -1840,10 +2442,15 @@ check_filters(ppd_file_t *ppd,          /* I - PPD file */
                pathprog[1024];         /* Complete path to program/filter */
   int          cost;                   /* Cost of filter */
   const char   *prefix;                /* WARN/FAIL prefix */
+  struct stat  fileinfo;               /* File information */
 
 
   prefix = warn ? "  WARN  " : "**FAIL**";
 
+ /*
+  * cupsFilter
+  */
+
   for (i = 0; i < ppd->num_filters; i ++)
   {
     if (sscanf(ppd->filters[i], "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
@@ -1854,94 +2461,440 @@ check_filters(ppd_file_t *ppd,         /* I - PPD file */
 
       if (verbose >= 0)
        _cupsLangPrintf(stdout,
-                       _("      %s  Bad cupsFilter value \"%s\"!\n"),
-                       prefix, ppd->filters[i]);
+                       _("      %s  Bad cupsFilter value \"%s\"\n"),
+                       prefix, ppd->filters[i]);
+
+      if (!warn)
+        errors ++;
+    }
+    else if (strcmp(program, "-"))
+    {
+      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, &fileinfo))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout, _("      %s  Missing cupsFilter "
+                                   "file \"%s\"\n"), prefix, pathprog);
+
+       if (!warn)
+         errors ++;
+      }
+      else if (fileinfo.st_uid != 0 ||
+               (fileinfo.st_mode & MODE_WRITE) ||
+              (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout, _("      %s  Bad permissions on cupsFilter "
+                                   "file \"%s\"\n"), prefix, pathprog);
+
+       if (!warn)
+         errors ++;
+      }
+      else
+        errors = valid_path("cupsFilter", pathprog, errors, verbose, warn);
+    }
+  }
+
+ /*
+  * cupsPreFilter
+  */
+
+  for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
+       attr;
+       attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
+  {
+    if (strcmp(attr->name, "cupsPreFilter"))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad spelling of %s - should be %s\n"),
+                       prefix, attr->name, "cupsPreFilter");
+
+      if (!warn)
+        errors ++;
+    }
+
+    if (!attr->value ||
+       sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", 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 (strcmp(program, "-"))
+    {
+      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, &fileinfo))
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout, _("      %s  Missing cupsPreFilter "
+                                   "file \"%s\"\n"), prefix, pathprog);
+
+        if (!warn)
+         errors ++;
+      }
+      else if (fileinfo.st_uid != 0 ||
+               (fileinfo.st_mode & MODE_WRITE) ||
+              (fileinfo.st_mode & MODE_MASK) != MODE_PROGRAM)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout, _("      %s  Bad permissions on "
+                                   "cupsPreFilter file \"%s\"\n"), prefix,
+                         pathprog);
+
+       if (!warn)
+         errors ++;
+      }
+      else
+        errors = valid_path("cupsPreFilter", pathprog, errors, verbose, warn);
+    }
+  }
+
+#ifdef __APPLE__
+ /*
+  * APDialogExtension
+  */
+
+  for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL); 
+       attr != NULL; 
+       attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL))
+  {
+    if (strcmp(attr->name, "APDialogExtension"))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad spelling of %s - should be %s\n"),
+                       prefix, attr->name, "APDialogExtension");
+
+      if (!warn)
+        errors ++;
+    }
+    
+    snprintf(pathprog, sizeof(pathprog), "%s%s", root,
+             attr->value ? attr->value : "(null)");
+
+    if (!attr->value || stat(pathprog, &fileinfo))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Missing "
+                                 "APDialogExtension file \"%s\"\n"),
+                       prefix, pathprog);
+
+      if (!warn)
+       errors ++;
+    }
+    else if (fileinfo.st_uid != 0 ||
+            (fileinfo.st_mode & MODE_WRITE) ||
+            (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Bad permissions on "
+                                 "APDialogExtension file \"%s\"\n"), prefix,
+                       pathprog);
+
+      if (!warn)
+       errors ++;
+    }
+    else
+      errors = valid_path("APDialogExtension", pathprog, errors, verbose,
+                          warn);
+  }
+
+ /*
+  * APPrinterIconPath
+  */
+
+  if ((attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
+  {
+    if (strcmp(attr->name, "APPrinterIconPath"))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad spelling of %s - should be %s\n"),
+                       prefix, attr->name, "APPrinterIconPath");
+
+      if (!warn)
+        errors ++;
+    }
+
+    snprintf(pathprog, sizeof(pathprog), "%s%s", root,
+             attr->value ? attr->value : "(null)");
+
+    if (!attr->value || stat(pathprog, &fileinfo))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Missing "
+                                 "APPrinterIconPath file \"%s\"\n"),
+                       prefix, pathprog);
+
+      if (!warn)
+       errors ++;
+    }
+    else if (fileinfo.st_uid != 0 ||
+            (fileinfo.st_mode & MODE_WRITE) ||
+            (fileinfo.st_mode & MODE_MASK) != MODE_DATAFILE)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Bad permissions on "
+                                 "APPrinterIconPath file \"%s\"\n"), prefix,
+                       pathprog);
+
+      if (!warn)
+       errors ++;
+    }
+    else
+      errors = valid_path("APPrinterIconPath", pathprog, errors, verbose,
+                          warn);
+  }
+
+ /*
+  * APPrinterLowInkTool
+  */
+
+  if ((attr = ppdFindAttr(ppd, "APPrinterLowInkTool", NULL)) != NULL)
+  {
+    if (strcmp(attr->name, "APPrinterLowInkTool"))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad spelling of %s - should be %s\n"),
+                       prefix, attr->name, "APPrinterLowInkTool");
+
+      if (!warn)
+        errors ++;
+    }
+
+    snprintf(pathprog, sizeof(pathprog), "%s%s", root,
+             attr->value ? attr->value : "(null)");
+
+    if (!attr->value || stat(pathprog, &fileinfo))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Missing "
+                                 "APPrinterLowInkTool file \"%s\"\n"),
+                       prefix, pathprog);
+
+      if (!warn)
+       errors ++;
+    }
+    else if (fileinfo.st_uid != 0 ||
+            (fileinfo.st_mode & MODE_WRITE) ||
+            (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Bad permissions on "
+                                 "APPrinterLowInkTool file \"%s\"\n"), prefix,
+                       pathprog);
+
+      if (!warn)
+       errors ++;
+    }
+    else
+      errors = valid_path("APPrinterLowInkTool", pathprog, errors, verbose,
+                          warn);
+  }
+
+ /*
+  * APPrinterUtilityPath
+  */
+
+  if ((attr = ppdFindAttr(ppd, "APPrinterUtilityPath", NULL)) != NULL)
+  {
+    if (strcmp(attr->name, "APPrinterUtilityPath"))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Bad spelling of %s - should be %s\n"),
+                       prefix, attr->name, "APPrinterUtilityPath");
 
       if (!warn)
         errors ++;
     }
-    else
+
+    snprintf(pathprog, sizeof(pathprog), "%s%s", root,
+             attr->value ? attr->value : "(null)");
+
+    if (!attr->value || stat(pathprog, &fileinfo))
     {
-      if (program[0] == '/')
-       snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
-      else
-      {
-       if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
-         ptr = CUPS_SERVERBIN;
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
 
-       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 (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Missing "
+                                 "APPrinterUtilityPath file \"%s\"\n"),
+                       prefix, pathprog);
 
-      if (access(pathprog, X_OK))
-      {
-       if (!warn && !errors && !verbose)
-         _cupsLangPuts(stdout, _(" FAIL\n"));
+      if (!warn)
+       errors ++;
+    }
+    else if (fileinfo.st_uid != 0 ||
+            (fileinfo.st_mode & MODE_WRITE) ||
+            (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
 
-       if (verbose >= 0)
-         _cupsLangPrintf(stdout, _("      %s  Missing cupsFilter "
-                                   "file \"%s\"\n"), prefix, program);
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Bad permissions on "
+                                 "APPrinterUtilityPath file \"%s\"\n"), prefix,
+                       pathprog);
 
-       if (!warn)
-         errors ++;
-      }
+      if (!warn)
+       errors ++;
     }
+    else
+      errors = valid_path("APPrinterUtilityPath", pathprog, errors, verbose,
+                          warn);
   }
 
-  for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL);
-       attr;
-       attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL))
+ /*
+  * APScanAppBundleID and APScanAppPath
+  */
+
+  if ((attr = ppdFindAttr(ppd, "APScanAppPath", NULL)) != NULL)
   {
-    if (!attr->value ||
-       sscanf(attr->value, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type,
-              &cost, program) != 4)
+    if (strcmp(attr->name, "APScanAppPath"))
     {
       if (!warn && !errors && !verbose)
        _cupsLangPuts(stdout, _(" FAIL\n"));
 
       if (verbose >= 0)
        _cupsLangPrintf(stdout,
-                       _("      %s  Bad cupsPreFilter value \"%s\"!\n"),
-                       prefix, attr->value ? attr->value : "");
+                       _("      %s  Bad spelling of %s - should be %s\n"),
+                       prefix, attr->name, "APScanAppPath");
 
       if (!warn)
         errors ++;
     }
-    else
+
+    if (!attr->value || stat(attr->value, &fileinfo))
     {
-      if (program[0] == '/')
-       snprintf(pathprog, sizeof(pathprog), "%s%s", root, program);
-      else
-      {
-       if ((ptr = getenv("CUPS_SERVERBIN")) == NULL)
-         ptr = CUPS_SERVERBIN;
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
 
-       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 (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Missing "
+                                 "APScanAppPath file \"%s\"\n"),
+                       prefix, attr->value ? attr->value : "<NULL>");
 
-      if (access(pathprog, X_OK))
-      {
-       if (!warn && !errors && !verbose)
-         _cupsLangPuts(stdout, _(" FAIL\n"));
+      if (!warn)
+       errors ++;
+    }
+    else if (fileinfo.st_uid != 0 ||
+            (fileinfo.st_mode & MODE_WRITE) ||
+            (fileinfo.st_mode & MODE_MASK) != MODE_DIRECTORY)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
 
-       if (verbose >= 0)
-         _cupsLangPrintf(stdout, _("      %s  Missing cupsPreFilter "
-                                   "file \"%s\"\n"), prefix, program);
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Bad permissions on "
+                                 "APScanAppPath file \"%s\"\n"), prefix,
+                       attr->value);
 
-        if (!warn)
-         errors ++;
-      }
+      if (!warn)
+       errors ++;
+    }
+    else
+      errors = valid_path("APScanAppPath", attr->value, errors, verbose,
+                          warn);
+
+    if (ppdFindAttr(ppd, "APScanAppBundleID", NULL))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Cannot provide both "
+                                 "APScanAppPath and APScanAppBundleID\n"),
+                       prefix);
+
+      if (!warn)
+       errors ++;
     }
   }
+#endif /* __APPLE__ */
 
   return (errors);
 }
@@ -1963,6 +2916,7 @@ check_profiles(ppd_file_t *ppd,           /* I - PPD file */
   const char   *ptr;                   /* Pointer into string */
   const char   *prefix;                /* WARN/FAIL prefix */
   char         filename[1024];         /* Profile filename */
+  struct stat  fileinfo;               /* File information */
   int          num_profiles = 0;       /* Number of profiles */
   unsigned     hash,                   /* Current hash value */
                hashes[1000];           /* Hash values of profile names */
@@ -1989,7 +2943,7 @@ check_profiles(ppd_file_t *ppd,           /* I - PPD file */
 
       if (verbose >= 0)
        _cupsLangPrintf(stdout,
-                       _("      %s  Bad cupsICCProfile %s!\n"),
+                       _("      %s  Bad cupsICCProfile %s\n"),
                        prefix, attr->spec);
 
       if (!warn)
@@ -2017,18 +2971,35 @@ check_profiles(ppd_file_t *ppd,         /* I - PPD file */
                 attr->value);
     }
 
-    if (access(filename, 0))
+    if (stat(filename, &fileinfo))
     {
       if (!warn && !errors && !verbose)
        _cupsLangPuts(stdout, _(" FAIL\n"));
 
       if (verbose >= 0)
        _cupsLangPrintf(stdout, _("      %s  Missing cupsICCProfile "
-                                 "file \"%s\"!\n"), prefix, attr->value);
+                                 "file \"%s\"\n"), prefix, filename);
+
+      if (!warn)
+       errors ++;
+    }
+    else if (fileinfo.st_uid != 0 ||
+            (fileinfo.st_mode & MODE_WRITE) ||
+            (fileinfo.st_mode & MODE_MASK) != MODE_DATAFILE)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout, _("      %s  Bad permissions on "
+                                 "cupsICCProfile file \"%s\"\n"), prefix,
+                       filename);
 
       if (!warn)
        errors ++;
     }
+    else
+      errors = valid_path("cupsICCProfile", filename, errors, verbose, warn);
 
    /*
     * Check for hash collisions...
@@ -2050,7 +3021,7 @@ check_profiles(ppd_file_t *ppd,           /* I - PPD file */
        if (verbose >= 0)
          _cupsLangPrintf(stdout,
                          _("      %s  cupsICCProfile %s hash value "
-                           "collides with %s!\n"), prefix, attr->spec,
+                           "collides with %s\n"), prefix, attr->spec,
                          specs[i]);
 
        if (!warn)
@@ -2074,6 +3045,158 @@ check_profiles(ppd_file_t *ppd,         /* I - PPD file */
 }
 
 
+/*
+ * 'check_sizes()' - Check media sizes in the PPD file.
+ */
+
+static int                             /* O - Errors found */
+check_sizes(ppd_file_t *ppd,           /* I - PPD file */
+           int        errors,          /* I - Errors found */
+           int        verbose,         /* I - Verbosity level */
+           int        warn)            /* I - Warnings only? */
+{
+  int          i;                      /* Looping vars */
+  ppd_size_t   *size;                  /* Current size */
+  int          width,                  /* Custom width */
+               length;                 /* Custom length */
+  char         name[PPD_MAX_NAME],     /* Size name without dot suffix */
+               *nameptr;               /* Pointer into name */
+  const char   *prefix;                /* WARN/FAIL prefix */
+  ppd_option_t *page_size,             /* PageSize option */
+               *page_region;           /* PageRegion option */
+
+
+  prefix = warn ? "  WARN  " : "**FAIL**";
+
+  if ((page_size = ppdFindOption(ppd, "PageSize")) == NULL && warn != 2)
+  {
+    if (!warn && !errors && !verbose)
+      _cupsLangPuts(stdout, _(" FAIL\n"));
+
+    if (verbose >= 0)
+      _cupsLangPrintf(stdout,
+                     _("      %s  Missing REQUIRED PageSize option\n"
+                       "                REF: Page 99, section 5.14.\n"),
+                     prefix);
+
+    if (!warn)
+      errors ++;
+  }
+
+  if ((page_region = ppdFindOption(ppd, "PageRegion")) == NULL && warn != 2)
+  {
+    if (!warn && !errors && !verbose)
+      _cupsLangPuts(stdout, _(" FAIL\n"));
+
+    if (verbose >= 0)
+      _cupsLangPrintf(stdout,
+                     _("      %s  Missing REQUIRED PageRegion option\n"
+                       "                REF: Page 100, section 5.14.\n"),
+                     prefix);
+
+    if (!warn)
+      errors ++;
+  }
+
+  for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+  {
+   /*
+    * Check that the size name is standard...
+    */
+
+    if (!strcmp(size->name, "Custom"))
+    {
+     /*
+      * Skip custom page size...
+      */
+
+      continue;
+    }
+    else if (warn != 2 && size->name[0] == 'w' &&
+             sscanf(size->name, "w%dh%d", &width, &length) == 2)
+    {
+     /*
+      * Validate device-specific size wNNNhNNN should have proper width and
+      * length...
+      */
+
+      if (fabs(width - size->width) >= 1.0 ||
+          fabs(length - size->length) >= 1.0)
+      {
+       if (!warn && !errors && !verbose)
+         _cupsLangPuts(stdout, _(" FAIL\n"));
+
+       if (verbose >= 0)
+         _cupsLangPrintf(stdout,
+                         _("      %s  Size \"%s\" has unexpected dimensions "
+                           "(%gx%g)\n"),
+                         prefix, size->name, size->width, size->length);
+
+       if (!warn)
+         errors ++;
+      }
+    }
+    else if (warn && verbose >= 0)
+    {
+     /*
+      * Lookup the size name in the standard size table...
+      */
+
+      strlcpy(name, size->name, sizeof(name));
+      if ((nameptr = strchr(name, '.')) != NULL)
+        *nameptr = '\0';
+
+      if (!bsearch(name, adobe_size_names,
+                   sizeof(adobe_size_names) /
+                      sizeof(adobe_size_names[0]),
+                  sizeof(adobe_size_names[0]),
+                  (int (*)(const void *, const void *))strcmp))
+      {
+       _cupsLangPrintf(stdout,
+                       _("      %s  Non-standard size name \"%s\"\n"
+                         "                REF: Page 187, section B.2.\n"),
+                       prefix, size->name);
+      }
+    }
+
+   /*
+    * Verify that the size is defined for both PageSize and PageRegion...
+    */
+
+    if (warn != 2 && !ppdFindChoice(page_size, size->name))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Size \"%s\" defined for %s but not for "
+                         "%s\n"),
+                       prefix, size->name, "PageRegion", "PageSize");
+
+      if (!warn)
+       errors ++;
+    }
+    else if (warn != 2 && !ppdFindChoice(page_region, size->name))
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  Size \"%s\" defined for %s but not for "
+                         "%s\n"),
+                       prefix, size->name, "PageSize", "PageRegion");
+
+      if (!warn)
+       errors ++;
+    }
+  }
+
+  return (errors);
+}
+
+
 /*
  * 'check_translations()' - Check translations in the PPD file.
  */
@@ -2099,6 +3222,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
   ppd_cparam_t *cparam;                /* Custom parameter */
   char         ll[3];                  /* Base language */
   const char   *prefix;                /* WARN/FAIL prefix */
+  const char   *text;                  /* Pointer into UI text */
 
 
   prefix = warn ? "  WARN  " : "**FAIL**";
@@ -2113,7 +3237,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
          language;
         language = (char *)cupsArrayNext(languages))
     {
-      langlen = strlen(language);
+      langlen = (int)strlen(language);
       if (langlen != 2 && langlen != 5)
       {
        if (!warn && !errors && !verbose)
@@ -2121,7 +3245,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
 
        if (verbose >= 0)
          _cupsLangPrintf(stdout,
-                         _("      %s  Bad language \"%s\"!\n"),
+                         _("      %s  Bad language \"%s\"\n"),
                          prefix, language);
 
        if (!warn)
@@ -2158,7 +3282,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
          if (verbose >= 0)
            _cupsLangPrintf(stdout,
                            _("      %s  Missing \"%s\" translation "
-                             "string for option %s!\n"),
+                             "string for option %s\n"),
                            prefix, language, option->keyword);
 
           if (!warn)
@@ -2172,7 +3296,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
          if (verbose >= 0)
            _cupsLangPrintf(stdout,
                            _("      %s  Bad UTF-8 \"%s\" translation "
-                             "string for option %s!\n"),
+                             "string for option %s\n"),
                            prefix, language, option->keyword);
 
          if (!warn)
@@ -2186,6 +3310,22 @@ check_translations(ppd_file_t *ppd,      /* I - PPD file */
 
        for (j = 0; j < option->num_choices; j ++)
        {
+         /*
+         * First see if this choice is a number; if so, don't require
+         * translation...
+         */
+
+          for (text = option->choices[j].text; *text; text ++)
+           if (!strchr("0123456789-+.", *text))
+             break;
+
+          if (!*text)
+           continue;
+
+        /*
+         * Check custom choices differently...
+         */
+
          if (!strcasecmp(option->choices[j].choice, "Custom") &&
              (coption = ppdFindCustomOption(ppd,
                                             option->keyword)) != NULL)
@@ -2203,7 +3343,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
                _cupsLangPrintf(stdout,
                                _("      %s  Bad UTF-8 \"%s\" "
                                  "translation string for option %s, "
-                                 "choice %s!\n"),
+                                 "choice %s\n"),
                                prefix, language,
                                ckeyword + 1 + strlen(language),
                                "True");
@@ -2235,7 +3375,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
                    _cupsLangPrintf(stdout,
                                    _("      %s  Missing \"%s\" "
                                      "translation string for option %s, "
-                                     "choice %s!\n"),
+                                     "choice %s\n"),
                                    prefix, language,
                                    ckeyword + 1 + strlen(language),
                                    cparam->name);
@@ -2252,7 +3392,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
                    _cupsLangPrintf(stdout,
                                    _("      %s  Bad UTF-8 \"%s\" "
                                      "translation string for option %s, "
-                                     "choice %s!\n"),
+                                     "choice %s\n"),
                                    prefix, language,
                                    ckeyword + 1 + strlen(language),
                                    cparam->name);
@@ -2275,7 +3415,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
              _cupsLangPrintf(stdout,
                              _("      %s  Missing \"%s\" "
                                "translation string for option %s, "
-                               "choice %s!\n"),
+                               "choice %s\n"),
                              prefix, language, option->keyword,
                              option->choices[j].choice);
 
@@ -2291,7 +3431,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
              _cupsLangPrintf(stdout,
                              _("      %s  Bad UTF-8 \"%s\" "
                                "translation string for option %s, "
-                               "choice %s!\n"),
+                               "choice %s\n"),
                              prefix, language, option->keyword,
                              option->choices[j].choice);
 
@@ -2319,7 +3459,8 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
 
        strlcpy(ll, language, sizeof(ll));
 
-       if (!cupsArrayFind(languages, ll) && strcmp(ll, "zh"))
+       if (!cupsArrayFind(languages, ll) &&
+           strcmp(ll, "zh") && strcmp(ll, "en"))
        {
          if (!warn && !errors && !verbose)
            _cupsLangPuts(stdout, _(" FAIL\n"));
@@ -2327,7 +3468,7 @@ check_translations(ppd_file_t *ppd,       /* I - PPD file */
          if (verbose >= 0)
            _cupsLangPrintf(stdout,
                            _("      %s  No base translation \"%s\" "
-                             "is included in file!\n"), prefix, ll);
+                             "is included in file\n"), prefix, ll);
 
          if (!warn)
            errors ++;
@@ -2512,8 +3653,11 @@ usage(void)
                  "\n"
                  "Options:\n"
                  "\n"
+                 "    -I {filters,profiles}\n"
+                 "                         Ignore missing files\n"
                  "    -R root-directory    Set alternate root\n"
-                 "    -W {all,none,constraints,defaults,filters,translations}\n"
+                 "    -W {all,none,constraints,defaults,duplex,filters,"
+                 "profiles,sizes,translations}\n"
                  "                         Issue warnings instead of errors\n"
                  "    -q                   Run silently\n"
                  "    -r                   Use 'relaxed' open mode\n"
@@ -2524,6 +3668,89 @@ usage(void)
 }
 
 
+/*
+ * 'valid_path()' - Check whether a path has the correct capitalization.
+ */
+
+static int                             /* O - Errors found */
+valid_path(const char *keyword,                /* I - Keyword using path */
+           const char *path,           /* I - Path to check */
+          int        errors,           /* I - Errors found */
+          int        verbose,          /* I - Verbosity level */
+          int        warn)             /* I - Warnings only? */
+{
+  cups_dir_t   *dir;                   /* Current directory */
+  cups_dentry_t        *dentry;                /* Current directory entry */
+  char         temp[1024],             /* Temporary path */
+               *ptr;                   /* Pointer into temporary path */
+  const char   *prefix;                /* WARN/FAIL prefix */
+
+
+  prefix = warn ? "  WARN  " : "**FAIL**";
+
+ /*
+  * Loop over the components of the path, checking that the entry exists with
+  * the same capitalization...
+  */
+
+  strlcpy(temp, path, sizeof(temp));
+
+  while ((ptr = strrchr(temp, '/')) != NULL)
+  {
+   /*
+    * Chop off the trailing component so temp == dirname and ptr == basename.
+    */
+
+    *ptr++ = '\0';
+
+   /*
+    * Try opening the directory containing the base name...
+    */
+
+    if (temp[0])
+      dir = cupsDirOpen(temp);
+    else
+      dir = cupsDirOpen("/");
+
+    if (!dir)
+      dentry = NULL;
+    else
+    {
+      while ((dentry = cupsDirRead(dir)) != NULL)
+      {
+        if (!strcmp(dentry->filename, ptr))
+         break;
+      }
+
+      cupsDirClose(dir);
+    }
+
+   /*
+    * Display an error if the filename doesn't exist with the same
+    * capitalization...
+    */
+
+    if (!dentry)
+    {
+      if (!warn && !errors && !verbose)
+       _cupsLangPuts(stdout, _(" FAIL\n"));
+
+      if (verbose >= 0)
+       _cupsLangPrintf(stdout,
+                       _("      %s  %s file \"%s\" has the wrong "
+                         "capitalization\n"), prefix, keyword, path);
+
+      if (!warn)
+       errors ++;
+
+      break;
+    }
+  }
+
+  return (errors);
+}
+
+
 /*
  * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
  */
@@ -2601,5 +3828,5 @@ valid_utf8(const char *s)         /* I - String to check */
 
 
 /*
- * End of "$Id: cupstestppd.c 7637 2008-06-11 17:25:36Z mike $".
+ * End of "$Id: cupstestppd.c 7807 2008-07-28 21:54:24Z mike $".
  */