]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ppd.c
Update dependencies.
[thirdparty/cups.git] / cups / ppd.c
index a093ab2b6e3378fd98caeb4c66cc4f147e2581be..54740b896c311d1d2af5db59f53ccf4fa9ac817a 100644 (file)
@@ -1,25 +1,17 @@
+#define DEBUG
 /*
- * "$Id: ppd.c,v 1.51.2.42 2003/02/19 18:08:36 mike Exp $"
+ * "$Id$"
  *
  *   PPD file routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2003 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-3111 USA
- *
- *       Voice: (301) 373-9603
- *       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:
  *
- *   _ppd_attr_compare()   - Compare two attributes.
- *   ppdClose()            - Free all memory used by the PPD file.
- *   ppdErrorString()      - Returns the text assocated with a status.
- *   ppdLastError()        - Return the status from the last ppdOpen*().
- *   ppdOpen()             - Read a PPD file into memory.
- *   ppdOpenFd()           - Read a PPD file into memory.
- *   ppdOpenFile()         - Read a PPD file into memory.
- *   ppd_add_attr()        - Add an attribute to the PPD data.
- *   ppd_add_choice()      - Add a choice to an option.
- *   ppd_add_size()        - Add a page size.
- *   ppd_compare_groups()  - Compare two groups.
- *   ppd_compare_options() - Compare two options.
- *   ppd_decode()          - Decode a string value...
- *   ppd_fix()             - Fix WinANSI characters in the range 0x80 to
- *                           0x9f to be valid ISO-8859-1 characters...
- *   ppd_free_group()      - Free a single UI group.
- *   ppd_free_option()     - Free a single option.
- *   ppd_get_extopt()      - Get an extended option record.
- *   ppd_get_group()       - Find or create the named group as needed.
- *   ppd_get_option()      - Find or create the named option as needed.
- *   ppd_read()            - Read a line from a PPD file, skipping comment
- *                           lines as necessary.
+ *   ppdClose()             - Free all memory used by the PPD file.
+ *   ppdErrorString()       - Returns the text assocated with a status.
+ *   _ppdGetEncoding()      - Get the CUPS encoding value for the given
+ *                            LanguageEncoding.
+ *   ppdLastError()         - Return the status from the last ppdOpen*().
+ *   ppdOpen()              - Read a PPD file into memory.
+ *   ppdOpen2()             - Read a PPD file into memory.
+ *   ppdOpenFd()            - Read a PPD file into memory.
+ *   ppdOpenFile()          - Read a PPD file into memory.
+ *   ppdSetConformance()    - Set the conformance level for PPD files.
+ *   ppd_add_attr()         - Add an attribute to the PPD data.
+ *   ppd_add_choice()       - Add a choice to an option.
+ *   ppd_add_size()         - Add a page size.
+ *   ppd_compare_attrs()    - Compare two attributes.
+ *   ppd_compare_choices()  - Compare two choices...
+ *   ppd_compare_consts()   - Compare two constraints.
+ *   ppd_compare_coptions() - Compare two custom options.
+ *   ppd_compare_cparams()  - Compare two custom parameters.
+ *   ppd_compare_options()  - Compare two options.
+ *   ppd_decode()           - Decode a string value...
+ *   ppd_free_group()       - Free a single UI group.
+ *   ppd_free_option()      - Free a single option.
+ *   ppd_get_coption()      - Get a custom option record.
+ *   ppd_get_cparam()       - Get a custom parameter record.
+ *   ppd_get_group()        - Find or create the named group as needed.
+ *   ppd_get_option()       - Find or create the named option as needed.
+ *   ppd_hash_option()      - Generate a hash of the option name...
+ *   ppd_read()             - Read a line from a PPD file, skipping comment
+ *                            lines as necessary.
  */
 
 /*
  * Include necessary headers.
  */
 
-#include "ppd.h"
-#include <stdlib.h>
-#include <ctype.h>
-#include "string.h"
-#include "language.h"
+#include "globals.h"
 #include "debug.h"
+#include <stdlib.h>
 
 
 /*
 #define PPD_TEXT       4               /* Line contained human-readable text */
 #define PPD_STRING     8               /* Line contained a string or code */
 
-
-/*
- * Local globals...
- */
-
-static ppd_status_t    ppd_status = PPD_OK;
-                                       /* Status of last ppdOpen*() */
-static int             ppd_line = 0;   /* Current line number */
+#define PPD_HASHSIZE   512             /* Size of hash */
 
 
 /*
@@ -104,47 +93,32 @@ static int         ppd_line = 0;   /* Current line number */
  */
 
 static ppd_attr_t      *ppd_add_attr(ppd_file_t *ppd, const char *name,
-                                     const char *spec, const char *value);
+                                     const char *spec, const char *text,
+                                     const char *value);
 static ppd_choice_t    *ppd_add_choice(ppd_option_t *option, const char *name);
 static ppd_size_t      *ppd_add_size(ppd_file_t *ppd, const char *name);
-#ifndef __APPLE__
-static int             ppd_compare_groups(ppd_group_t *g0, ppd_group_t *g1);
-static int             ppd_compare_options(ppd_option_t *o0, ppd_option_t *o1);
-#endif /* !__APPLE__ */
-static void            ppd_decode(char *string);
-#ifndef __APPLE__
-static void            ppd_fix(char *string);
-#else
-#  define ppd_fix(s)
-#endif /* !__APPLE__ */
+static int             ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b);
+static int             ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b);
+static int             ppd_compare_consts(ppd_const_t *a, ppd_const_t *b);
+static int             ppd_compare_coptions(ppd_coption_t *a,
+                                            ppd_coption_t *b);
+static int             ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
+static int             ppd_compare_options(ppd_option_t *a, ppd_option_t *b);
+static int             ppd_decode(char *string);
 static void            ppd_free_group(ppd_group_t *group);
 static void            ppd_free_option(ppd_option_t *option);
-static ppd_ext_option_t        *ppd_get_extopt(ppd_file_t *ppd, const char *name);
+static ppd_coption_t   *ppd_get_coption(ppd_file_t *ppd, const char *name);
+static ppd_cparam_t    *ppd_get_cparam(ppd_coption_t *opt,
+                                       const char *param,
+                                       const char *text);
 static ppd_group_t     *ppd_get_group(ppd_file_t *ppd, const char *name,
-                                      const char *text);
+                                      const char *text, _cups_globals_t *cg,
+                                      cups_encoding_t encoding);
 static ppd_option_t    *ppd_get_option(ppd_group_t *group, const char *name);
-static int             ppd_read(FILE *fp, char *keyword, char *option,
-                                char *text, char **string);
-
-
-/*
- * '_ppd_attr_compare()' - Compare two attributes.
- */
-
-int                                    /* O - Result of comparison */
-_ppd_attr_compare(ppd_attr_t **a,      /* I - First attribute */
-                  ppd_attr_t **b)      /* I - Second attribute */
-{
-  int  ret;                            /* Result of comparison */
-
-
-  if ((ret = strcasecmp((*a)->name, (*b)->name)) != 0)
-    return (ret);
-  else if ((*a)->spec[0] && (*b)->spec[0])
-    return (strcasecmp((*a)->spec, (*b)->spec));
-  else
-    return (0);
-}
+static int             ppd_hash_option(ppd_option_t *option);
+static int             ppd_read(cups_file_t *fp, char *keyword, char *option,
+                                char *text, char **string, int ignoreblank,
+                                _cups_globals_t *cg);
 
 
 /*
@@ -154,36 +128,33 @@ _ppd_attr_compare(ppd_attr_t **a, /* I - First attribute */
 void
 ppdClose(ppd_file_t *ppd)              /* I - PPD file record */
 {
-  int          i;                      /* Looping var */
-  ppd_emul_t   *emul;                  /* Current emulation */
-  ppd_group_t  *group;                 /* Current group */
-  char         **font;                 /* Current font */
-  char         **filter;               /* Current filter */
-  ppd_attr_t   **attr;                 /* Current attribute */
+  int                  i;              /* Looping var */
+  ppd_emul_t           *emul;          /* Current emulation */
+  ppd_group_t          *group;         /* Current group */
+  char                 **font;         /* Current font */
+  char                 **filter;       /* Current filter */
+  ppd_attr_t           **attr;         /* Current attribute */
+  ppd_coption_t                *coption;       /* Current custom option */
+  ppd_cparam_t         *cparam;        /* Current custom parameter */
 
 
  /*
-  * Range check the PPD file record...
+  * Range check arguments...
   */
 
-  if (ppd == NULL)
+  if (!ppd)
     return;
 
  /*
   * Free all strings at the top level...
   */
 
+  ppd_free(ppd->lang_encoding);
+  ppd_free(ppd->nickname);
   ppd_free(ppd->patches);
   ppd_free(ppd->jcl_begin);
-  ppd_free(ppd->jcl_ps);
   ppd_free(ppd->jcl_end);
-  ppd_free(ppd->lang_encoding);
-  ppd_free(ppd->lang_version);
-  ppd_free(ppd->modelname);
-  ppd_free(ppd->ttrasterizer);
-  ppd_free(ppd->manufacturer);
-  ppd_free(ppd->nickname);
-  ppd_free(ppd->shortnickname);
+  ppd_free(ppd->jcl_ps);
 
  /*
   * Free any emulations...
@@ -212,6 +183,9 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
     ppd_free(ppd->groups);
   }
 
+  cupsArrayDelete(ppd->options);
+  cupsArrayDelete(ppd->marked);
+
  /*
   * Free any page sizes...
   */
@@ -233,7 +207,9 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
   if (ppd->num_filters > 0)
   {
     for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
+    {
       ppd_free(*filter);
+    }
 
     ppd_free(ppd->filters);
   }
@@ -245,7 +221,9 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
   if (ppd->num_fonts > 0)
   {
     for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++)
+    {
       ppd_free(*font);
+    }
 
     ppd_free(ppd->fonts);
   }
@@ -272,6 +250,42 @@ ppdClose(ppd_file_t *ppd)          /* I - PPD file record */
     ppd_free(ppd->attrs);
   }
 
+  cupsArrayDelete(ppd->sorted_attrs);
+
+ /*
+  * Free custom options...
+  */
+
+  for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
+       coption;
+       coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
+  {
+    for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+         cparam;
+        cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+    {
+      switch (cparam->type)
+      {
+        case PPD_CUSTOM_PASSCODE :
+        case PPD_CUSTOM_PASSWORD :
+        case PPD_CUSTOM_STRING :
+            ppd_free(cparam->current.custom_string);
+           break;
+
+       default :
+           break;
+      }
+
+      free(cparam);
+    }
+
+    cupsArrayDelete(coption->params);
+
+    free(coption);
+  }
+
+  cupsArrayDelete(ppd->coptions);
+
  /*
   * Free the whole record...
   */
@@ -282,6 +296,8 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
 
 /*
  * 'ppdErrorString()' - Returns the text assocated with a status.
+ *
+ * @since CUPS 1.1.19@
  */
 
 const char *                           /* O - Status string */
@@ -289,46 +305,79 @@ ppdErrorString(ppd_status_t status)       /* I - PPD status */
 {
   static const char * const messages[] =/* Status messages */
                {
-                 "OK",
-                 "Unable to open PPD file",
-                 "NULL PPD file pointer",
-                 "Memory allocation error",
-                 "Missing PPD-Adobe-4.x header",
-                 "Missing value string",
-                 "Internal error",
-                 "Bad OpenGroup",
-                 "OpenGroup without a CloseGroup first",
-                 "Bad OpenUI/JCLOpenUI",
-                 "OpenUI/JCLOpenUI without a CloseUI/JCLCLoseUI first",
-                 "Bad OrderDependency",
-                 "Bad UIConstraints",
-                 "Missing asterisk in column 1",
-                 "Line longer than the maximum allowed (255 characters)",
-                 "Illegal control character",
-                 "Illegal main keyword string",
-                 "Illegal option keyword string",
-                 "Illegal translation string"
+                 _("OK"),
+                 _("Unable to open PPD file"),
+                 _("NULL PPD file pointer"),
+                 _("Memory allocation error"),
+                 _("Missing PPD-Adobe-4.x header"),
+                 _("Missing value string"),
+                 _("Internal error"),
+                 _("Bad OpenGroup"),
+                 _("OpenGroup without a CloseGroup first"),
+                 _("Bad OpenUI/JCLOpenUI"),
+                 _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
+                 _("Bad OrderDependency"),
+                 _("Bad UIConstraints"),
+                 _("Missing asterisk in column 1"),
+                 _("Line longer than the maximum allowed (255 characters)"),
+                 _("Illegal control character"),
+                 _("Illegal main keyword string"),
+                 _("Illegal option keyword string"),
+                 _("Illegal translation string"),
+                 _("Illegal whitespace character"),
+                 _("Bad custom parameter")
                };
 
 
-  if (status < PPD_OK || status > PPD_ILLEGAL_TRANSLATION)
-    return ("Unknown");
+  if (status < PPD_OK || status > PPD_ILLEGAL_WHITESPACE)
+    return (_cupsLangString(cupsLangDefault(), _("Unknown")));
+  else
+    return (_cupsLangString(cupsLangDefault(), messages[status]));
+}
+
+
+/*
+ * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
+ *                       LanguageEncoding.
+ */
+
+cups_encoding_t                                /* O - CUPS encoding value */
+_ppdGetEncoding(const char *name)      /* I - LanguageEncoding string */
+{
+  if (!strcasecmp(name, "ISOLatin1"))
+    return (CUPS_ISO8859_1);
+  else if (!strcasecmp(name, "ISOLatin2"))
+    return (CUPS_ISO8859_2);
+  else if (!strcasecmp(name, "ISOLatin5"))
+    return (CUPS_ISO8859_5);
+  else if (!strcasecmp(name, "JIS83-RKSJ"))
+    return (CUPS_WINDOWS_932);
+  else if (!strcasecmp(name, "MacStandard"))
+    return (CUPS_MAC_ROMAN);
+  else if (!strcasecmp(name, "WindowsANSI"))
+    return (CUPS_WINDOWS_1252);
   else
-    return (messages[status]);
+    return (CUPS_UTF8);
 }
 
 
 /*
  * 'ppdLastError()' - Return the status from the last ppdOpen*().
+ *
+ * @since CUPS 1.1.19@
  */
 
 ppd_status_t                           /* O - Status code */
 ppdLastError(int *line)                        /* O - Line number */
 {
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Global data */
+
+
   if (line)
-    *line = ppd_line;
+    *line = cg->ppd_line;
 
-  return (ppd_status);
+  return (cg->ppd_status);
 }
 
 
@@ -339,14 +388,48 @@ ppdLastError(int *line)                   /* O - Line number */
 ppd_file_t *                           /* O - PPD file record */
 ppdOpen(FILE *fp)                      /* I - File to read from */
 {
-  char                 *oldlocale;     /* Old locale settings */
-  int                  i, j, k, m;     /* Looping vars */
+  ppd_file_t   *ppd;                   /* PPD file record */
+  cups_file_t  *cf;                    /* CUPS file */
+
+
+ /*
+  * Reopen the stdio file as a CUPS file...
+  */
+
+  if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
+    return (NULL);
+
+ /*
+  * Load the PPD file using the newer API...
+  */
+
+  ppd = ppdOpen2(cf);
+
+ /*
+  * Close the CUPS file and return the PPD...
+  */
+
+  cupsFileClose(cf);
+
+  return (ppd);
+}
+
+
+/*
+ * 'ppdOpen2()' - Read a PPD file into memory.
+ *
+ * @since CUPS 1.2@
+ */
+
+ppd_file_t *                           /* O - PPD file record */
+ppdOpen2(cups_file_t *fp)              /* I - File to read from */
+{
+  int                  i, j, k;        /* Looping vars */
   int                  count;          /* Temporary count */
   ppd_file_t           *ppd;           /* PPD file record */
   ppd_group_t          *group,         /* Current group */
                        *subgroup;      /* Current sub-group */
   ppd_option_t         *option;        /* Current option */
-  ppd_ext_option_t     *extopt;        /* Current extended option */
   ppd_choice_t         *choice;        /* Current choice */
   ppd_const_t          *constraint;    /* Current constraint */
   ppd_size_t           *size;          /* Current page size */
@@ -367,9 +450,23 @@ ppdOpen(FILE *fp)                  /* I - File to read from */
   ppd_profile_t                *profile;       /* Pointer to color profile */
   char                 **filter;       /* Pointer to filter */
   cups_lang_t          *language;      /* Default language */
+  struct lconv         *loc;           /* Locale data */
   int                  ui_keyword;     /* Is this line a UI keyword? */
+  cups_encoding_t      encoding;       /* Encoding of PPD file */
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Global data */
   static const char * const ui_keywords[] =
                        {
+#ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
+ /*
+  * Adobe defines some 41 keywords as "UI", meaning that they are
+  * user interface elements and that they should be treated as such
+  * even if the PPD creator doesn't use Open/CloseUI around them.
+  *
+  * Since this can cause previously invisible options to appear and
+  * confuse users, the default is to only treat the PageSize and
+  * PageRegion keywords this way.
+  */
                          /* Boolean keywords */
                          "BlackSubstitution",
                          "Booklet",
@@ -414,6 +511,10 @@ ppdOpen(FILE *fp)                  /* I - File to read from */
                          "StapleWhen",
                          "StapleX",
                          "StapleY"
+#else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
+                         "PageRegion",
+                         "PageSize"
+#endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
                        };
 
 
@@ -421,8 +522,8 @@ ppdOpen(FILE *fp)                   /* I - File to read from */
   * Default to "OK" status...
   */
 
-  ppd_status = PPD_OK;
-  ppd_line   = 0;
+  cg->ppd_status = PPD_OK;
+  cg->ppd_line   = 0;
 
  /*
   * Range check input...
@@ -430,7 +531,7 @@ ppdOpen(FILE *fp)                   /* I - File to read from */
 
   if (fp == NULL)
   {
-    ppd_status = PPD_NULL_FILE;
+    cg->ppd_status = PPD_NULL_FILE;
     return (NULL);
   }
 
@@ -438,18 +539,20 @@ ppdOpen(FILE *fp)                 /* I - File to read from */
   * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
   */
 
-  mask = ppd_read(fp, keyword, name, text, &string);
+  mask = ppd_read(fp, keyword, name, text, &string, 0, cg);
+
+  DEBUG_printf(("mask=%x, keyword=\"%s\"...\n", mask, keyword));
 
   if (mask == 0 ||
-      strcmp(keyword, "PPD-Adobe") != 0 ||
+      strcmp(keyword, "PPD-Adobe") ||
       string == NULL || string[0] != '4')
   {
    /*
     * Either this is not a PPD file, or it is not a 4.x PPD file.
     */
 
-    if (ppd_status != PPD_OK)
-      ppd_status = PPD_MISSING_PPDADOBE4;
+    if (cg->ppd_status == PPD_OK)
+      cg->ppd_status = PPD_MISSING_PPDADOBE4;
 
     ppd_free(string);
 
@@ -464,9 +567,9 @@ ppdOpen(FILE *fp)                   /* I - File to read from */
   * Allocate memory for the PPD file record...
   */
 
-  if ((ppd = calloc(sizeof(ppd_file_t), 1)) == NULL)
+  if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
   {
-    ppd_status = PPD_ALLOC_ERROR;
+    cg->ppd_status = PPD_ALLOC_ERROR;
 
     return (NULL);
   }
@@ -475,18 +578,15 @@ ppdOpen(FILE *fp)                 /* I - File to read from */
   ppd->color_device   = 0;
   ppd->colorspace     = PPD_CS_GRAY;
   ppd->landscape      = -90;
+  ppd->coptions       = cupsArrayNew((cups_array_func_t)ppd_compare_coptions,
+                                     NULL);
 
  /*
   * Get the default language for the user...
   */
 
   language = cupsLangDefault();
-
-#ifdef LC_NUMERIC
-  oldlocale = setlocale(LC_NUMERIC, "C");
-#else
-  oldlocale = setlocale(LC_ALL, "C");
-#endif /* LC_NUMERIC */
+  loc      = localeconv();
 
  /*
   * Read lines from the PPD file and add them to the file record...
@@ -497,8 +597,9 @@ ppdOpen(FILE *fp)                   /* I - File to read from */
   option     = NULL;
   choice     = NULL;
   ui_keyword = 0;
+  encoding   = CUPS_ISO8859_1;
 
-  while ((mask = ppd_read(fp, keyword, name, text, &string)) != 0)
+  while ((mask = ppd_read(fp, keyword, name, text, &string, 1, cg)) != 0)
   {
 #ifdef DEBUG
     printf("mask = %x, keyword = \"%s\"", mask, keyword);
@@ -530,19 +631,9 @@ ppdOpen(FILE *fp)                  /* I - File to read from */
       * Need a string value!
       */
 
-      ppdClose(ppd);
-
-      cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-      setlocale(LC_NUMERIC, oldlocale);
-#else
-      setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-      ppd_status = PPD_MISSING_VALUE;
+      cg->ppd_status = PPD_MISSING_VALUE;
 
-      return (NULL);
+      goto error;
     }
 
    /*
@@ -564,10 +655,12 @@ ppdOpen(FILE *fp)                 /* I - File to read from */
       ui_keyword = 0;
     }
 
-    if (option == NULL)
+    if (option == NULL &&
+        (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
+           (PPD_KEYWORD | PPD_OPTION | PPD_STRING))
     {
       for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++)
-        if (!strcmp(name, ui_keywords[i]))
+        if (!strcmp(keyword, ui_keywords[i]))
          break;
 
       if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])))
@@ -578,70 +671,34 @@ ppdOpen(FILE *fp)                 /* I - File to read from */
 
         ui_keyword = 1;
 
+        DEBUG_printf(("**** FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!\n",
+                     keyword));
+
         if (!group)
        {
-          if (strcmp(name, "Collate") && strcmp(name, "Duplex") &&
-              strcmp(name, "InputSlot") && strcmp(name, "ManualFeed") &&
-              strcmp(name, "MediaType") && strcmp(name, "MediaColor") &&
-              strcmp(name, "MediaWeight") && strcmp(name, "OutputBin") &&
-              strcmp(name, "OutputMode") && strcmp(name, "OutputOrder") &&
-             strcmp(name, "PageSize") && strcmp(name, "PageRegion"))
-           group = ppd_get_group(ppd, "Extra",
-                                 cupsLangString(language, CUPS_MSG_EXTRA));
-         else
-           group = ppd_get_group(ppd, "General",
-                                 cupsLangString(language, CUPS_MSG_GENERAL));
-
-          if (group == NULL)
-         {
-            ppdClose(ppd);
-
-           ppd_free(string);
-
-            cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-            setlocale(LC_NUMERIC, oldlocale);
-#else
-            setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-            ppd_status = PPD_ALLOC_ERROR;
-
-           return (NULL);
-         }
+          if ((group = ppd_get_group(ppd, "General", _("General"), cg,
+                                    encoding)) == NULL)
+           goto error;
 
           DEBUG_printf(("Adding to group %s...\n", group->text));
-          option = ppd_get_option(group, name);
+          option = ppd_get_option(group, keyword);
          group  = NULL;
        }
        else
-          option = ppd_get_option(group, name);
+          option = ppd_get_option(group, keyword);
 
        if (option == NULL)
        {
-          ppdClose(ppd);
+          cg->ppd_status = PPD_ALLOC_ERROR;
 
-         ppd_free(string);
-
-          cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-          setlocale(LC_NUMERIC, oldlocale);
-#else
-          setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-          ppd_status = PPD_ALLOC_ERROR;
-
-         return (NULL);
+          goto error;
        }
 
        /*
        * Now fill in the initial information for the option...
        */
 
-       if (!strncmp(name, "JCL", 3))
+       if (!strncmp(keyword, "JCL", 3))
           option->section = PPD_ORDER_JCL;
        else
           option->section = PPD_ORDER_ANY;
@@ -652,129 +709,102 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
           option->ui = PPD_UI_BOOLEAN;
        else
           option->ui = PPD_UI_PICKONE;
+
+        for (j = 0; j < ppd->num_attrs; j ++)
+         if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
+             !strcmp(ppd->attrs[j]->name + 7, keyword) &&
+             ppd->attrs[j]->value)
+         {
+           DEBUG_printf(("Setting Default%s to %s via attribute...\n",
+                         option->keyword, ppd->attrs[j]->value));
+           strlcpy(option->defchoice, ppd->attrs[j]->value,
+                   sizeof(option->defchoice));
+           break;
+         }
+
+        if (!strcmp(keyword, "PageSize"))
+         strlcpy(option->text, _("Media Size"), sizeof(option->text));
+       else if (!strcmp(keyword, "MediaType"))
+         strlcpy(option->text, _("Media Type"), sizeof(option->text));
+       else if (!strcmp(keyword, "InputSlot"))
+         strlcpy(option->text, _("Media Source"), sizeof(option->text));
+       else if (!strcmp(keyword, "ColorModel"))
+         strlcpy(option->text, _("Output Mode"), sizeof(option->text));
+       else if (!strcmp(keyword, "Resolution"))
+         strlcpy(option->text, _("Resolution"), sizeof(option->text));
+        else
+         strlcpy(option->text, keyword, sizeof(option->text));
       }
     }
 
-    if (strcmp(keyword, "LanguageLevel") == 0)
+    if (!strcmp(keyword, "LanguageLevel"))
       ppd->language_level = atoi(string);
-    else if (strcmp(keyword, "LanguageEncoding") == 0)
+    else if (!strcmp(keyword, "LanguageEncoding"))
     {
-      ppd_free(ppd->lang_encoding);
-      ppd->lang_encoding = string;
-      string = NULL;                   /* Don't free this string below */
+     /*
+      * Say all PPD files are UTF-8, since we convert to UTF-8...
+      */
+
+      ppd->lang_encoding = strdup("UTF-8");
+      encoding           = _ppdGetEncoding(string);
     }
-    else if (strcmp(keyword, "LanguageVersion") == 0)
-    {
-      ppd_free(ppd->lang_version);
+    else if (!strcmp(keyword, "LanguageVersion"))
       ppd->lang_version = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "Manufacturer") == 0)
-    {
-      ppd_free(ppd->manufacturer);
+    else if (!strcmp(keyword, "Manufacturer"))
       ppd->manufacturer = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "ModelName") == 0)
-    {
-      ppd_free(ppd->modelname);
+    else if (!strcmp(keyword, "ModelName"))
       ppd->modelname = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "Protocols") == 0)
-    {
-      ppd_free(ppd->protocols);
+    else if (!strcmp(keyword, "Protocols"))
       ppd->protocols = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "PCFileName") == 0)
-    {
-      ppd_free(ppd->pcfilename);
+    else if (!strcmp(keyword, "PCFileName"))
       ppd->pcfilename = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "NickName") == 0)
+    else if (!strcmp(keyword, "NickName"))
     {
-      ppd_free(ppd->nickname);
-      ppd->nickname = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "Product") == 0)
-    {
-     /*
-      * Add each Product keyword as an attribute...
-      */
-
-      ppd_add_attr(ppd, keyword, "", string);
+      if (encoding != CUPS_UTF8)
+      {
+        cups_utf8_t    utf8[256];      /* UTF-8 version of NickName */
 
-     /*
-      * Save the last one in the product element...
-      */
 
-      ppd->product = string;
-      string = NULL;                   /* Don't free this string below */
+        cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
+       ppd->nickname = strdup((char *)utf8);
+      }
+      else
+        ppd->nickname = strdup(string);
     }
-    else if (strcmp(keyword, "ShortNickName") == 0)
-    {
-      ppd_free(ppd->shortnickname);
+    else if (!strcmp(keyword, "Product"))
+      ppd->product = string;
+    else if (!strcmp(keyword, "ShortNickName"))
       ppd->shortnickname = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "TTRasterizer") == 0)
-    {
-      ppd_free(ppd->ttrasterizer);
+    else if (!strcmp(keyword, "TTRasterizer"))
       ppd->ttrasterizer = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "JCLBegin") == 0)
+    else if (!strcmp(keyword, "JCLBegin"))
     {
-      ppd_free(ppd->jcl_begin);
-      ppd_decode(string);              /* Decode quoted string */
-      ppd->jcl_begin = string;
-      string = NULL;                   /* Don't free this string below */
+      ppd->jcl_begin = strdup(string);
+      ppd_decode(ppd->jcl_begin);      /* Decode quoted string */
     }
-    else if (strcmp(keyword, "JCLEnd") == 0)
+    else if (!strcmp(keyword, "JCLEnd"))
     {
-      ppd_free(ppd->jcl_end);
-      ppd_decode(string);              /* Decode quoted string */
-      ppd->jcl_end = string;
-      string = NULL;                   /* Don't free this string below */
+      ppd->jcl_end = strdup(string);
+      ppd_decode(ppd->jcl_end);                /* Decode quoted string */
     }
-    else if (strcmp(keyword, "JCLToPSInterpreter") == 0)
+    else if (!strcmp(keyword, "JCLToPSInterpreter"))
     {
-      ppd_free(ppd->jcl_ps);
-      ppd_decode(string);              /* Decode quoted string */
-      ppd->jcl_ps = string;
-      string = NULL;                   /* Don't free this string below */
+      ppd->jcl_ps = strdup(string);
+      ppd_decode(ppd->jcl_ps);         /* Decode quoted string */
     }
-    else if (strcmp(keyword, "AccurateScreensSupport") == 0)
-      ppd->accurate_screens = strcmp(string, "True") == 0;
-    else if (strcmp(keyword, "ColorDevice") == 0)
-      ppd->color_device = strcmp(string, "True") == 0;
-    else if (strcmp(keyword, "ContoneOnly") == 0)
-      ppd->contone_only = strcmp(string, "True") == 0;
-    else if (strcmp(keyword, "DefaultColorSpace") == 0)
-    {
-      if (strcmp(string, "CMY") == 0)
-        ppd->colorspace = PPD_CS_CMY;
-      else if (strcmp(string, "CMYK") == 0)
-        ppd->colorspace = PPD_CS_CMYK;
-      else if (strcmp(string, "RGB") == 0)
-        ppd->colorspace = PPD_CS_RGB;
-      else if (strcmp(string, "RGBK") == 0)
-        ppd->colorspace = PPD_CS_RGBK;
-      else if (strcmp(string, "N") == 0)
-        ppd->colorspace = PPD_CS_N;
-      else
-        ppd->colorspace = PPD_CS_GRAY;
-    }
-    else if (strcmp(keyword, "cupsFlipDuplex") == 0)
-      ppd->flip_duplex = strcmp(string, "True") == 0;
-    else if (strcmp(keyword, "cupsManualCopies") == 0)
-      ppd->manual_copies = strcmp(string, "True") == 0;
-    else if (strcmp(keyword, "cupsModelNumber") == 0)
+    else if (!strcmp(keyword, "AccurateScreensSupport"))
+      ppd->accurate_screens = !strcmp(string, "True");
+    else if (!strcmp(keyword, "ColorDevice"))
+      ppd->color_device = !strcmp(string, "True");
+    else if (!strcmp(keyword, "ContoneOnly"))
+      ppd->contone_only = !strcmp(string, "True");
+    else if (!strcmp(keyword, "cupsFlipDuplex"))
+      ppd->flip_duplex = !strcmp(string, "True");
+    else if (!strcmp(keyword, "cupsManualCopies"))
+      ppd->manual_copies = !strcmp(string, "True");
+    else if (!strcmp(keyword, "cupsModelNumber"))
       ppd->model_number = atoi(string);
-    else if (strcmp(keyword, "cupsColorProfile") == 0)
+    else if (!strcmp(keyword, "cupsColorProfile"))
     {
       if (ppd->num_profiles == 0)
         profile = malloc(sizeof(ppd_profile_t));
@@ -789,15 +819,20 @@ ppdOpen(FILE *fp)                 /* I - File to read from */
       memset(profile, 0, sizeof(ppd_profile_t));
       strlcpy(profile->resolution, name, sizeof(profile->resolution));
       strlcpy(profile->media_type, text, sizeof(profile->media_type));
-      sscanf(string, "%f%f%f%f%f%f%f%f%f%f%f", &(profile->density),
-            &(profile->gamma),
-            profile->matrix[0] + 0, profile->matrix[0] + 1,
-            profile->matrix[0] + 2, profile->matrix[1] + 0,
-            profile->matrix[1] + 1, profile->matrix[1] + 2,
-            profile->matrix[2] + 0, profile->matrix[2] + 1,
-            profile->matrix[2] + 2);
+
+      profile->density      = (float)_cupsStrScand(string, &sptr, loc);
+      profile->gamma        = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc);
     }
-    else if (strcmp(keyword, "cupsFilter") == 0)
+    else if (!strcmp(keyword, "cupsFilter"))
     {
       if (ppd->num_filters == 0)
         filter = malloc(sizeof(char *));
@@ -808,19 +843,9 @@ ppdOpen(FILE *fp)                  /* I - File to read from */
       {
         ppd_free(filter);
 
-       ppdClose(ppd);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-       return (NULL);
+       goto error;
       }
 
       ppd->filters     = filter;
@@ -834,9 +859,9 @@ ppdOpen(FILE *fp)                   /* I - File to read from */
       *filter = string;
       string  = NULL;
     }
-    else if (strcmp(keyword, "Throughput") == 0)
+    else if (!strcmp(keyword, "Throughput"))
       ppd->throughput = atoi(string);
-    else if (strcmp(keyword, "Font") == 0)
+    else if (!strcmp(keyword, "Font"))
     {
      /*
       * Add this font to the list of available fonts...
@@ -850,133 +875,207 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
 
       if (tempfonts == NULL)
       {
-        ppd_free(string);
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-        ppdClose(ppd);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
-
-       return (NULL);
+       goto error;
       }
       
       ppd->fonts                 = tempfonts;
       ppd->fonts[ppd->num_fonts] = strdup(name);
       ppd->num_fonts ++;
     }
-    else if (strcmp(keyword, "VariablePaperSize") == 0 &&
-             strcmp(string, "True") == 0 &&
-            !ppd->variable_sizes)
+    else if (!strncmp(keyword, "ParamCustom", 11))
     {
-      ppd->variable_sizes = 1;
+      ppd_coption_t    *coption;       /* Custom option */
+      ppd_cparam_t     *cparam;        /* Custom parameter */
+      int              corder;         /* Order number */
+      char             ctype[33],      /* Data type */
+                       cminimum[65],   /* Minimum value */
+                       cmaximum[65];   /* Maximum value */
+
 
      /*
-      * Add a "Custom" page size entry...
+      * Get the custom option and parameter...
       */
 
-      ppd_add_size(ppd, "Custom");
+      if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL)
+      {
+        cg->ppd_status = PPD_ALLOC_ERROR;
+
+       goto error;
+      }
+
+      if ((cparam = ppd_get_cparam(coption, name, text)) == NULL)
+      {
+        cg->ppd_status = PPD_ALLOC_ERROR;
+
+       goto error;
+      }
 
      /*
-      * Add a "Custom" page size option...
+      * Get the parameter data...
       */
 
-      if ((option = ppdFindOption(ppd, "PageSize")) == NULL)
+      if (sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
+                 cmaximum) != 4)
+      {
+        cg->ppd_status = PPD_BAD_CUSTOM_PARAM;
+
+       goto error;
+      }
+
+      cparam->order = corder;
+
+      if (!strcmp(ctype, "curve"))
+      {
+        cparam->type = PPD_CUSTOM_CURVE;
+       cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc);
+      }
+      else if (!strcmp(ctype, "int"))
+      {
+        cparam->type = PPD_CUSTOM_INT;
+       cparam->minimum.custom_int = atoi(cminimum);
+       cparam->maximum.custom_int = atoi(cmaximum);
+      }
+      else if (!strcmp(ctype, "invcurve"))
+      {
+        cparam->type = PPD_CUSTOM_INVCURVE;
+       cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc);
+      }
+      else if (!strcmp(ctype, "passcode"))
+      {
+        cparam->type = PPD_CUSTOM_PASSCODE;
+       cparam->minimum.custom_passcode = atoi(cminimum);
+       cparam->maximum.custom_passcode = atoi(cmaximum);
+      }
+      else if (!strcmp(ctype, "password"))
+      {
+        cparam->type = PPD_CUSTOM_PASSWORD;
+       cparam->minimum.custom_password = atoi(cminimum);
+       cparam->maximum.custom_password = atoi(cmaximum);
+      }
+      else if (!strcmp(ctype, "points"))
+      {
+        cparam->type = PPD_CUSTOM_POINTS;
+       cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc);
+      }
+      else if (!strcmp(ctype, "real"))
+      {
+        cparam->type = PPD_CUSTOM_REAL;
+       cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc);
+      }
+      else if (!strcmp(ctype, "string"))
       {
-        ppd_group_t    *temp;
+        cparam->type = PPD_CUSTOM_STRING;
+       cparam->minimum.custom_string = atoi(cminimum);
+       cparam->maximum.custom_string = atoi(cmaximum);
+      }
+      else
+      {
+        cg->ppd_status = PPD_BAD_CUSTOM_PARAM;
+
+       goto error;
+      }
 
+     /*
+      * Now special-case for CustomPageSize...
+      */
 
-       if ((temp = ppd_get_group(ppd, "General",
-                                  cupsLangString(language,
-                                                 CUPS_MSG_GENERAL))) == NULL)
+      if (!strcmp(coption->keyword, "PageSize"))
+      {
+       if (!strcmp(name, "Width"))
        {
-          ppdClose(ppd);
+         ppd->custom_min[0] = cparam->minimum.custom_points;
+         ppd->custom_max[0] = cparam->maximum.custom_points;
+       }
+       else if (!strcmp(name, "Height"))
+       {
+         ppd->custom_min[1] = cparam->minimum.custom_points;
+         ppd->custom_max[1] = cparam->maximum.custom_points;
+       }
+      }
+    }
+    else if (!strcmp(keyword, "HWMargins"))
+    {
+      for (i = 0, sptr = string; i < 4; i ++)
+        ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
+    }
+    else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
+    {
+      DEBUG_puts("Processing Custom option...");
 
-         ppd_free(string);
+     /*
+      * Get the option and custom option...
+      */
 
-          cupsLangFree(language);
+      if ((option = ppdFindOption(ppd, keyword + 6)) == NULL)
+      {
+        int            groupidx = -1;  /* Index for current group */
+       ppd_group_t     *gtemp;         /* Temporary group */
 
-#ifdef LC_NUMERIC
-          setlocale(LC_NUMERIC, oldlocale);
-#else
-          setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
 
-          ppd_status = PPD_ALLOC_ERROR;
+        DEBUG_printf(("%s option not found for %s...\n", keyword + 6, keyword));
 
-         return (NULL);
-       }
+        if (group)
+          groupidx = group - ppd->groups; /* Save index for current group */
 
-       if ((option = ppd_get_option(temp, "PageSize")) == NULL)
+       if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg,
+                                  encoding)) == NULL)
        {
-          ppdClose(ppd);
+         DEBUG_puts("Unable to get general group!");
 
-         ppd_free(string);
+         goto error;
+       }
 
-          cupsLangFree(language);
+        if (group)
+          group = ppd->groups + groupidx; /* Restore group pointer */
 
-#ifdef LC_NUMERIC
-          setlocale(LC_NUMERIC, oldlocale);
-#else
-          setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
+       if ((option = ppd_get_option(gtemp, keyword + 6)) == NULL)
+       {
+         DEBUG_printf(("Unable to get %s option!\n", keyword + 6));
 
-          ppd_status = PPD_ALLOC_ERROR;
+          cg->ppd_status = PPD_ALLOC_ERROR;
 
-         return (NULL);
+         goto error;
        }
       }
 
-      if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+      if (!ppd_get_coption(ppd, keyword + 6))
       {
-        ppdClose(ppd);
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-       ppd_free(string);
+       goto error;
+      }
 
-        cupsLangFree(language);
+     /*
+      * Add the "custom" option...
+      */
 
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
+      if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+      {
+       DEBUG_puts("Unable to add Custom choice!");
 
-        ppd_status = PPD_ALLOC_ERROR;
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-       return (NULL);
+       goto error;
       }
 
-      strlcpy(choice->text, cupsLangString(language, CUPS_MSG_VARIABLE),
+      strlcpy(choice->text, text[0] ? text : _("Custom"),
               sizeof(choice->text));
-      option = NULL;
-    }
-    else if (strcmp(keyword, "MaxMediaWidth") == 0)
-      ppd->custom_max[0] = (float)atof(string);
-    else if (strcmp(keyword, "MaxMediaHeight") == 0)
-      ppd->custom_max[1] = (float)atof(string);
-    else if (strcmp(keyword, "ParamCustomPageSize") == 0)
-    {
-      if (strcmp(name, "Width") == 0)
-        sscanf(string, "%*s%*s%f%f", ppd->custom_min + 0,
-              ppd->custom_max + 0);
-      else if (strcmp(name, "Height") == 0)
-        sscanf(string, "%*s%*s%f%f", ppd->custom_min + 1,
-              ppd->custom_max + 1);
-    }
-    else if (strcmp(keyword, "HWMargins") == 0)
-      sscanf(string, "%f%f%f%f", ppd->custom_margins + 0,
-             ppd->custom_margins + 1, ppd->custom_margins + 2,
-             ppd->custom_margins + 3);
-    else if (strcmp(keyword, "CustomPageSize") == 0 &&
-             strcmp(name, "True") == 0)
-    {
-      if (!ppd->variable_sizes)
+
+      choice->code = string;
+      string       = NULL;             /* Don't add as an attribute below */
+      option       = NULL;
+
+     /*
+      * Now process custom page sizes specially...
+      */
+
+      if (!strcmp(keyword, "CustomPageSize"))
       {
        ppd->variable_sizes = 1;
 
@@ -986,140 +1085,50 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
 
        ppd_add_size(ppd, "Custom");
 
-       /*
-       * Add a "Custom" page size option...
-       */
-
-        if ((option = ppdFindOption(ppd, "PageSize")) == NULL)
+       if ((option = ppdFindOption(ppd, "PageRegion")) == NULL)
        {
-         ppd_group_t   *temp;
+         int           groupidx = -1;  /* Index to current group */
+         ppd_group_t   *gtemp;         /* Temporary group */
 
+          if (group)
+            groupidx = group - ppd->groups; /* Save index for current group */
 
-         if ((temp = ppd_get_group(ppd, "General",
-                                    cupsLangString(language,
-                                                   CUPS_MSG_GENERAL))) == NULL)
+         if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg,
+                                    encoding)) == NULL)
          {
            DEBUG_puts("Unable to get general group!");
 
-            ppdClose(ppd);
-
-           ppd_free(string);
-
-            cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-            setlocale(LC_NUMERIC, oldlocale);
-#else
-            setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-            ppd_status = PPD_ALLOC_ERROR;
-
-           return (NULL);
+           goto error;
          }
 
-         if ((option = ppd_get_option(temp, "PageSize")) == NULL)
-         {
-           DEBUG_puts("Unable to get PageSize option!");
-
-            ppdClose(ppd);
-
-           ppd_free(string);
-
-            cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-            setlocale(LC_NUMERIC, oldlocale);
-#else
-            setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-            ppd_status = PPD_ALLOC_ERROR;
+          if (group)
+            group = ppd->groups + groupidx; /* Restore group pointer */
 
-           return (NULL);
-         }
+         option = ppd_get_option(gtemp, "PageRegion");
         }
 
        if ((choice = ppd_add_choice(option, "Custom")) == NULL)
        {
          DEBUG_puts("Unable to add Custom choice!");
 
-          ppdClose(ppd);
-
-         ppd_free(string);
-
-          cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-          setlocale(LC_NUMERIC, oldlocale);
-#else
-          setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-          ppd_status = PPD_ALLOC_ERROR;
+         cg->ppd_status = PPD_ALLOC_ERROR;
 
-         return (NULL);
+         goto error;
        }
 
-       strlcpy(choice->text, cupsLangString(language, CUPS_MSG_VARIABLE),
-               sizeof(choice->text));
-       option = NULL;
+       strlcpy(choice->text, text[0] ? text : _("Custom"),
+               sizeof(choice->text));
+        option = NULL;
       }
-
-      if ((option = ppdFindOption(ppd, "PageSize")) == NULL)
-      {
-       DEBUG_puts("Unable to find PageSize option!");
-
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_INTERNAL_ERROR;
-
-       return (NULL);
-      }
-
-      if ((choice = ppdFindChoice(option, "Custom")) == NULL)
-      {
-       DEBUG_puts("Unable to find Custom choice!");
-
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_INTERNAL_ERROR;
-
-       return (NULL);
-      }
-
-      choice->code = string;
-      string = NULL;
-      option = NULL;
     }
-    else if (strcmp(keyword, "LandscapeOrientation") == 0)
+    else if (!strcmp(keyword, "LandscapeOrientation"))
     {
-      if (strcmp(string, "Minus90") == 0)
+      if (!strcmp(string, "Minus90"))
         ppd->landscape = -90;
-      else if (strcmp(string, "Plus90") == 0)
+      else if (!strcmp(string, "Plus90"))
         ppd->landscape = 90;
     }
-    else if (strcmp(keyword, "Emulators") == 0)
+    else if (!strcmp(keyword, "Emulators"))
     {
       for (count = 1, sptr = string; sptr != NULL;)
         if ((sptr = strchr(sptr, ' ')) != NULL)
@@ -1130,7 +1139,7 @@ ppdOpen(FILE *fp)                 /* I - File to read from */
        }
 
       ppd->num_emulations = count;
-      ppd->emulations     = calloc(sizeof(ppd_emul_t), count);
+      ppd->emulations     = calloc(count, sizeof(ppd_emul_t));
 
       for (i = 0, sptr = string; i < count; i ++)
       {
@@ -1146,56 +1155,41 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
          sptr ++;
       }
     }
-    else if (strncmp(keyword, "StartEmulator_", 14) == 0)
+    else if (!strncmp(keyword, "StartEmulator_", 14))
     {
       ppd_decode(string);
 
       for (i = 0; i < ppd->num_emulations; i ++)
-        if (strcmp(keyword + 14, ppd->emulations[i].name) == 0)
+        if (!strcmp(keyword + 14, ppd->emulations[i].name))
        {
          ppd->emulations[i].start = string;
          string = NULL;
        }
     }
-    else if (strncmp(keyword, "StopEmulator_", 13) == 0)
+    else if (!strncmp(keyword, "StopEmulator_", 13))
     {
       ppd_decode(string);
 
       for (i = 0; i < ppd->num_emulations; i ++)
-        if (strcmp(keyword + 13, ppd->emulations[i].name) == 0)
+        if (!strcmp(keyword + 13, ppd->emulations[i].name))
        {
          ppd->emulations[i].stop = string;
          string = NULL;
        }
     }
-    else if (strcmp(keyword, "JobPatchFile") == 0)
+    else if (!strcmp(keyword, "JobPatchFile"))
     {
       if (ppd->patches == NULL)
-      {
-        ppd->patches = string;
-       string       = NULL;
-      }
+        ppd->patches = strdup(string);
       else
       {
         temp = realloc(ppd->patches, strlen(ppd->patches) +
                                     strlen(string) + 1);
         if (temp == NULL)
        {
-          ppdClose(ppd);
-
-         ppd_free(string);
-
-          cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-          setlocale(LC_NUMERIC, oldlocale);
-#else
-          setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
+          cg->ppd_status = PPD_ALLOC_ERROR;
 
-          ppd_status = PPD_ALLOC_ERROR;
-
-         return (NULL);
+         goto error;
        }
 
         ppd->patches = temp;
@@ -1203,39 +1197,29 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
         strcpy(ppd->patches + strlen(ppd->patches), string);
       }
     }
-    else if (strcmp(keyword, "OpenUI") == 0)
+    else if (!strcmp(keyword, "OpenUI"))
     {
      /*
       * Don't allow nesting of options...
       */
 
-      if (option)
+      if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
       {
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_NESTED_OPEN_UI;
+        cg->ppd_status = PPD_NESTED_OPEN_UI;
 
-       return (NULL);
+       goto error;
       }
 
      /*
       * Add an option record to the current sub-group, group, or file...
       */
 
+      printf("name=\"%s\" (%d)\n", name, strlen(name));
+
       if (name[0] == '*')
-        strcpy(name, name + 1); /* Eliminate leading asterisk */
+        _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
 
-      for (i = strlen(name) - 1; i > 0 && isspace(name[i]); i --)
+      for (i = (int)strlen(name) - 1; i > 0 && isspace(name[i] & 255); i --)
         name[i] = '\0'; /* Eliminate trailing spaces */
 
       DEBUG_printf(("OpenUI of %s in group %s...\n", name,
@@ -1245,36 +1229,9 @@ ppdOpen(FILE *fp)                        /* I - File to read from */
         option = ppd_get_option(subgroup, name);
       else if (group == NULL)
       {
-        if (strcmp(name, "Collate") && strcmp(name, "Duplex") &&
-            strcmp(name, "InputSlot") && strcmp(name, "ManualFeed") &&
-            strcmp(name, "MediaType") && strcmp(name, "MediaColor") &&
-            strcmp(name, "MediaWeight") && strcmp(name, "OutputBin") &&
-            strcmp(name, "OutputMode") && strcmp(name, "OutputOrder") &&
-           strcmp(name, "PageSize") && strcmp(name, "PageRegion"))
-         group = ppd_get_group(ppd, "Extra",
-                               cupsLangString(language, CUPS_MSG_EXTRA));
-       else
-         group = ppd_get_group(ppd, "General",
-                               cupsLangString(language, CUPS_MSG_GENERAL));
-
-        if (group == NULL)
-       {
-          ppdClose(ppd);
-
-         ppd_free(string);
-
-          cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-          setlocale(LC_NUMERIC, oldlocale);
-#else
-          setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-          ppd_status = PPD_ALLOC_ERROR;
-
-         return (NULL);
-       }
+       if ((group = ppd_get_group(ppd, "General", _("General"), cg,
+                                  encoding)) == NULL)
+         goto error;
 
         DEBUG_printf(("Adding to group %s...\n", group->text));
         option = ppd_get_option(group, name);
@@ -1285,196 +1242,153 @@ ppdOpen(FILE *fp)                     /* I - File to read from */
 
       if (option == NULL)
       {
-        ppdClose(ppd);
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
-
-       return (NULL);
+       goto error;
       }
 
      /*
       * Now fill in the initial information for the option...
       */
 
-      if (string && strcmp(string, "PickMany") == 0)
+      if (string && !strcmp(string, "PickMany"))
         option->ui = PPD_UI_PICKMANY;
-      else if (string && strcmp(string, "Boolean") == 0)
+      else if (string && !strcmp(string, "Boolean"))
         option->ui = PPD_UI_BOOLEAN;
-      else if (string && strcmp(string, "PickOne") == 0)
+      else if (string && !strcmp(string, "PickOne"))
         option->ui = PPD_UI_PICKONE;
-      else
+      else if (cg->ppd_conform == PPD_CONFORM_STRICT)
       {
-        ppdClose(ppd);
+        cg->ppd_status = PPD_BAD_OPEN_UI;
 
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_BAD_OPEN_UI;
-
-       return (NULL);
+       goto error;
       }
+      else
+        option->ui = PPD_UI_PICKONE;
+
+      for (j = 0; j < ppd->num_attrs; j ++)
+       if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
+           !strcmp(ppd->attrs[j]->name + 7, name) &&
+           ppd->attrs[j]->value)
+       {
+         DEBUG_printf(("Setting Default%s to %s via attribute...\n",
+                       option->keyword, ppd->attrs[j]->value));
+         strlcpy(option->defchoice, ppd->attrs[j]->value,
+                 sizeof(option->defchoice));
+         break;
+       }
 
       if (text[0])
-      {
-        strlcpy(option->text, text, sizeof(option->text));
-       ppd_fix(option->text);
-      }
+        cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
+                          sizeof(option->text), encoding);
       else
       {
-        if (strcmp(name, "PageSize") == 0)
-         strlcpy(option->text, cupsLangString(language, CUPS_MSG_MEDIA_SIZE),
-                  sizeof(option->text));
-       else if (strcmp(name, "MediaType") == 0)
-         strlcpy(option->text, cupsLangString(language, CUPS_MSG_MEDIA_TYPE),
-                  sizeof(option->text));
-       else if (strcmp(name, "InputSlot") == 0)
-         strlcpy(option->text, cupsLangString(language, CUPS_MSG_MEDIA_SOURCE),
-                  sizeof(option->text));
-       else if (strcmp(name, "ColorModel") == 0)
-         strlcpy(option->text, cupsLangString(language, CUPS_MSG_OUTPUT_MODE),
-                  sizeof(option->text));
-       else if (strcmp(name, "Resolution") == 0)
-         strlcpy(option->text, cupsLangString(language, CUPS_MSG_RESOLUTION),
-                  sizeof(option->text));
+        if (!strcmp(name, "PageSize"))
+         strlcpy(option->text, _("Media Size"), sizeof(option->text));
+       else if (!strcmp(name, "MediaType"))
+         strlcpy(option->text, _("Media Type"), sizeof(option->text));
+       else if (!strcmp(name, "InputSlot"))
+         strlcpy(option->text, _("Media Source"), sizeof(option->text));
+       else if (!strcmp(name, "ColorModel"))
+         strlcpy(option->text, _("Output Mode"), sizeof(option->text));
+       else if (!strcmp(name, "Resolution"))
+         strlcpy(option->text, _("Resolution"), sizeof(option->text));
         else
          strlcpy(option->text, name, sizeof(option->text));
       }
 
       option->section = PPD_ORDER_ANY;
+
+      ppd_free(string);
+      string = NULL;
     }
-    else if (strcmp(keyword, "JCLOpenUI") == 0)
+    else if (!strcmp(keyword, "JCLOpenUI"))
     {
      /*
       * Don't allow nesting of options...
       */
 
-      if (option)
+      if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
       {
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
+        cg->ppd_status = PPD_NESTED_OPEN_UI;
 
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_NESTED_OPEN_UI;
-
-       return (NULL);
+       goto error;
       }
 
      /*
       * Find the JCL group, and add if needed...
       */
 
-      group = ppd_get_group(ppd, "JCL", "JCL");
+      group = ppd_get_group(ppd, "JCL", _("JCL"), cg, encoding);
 
       if (group == NULL)
-      {
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
-
-       return (NULL);
-      }
+       goto error;
 
      /*
       * Add an option record to the current JCLs...
       */
 
       if (name[0] == '*')
-        strcpy(name, name + 1);
+        _cups_strcpy(name, name + 1);
 
       option = ppd_get_option(group, name);
 
       if (option == NULL)
       {
-        ppdClose(ppd);
-
-       ppd_free(string);
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
-
-       return (NULL);
+       goto error;
       }
 
      /*
       * Now fill in the initial information for the option...
       */
 
-      if (string && strcmp(string, "PickMany") == 0)
+      if (string && !strcmp(string, "PickMany"))
         option->ui = PPD_UI_PICKMANY;
-      else if (string && strcmp(string, "Boolean") == 0)
+      else if (string && !strcmp(string, "Boolean"))
         option->ui = PPD_UI_BOOLEAN;
-      else if (string && strcmp(string, "PickOne") == 0)
+      else if (string && !strcmp(string, "PickOne"))
         option->ui = PPD_UI_PICKONE;
       else
       {
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
+        cg->ppd_status = PPD_BAD_OPEN_UI;
 
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_BAD_OPEN_UI;
-
-       return (NULL);
+       goto error;
       }
 
-      strlcpy(option->text, text, sizeof(option->text));
+      for (j = 0; j < ppd->num_attrs; j ++)
+       if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
+           !strcmp(ppd->attrs[j]->name + 7, name) &&
+           ppd->attrs[j]->value)
+       {
+         DEBUG_printf(("Setting Default%s to %s via attribute...\n",
+                       option->keyword, ppd->attrs[j]->value));
+         strlcpy(option->defchoice, ppd->attrs[j]->value,
+                 sizeof(option->defchoice));
+         break;
+       }
+
+      if (text[0])
+        cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
+                          sizeof(option->text), encoding);
+      else
+        strlcpy(option->text, name, sizeof(option->text));
 
       option->section = PPD_ORDER_JCL;
       group = NULL;
+
+      ppd_free(string);
+      string = NULL;
     }
-    else if (strcmp(keyword, "CloseUI") == 0 ||
-             strcmp(keyword, "JCLCloseUI") == 0)
+    else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
+    {
       option = NULL;
-    else if (strcmp(keyword, "OpenGroup") == 0)
+
+      ppd_free(string);
+      string = NULL;
+    }
+    else if (!strcmp(keyword, "OpenGroup"))
     {
      /*
       * Open a new group...
@@ -1482,40 +1396,16 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
 
       if (group != NULL)
       {
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
+        cg->ppd_status = PPD_NESTED_OPEN_GROUP;
 
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_NESTED_OPEN_GROUP;
-
-       return (NULL);
+       goto error;
       }
 
       if (!string)
       {
-        ppdClose(ppd);
-
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_BAD_OPEN_GROUP;
+        cg->ppd_status = PPD_BAD_OPEN_GROUP;
 
-       return (NULL);
+       goto error;
       }
 
      /*
@@ -1532,73 +1422,73 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
       */
 
       ppd_decode(sptr);
-      ppd_fix(sptr);
 
      /*
       * Find/add the group...
       */
 
-      group = ppd_get_group(ppd, string, sptr);
-    }
-    else if (strcmp(keyword, "CloseGroup") == 0)
-      group = NULL;
-    else if (strcmp(keyword, "OrderDependency") == 0 ||
-             strcmp(keyword, "NonUIOrderDependency") == 0)
-    {
-      if (sscanf(string, "%f%40s%40s", &order, name, keyword) != 3)
-      {
-        ppdClose(ppd);
+      group = ppd_get_group(ppd, string, sptr, cg, encoding);
 
-       ppd_free(string);
+      if (group == NULL)
+       goto error;
 
-        cupsLangFree(language);
+      ppd_free(string);
+      string = NULL;
+    }
+    else if (!strcmp(keyword, "CloseGroup"))
+    {
+      group = NULL;
 
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
+      ppd_free(string);
+      string = NULL;
+    }
+    else if (!strcmp(keyword, "OrderDependency") ||
+             !strcmp(keyword, "NonUIOrderDependency"))
+    {
+      order = (float)_cupsStrScand(string, &sptr, loc);
 
-        ppd_status = PPD_BAD_ORDER_DEPENDENCY;
+      if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
+      {
+        cg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
 
-       return (NULL);
+       goto error;
       }
 
       if (keyword[0] == '*')
-        strcpy(keyword, keyword + 1);
+        _cups_strcpy(keyword, keyword + 1);
 
-      if (strcmp(name, "ExitServer") == 0)
+      if (!strcmp(name, "ExitServer"))
         section = PPD_ORDER_EXIT;
-      else if (strcmp(name, "Prolog") == 0)
+      else if (!strcmp(name, "Prolog"))
         section = PPD_ORDER_PROLOG;
-      else if (strcmp(name, "DocumentSetup") == 0)
+      else if (!strcmp(name, "DocumentSetup"))
         section = PPD_ORDER_DOCUMENT;
-      else if (strcmp(name, "PageSetup") == 0)
+      else if (!strcmp(name, "PageSetup"))
         section = PPD_ORDER_PAGE;
-      else if (strcmp(name, "JCLSetup") == 0)
+      else if (!strcmp(name, "JCLSetup"))
         section = PPD_ORDER_JCL;
       else
         section = PPD_ORDER_ANY;
 
       if (option == NULL)
       {
-        ppd_group_t    *temp;
+        ppd_group_t    *gtemp;
 
 
        /*
         * Only valid for Non-UI options...
        */
 
-        for (i = ppd->num_groups, temp = ppd->groups; i > 0; i --, temp ++)
-          if (temp->text[0] == '\0')
+        for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
+          if (gtemp->text[0] == '\0')
            break;
 
         if (i > 0)
-          for (i = 0; i < temp->num_options; i ++)
-           if (strcmp(keyword, temp->options[i].keyword) == 0)
+          for (i = 0; i < gtemp->num_options; i ++)
+           if (!strcmp(keyword, gtemp->options[i].keyword))
            {
-             temp->options[i].section = section;
-             temp->options[i].order   = order;
+             gtemp->options[i].section = section;
+             gtemp->options[i].order   = order;
              break;
            }
       }
@@ -1607,99 +1497,87 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
         option->section = section;
        option->order   = order;
       }
+
+      ppd_free(string);
+      string = NULL;
     }
-    else if (strncmp(keyword, "Default", 7) == 0)
+    else if (!strncmp(keyword, "Default", 7))
     {
       if (string == NULL)
         continue;
 
+     /*
+      * Drop UI text, if any, from value...
+      */
+
       if (strchr(string, '/') != NULL)
         *strchr(string, '/') = '\0';
 
-      if (option == NULL)
-      {
-        ppd_group_t    *temp;
-
+     /*
+      * Assign the default value as appropriate...
+      */
 
+      if (!strcmp(keyword, "DefaultColorSpace"))
+      {
        /*
-        * Only valid for Non-UI options...
+        * Set default colorspace...
        */
 
-        for (i = ppd->num_groups, temp = ppd->groups; i > 0; i --, temp ++)
-          if (temp->text[0] == '\0')
-           break;
-
-        if (i > 0)
-       {
-          for (i = 0; i < temp->num_options; i ++)
-           if (strcmp(keyword + 7, temp->options[i].keyword) == 0)
-           {
-             strlcpy(temp->options[i].defchoice, string,
-                      sizeof(temp->options[i].defchoice));
-             break;
-           }
-
-          if (i >= temp->num_options)
-         {
-          /*
-           * Option not found; add this as an attribute...
-           */
-
-            ppd_add_attr(ppd, keyword, "", string);
-
-            string = NULL;             /* Don't free this string below */
-         }
-       }
+       if (!strcmp(string, "CMY"))
+          ppd->colorspace = PPD_CS_CMY;
+       else if (!strcmp(string, "CMYK"))
+          ppd->colorspace = PPD_CS_CMYK;
+       else if (!strcmp(string, "RGB"))
+          ppd->colorspace = PPD_CS_RGB;
+       else if (!strcmp(string, "RGBK"))
+          ppd->colorspace = PPD_CS_RGBK;
+       else if (!strcmp(string, "N"))
+          ppd->colorspace = PPD_CS_N;
        else
-       {
-        /*
-         * Default group not found; add this as an attribute...
-         */
+          ppd->colorspace = PPD_CS_GRAY;
+      }
+      else if (option && !strcmp(keyword + 7, option->keyword))
+      {
+       /*
+        * Set the default as part of the current option...
+       */
 
-          ppd_add_attr(ppd, keyword, "", string);
+        DEBUG_printf(("Setting %s to %s...\n", keyword, string));
 
-          string = NULL;               /* Don't free this string below */
-       }
-      }
-      else if (strcmp(keyword + 7, option->keyword) == 0)
         strlcpy(option->defchoice, string, sizeof(option->defchoice));
+
+        DEBUG_printf(("%s is now %s...\n", keyword, option->defchoice));
+      }
       else
       {
        /*
-       * Default doesn't match this option; add as an attribute...
+        * Lookup option and set if it has been defined...
        */
 
-        ppd_add_attr(ppd, keyword, "", string);
+        ppd_option_t   *toption;       /* Temporary option */
+
 
-        string = NULL;                 /* Don't free this string below */
+        if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
+       {
+         DEBUG_printf(("Setting %s to %s...\n", keyword, string));
+         strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
+       }
       }
     }
-    else if (strcmp(keyword, "UIConstraints") == 0 ||
-             strcmp(keyword, "NonUIConstraints") == 0)
+    else if (!strcmp(keyword, "UIConstraints") ||
+             !strcmp(keyword, "NonUIConstraints"))
     {
       if (ppd->num_consts == 0)
-       constraint = calloc(sizeof(ppd_const_t), 1);
+       constraint = calloc(2, sizeof(ppd_const_t));
       else
        constraint = realloc(ppd->consts,
-                            (ppd->num_consts + 1) * sizeof(ppd_const_t));
+                            (ppd->num_consts + 2) * sizeof(ppd_const_t));
 
       if (constraint == NULL)
       {
-        ppdClose(ppd);
-
-       ppd_free(string);
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
-
-       return (NULL);
+       goto error;
       }
 
       ppd->consts = constraint;
@@ -1712,24 +1590,42 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
       {
         case 0 : /* Error */
        case 1 : /* Error */
-           ppdClose(ppd);
-           ppd_free(string);
-           ppd_status = PPD_BAD_UI_CONSTRAINTS;
-           return (NULL);
+           cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+           goto error;
 
        case 2 : /* Two options... */
+          /*
+           * Check for broken constraints like "* Option"...
+           */
+
+           if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+               (!strcmp(constraint->option1, "*") ||
+                !strcmp(constraint->choice1, "*")))
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
+
           /*
            * The following strcpy's are safe, as optionN and
            * choiceN are all the same size (size defined by PPD spec...)
            */
 
            if (constraint->option1[0] == '*')
-             strcpy(constraint->option1, constraint->option1 + 1);
+             _cups_strcpy(constraint->option1, constraint->option1 + 1);
+           else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
 
            if (constraint->choice1[0] == '*')
-             strcpy(constraint->option2, constraint->choice1 + 1);
-           else
-             strcpy(constraint->option2, constraint->choice1);
+             _cups_strcpy(constraint->option2, constraint->choice1 + 1);
+           else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
 
             constraint->choice1[0] = '\0';
             constraint->choice2[0] = '\0';
@@ -1737,38 +1633,159 @@ ppdOpen(FILE *fp)                      /* I - File to read from */
            
        case 3 : /* Two options, one choice... */
           /*
-           * The following strcpy's are safe, as optionN and
+           * Check for broken constraints like "* Option"...
+           */
+
+           if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+               (!strcmp(constraint->option1, "*") ||
+                !strcmp(constraint->choice1, "*") ||
+                !strcmp(constraint->option2, "*")))
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
+
+          /*
+           * The following _cups_strcpy's are safe, as optionN and
            * choiceN are all the same size (size defined by PPD spec...)
            */
 
            if (constraint->option1[0] == '*')
-             strcpy(constraint->option1, constraint->option1 + 1);
+             _cups_strcpy(constraint->option1, constraint->option1 + 1);
+           else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
 
            if (constraint->choice1[0] == '*')
            {
-             strcpy(constraint->choice2, constraint->option2);
-             strcpy(constraint->option2, constraint->choice1 + 1);
+             if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+                 constraint->option2[0] == '*')
+             {
+               cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+               goto error;
+             }
+
+             _cups_strcpy(constraint->choice2, constraint->option2);
+             _cups_strcpy(constraint->option2, constraint->choice1 + 1);
               constraint->choice1[0] = '\0';
            }
            else
            {
              if (constraint->option2[0] == '*')
-               strcpy(constraint->option2, constraint->option2 + 1);
+               _cups_strcpy(constraint->option2, constraint->option2 + 1);
+             else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+             {
+               cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+               goto error;
+             }
 
               constraint->choice2[0] = '\0';
            }
            break;
            
        case 4 : /* Two options, two choices... */
+          /*
+           * Check for broken constraints like "* Option"...
+           */
+
+           if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+               (!strcmp(constraint->option1, "*") ||
+                !strcmp(constraint->choice1, "*") ||
+                !strcmp(constraint->option2, "*") ||
+                !strcmp(constraint->choice2, "*")))
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
+
            if (constraint->option1[0] == '*')
-             strcpy(constraint->option1, constraint->option1 + 1);
+             _cups_strcpy(constraint->option1, constraint->option1 + 1);
+           else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
+
+            if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+               constraint->choice1[0] == '*')
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
 
            if (constraint->option2[0] == '*')
-             strcpy(constraint->option2, constraint->option2 + 1);
+             _cups_strcpy(constraint->option2, constraint->option2 + 1);
+           else if (cg->ppd_conform == PPD_CONFORM_STRICT)
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
+
+            if (cg->ppd_conform == PPD_CONFORM_STRICT &&
+               constraint->choice2[0] == '*')
+           {
+             cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+             goto error;
+           }
            break;
       }
+
+     /*
+      * For CustomPageSize and InputSlot/ManualFeed, create a duplicate
+      * constraint for PageRegion...
+      */
+
+      if (!strcasecmp(constraint->option1, "CustomPageSize") &&
+          (!strcasecmp(constraint->option2, "InputSlot") ||
+          !strcasecmp(constraint->option2, "ManualFeed")))
+      {
+        ppd->num_consts ++;
+
+        strcpy(constraint[1].option1, "PageRegion");
+       strcpy(constraint[1].choice1, "Custom");
+       strcpy(constraint[1].option2, constraint->option2);
+       strcpy(constraint[1].choice2, constraint->choice2);
+      }
+      else if (!strcasecmp(constraint->option2, "CustomPageSize") &&
+               (!strcasecmp(constraint->option1, "InputSlot") ||
+               !strcasecmp(constraint->option1, "ManualFeed")))
+      {
+        ppd->num_consts ++;
+
+       strcpy(constraint[1].option1, constraint->option1);
+       strcpy(constraint[1].choice1, constraint->choice1);
+        strcpy(constraint[1].option2, "PageRegion");
+       strcpy(constraint[1].choice2, "Custom");
+      }
+
+     /*
+      * Handle CustomFoo option constraints...
+      */
+
+      if (!strncasecmp(constraint->option1, "Custom", 6) &&
+          !strcasecmp(constraint->choice1, "True"))
+      {
+        _cups_strcpy(constraint->option1, constraint->option1 + 6);
+       strcpy(constraint->choice1, "Custom");
+      }
+
+      if (!strncasecmp(constraint->option2, "Custom", 6) &&
+          !strcasecmp(constraint->choice2, "True"))
+      {
+        _cups_strcpy(constraint->option2, constraint->option2 + 6);
+       strcpy(constraint->choice2, "Custom");
+      }
+
+     /*
+      * Don't add this one as an attribute...
+      */
+
+      ppd_free(string);
+      string = NULL;
     }
-    else if (strcmp(keyword, "PaperDimension") == 0)
+    else if (!strcmp(keyword, "PaperDimension"))
     {
       if ((size = ppdPageSize(ppd, name)) == NULL)
        size = ppd_add_size(ppd, name);
@@ -1779,26 +1796,18 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
         * Unable to add or find size!
        */
 
-        ppdClose(ppd);
-
-       ppd_free(string);
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
-
-       return (NULL);
+       goto error;
       }
 
-      sscanf(string, "%f%f", &(size->width), &(size->length));
+      size->width  = (float)_cupsStrScand(string, &sptr, loc);
+      size->length = (float)_cupsStrScand(sptr, NULL, loc);
+
+      ppd_free(string);
+      string = NULL;
     }
-    else if (strcmp(keyword, "ImageableArea") == 0)
+    else if (!strcmp(keyword, "ImageableArea"))
     {
       if ((size = ppdPageSize(ppd, name)) == NULL)
        size = ppd_add_size(ppd, name);
@@ -1809,34 +1818,27 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
         * Unable to add or find size!
        */
 
-        ppdClose(ppd);
+        cg->ppd_status = PPD_ALLOC_ERROR;
 
-       ppd_free(string);
-
-        cupsLangFree(language);
-
-#ifdef LC_NUMERIC
-        setlocale(LC_NUMERIC, oldlocale);
-#else
-        setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
-        ppd_status = PPD_ALLOC_ERROR;
-
-       return (NULL);
+       goto error;
       }
 
-      sscanf(string, "%f%f%f%f", &(size->left), &(size->bottom),
-            &(size->right), &(size->top));
+      size->left   = (float)_cupsStrScand(string, &sptr, loc);
+      size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
+      size->right  = (float)_cupsStrScand(sptr, &sptr, loc);
+      size->top    = (float)_cupsStrScand(sptr, NULL, loc);
+
+      ppd_free(string);
+      string = NULL;
     }
     else if (option != NULL &&
              (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
                 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
-            strcmp(keyword, option->keyword) == 0)
+            !strcmp(keyword, option->keyword))
     {
       DEBUG_printf(("group = %p, subgroup = %p\n", group, subgroup));
 
-      if (strcmp(keyword, "PageSize") == 0)
+      if (!strcmp(keyword, "PageSize"))
       {
        /*
         * Add a page size...
@@ -1852,15 +1854,13 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
 
       choice = ppd_add_choice(option, name);
 
-      if (mask & PPD_TEXT)
-      {
-        strlcpy(choice->text, text, sizeof(choice->text));
-        ppd_fix(choice->text);
-      }
-      else if (strcmp(name, "True") == 0)
-        strcpy(choice->text, "Yes");
-      else if (strcmp(name, "False") == 0)
-        strcpy(choice->text, "No");
+      if (text[0])
+        cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
+                          sizeof(choice->text), encoding);
+      else if (!strcmp(name, "True"))
+        strcpy(choice->text, _("Yes"));
+      else if (!strcmp(name, "False"))
+        strcpy(choice->text, _("No"));
       else
         strlcpy(choice->text, name, sizeof(choice->text));
 
@@ -1868,232 +1868,18 @@ ppdOpen(FILE *fp)                      /* I - File to read from */
         ppd_decode(string);            /* Decode quoted string */
 
       choice->code = string;
-      string = NULL;                   /* Don't free this string below */
-    }
-    else if (strcmp(keyword, "cupsUIType") == 0 &&
-             (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING) &&
-            option != NULL)
-    {
-     /*
-      * Define an extended option value type...
-      */
-
-      extopt = ppd_get_extopt(ppd, name);
-
-      if (strcmp(string, "Text") == 0)
-        option->ui = PPD_UI_CUPS_TEXT;
-      else if (strcmp(string, "Integer") == 0)
-      {
-        option->ui             = PPD_UI_CUPS_INTEGER;
-       extopt->defval.integer = 0;
-       extopt->minval.integer = 0;
-       extopt->maxval.integer = 100;
-      }
-      else if (strcmp(string, "Real") == 0)
-      {
-        option->ui          = PPD_UI_CUPS_REAL;
-       extopt->defval.real = 0.0;
-       extopt->minval.real = 0.0;
-       extopt->maxval.real = 1.0;
-      }
-      else if (strcmp(string, "Gamma") == 0)
-      {
-        option->ui           = PPD_UI_CUPS_GAMMA;
-       extopt->defval.gamma = 1.0;
-       extopt->minval.gamma = 1.0;
-       extopt->maxval.gamma = 10.0;
-      }
-      else if (strcmp(string, "Curve") == 0)
-      {
-        option->ui                 = PPD_UI_CUPS_CURVE;
-       extopt->defval.curve.start = 0.0;
-       extopt->defval.curve.end   = 0.0;
-       extopt->defval.curve.gamma = 1.0;
-       extopt->minval.curve.start = 0.0;
-       extopt->minval.curve.end   = 0.0;
-       extopt->minval.curve.gamma = 1.0;
-       extopt->maxval.curve.start = 1.0;
-       extopt->maxval.curve.end   = 1.0;
-       extopt->maxval.curve.gamma = 10.0;
-      }
-      else if (strcmp(string, "IntegerArray") == 0)
-      {
-        option->ui                                = PPD_UI_CUPS_INTEGER_ARRAY;
-       extopt->defval.integer_array.num_elements = 2;
-       extopt->minval.integer_array.num_elements = 2;
-       extopt->maxval.integer_array.num_elements = 16;
-      }
-      else if (strcmp(string, "RealArray") == 0)
-      {
-        option->ui                             = PPD_UI_CUPS_REAL_ARRAY;
-       extopt->defval.real_array.num_elements = 2;
-       extopt->minval.real_array.num_elements = 2;
-       extopt->maxval.real_array.num_elements = 16;
-      }
+      string       = NULL;             /* Don't add as an attribute below */
     }
-    else if (strcmp(keyword, "cupsUIDefault") == 0 &&
-             (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING) &&
-            option != NULL)
-    {
-     /*
-      * Define an extended option minimum value...
-      */
-
-      extopt = ppd_get_extopt(ppd, name);
 
-      switch (option->ui)
-      {
-        case PPD_UI_CUPS_INTEGER :
-           sscanf(string, "%d", &(extopt->defval.integer));
-           break;
-
-        case PPD_UI_CUPS_REAL :
-           sscanf(string, "%f", &(extopt->defval.real));
-           break;
-
-        case PPD_UI_CUPS_GAMMA :
-           sscanf(string, "%f", &(extopt->defval.gamma));
-           break;
-
-        case PPD_UI_CUPS_CURVE :
-           sscanf(string, "%f%f%f", &(extopt->defval.curve.start),
-                  &(extopt->defval.curve.end),
-                  &(extopt->defval.curve.gamma));
-           break;
-
-        case PPD_UI_CUPS_INTEGER_ARRAY :
-           extopt->defval.integer_array.elements = calloc(1, sizeof(int));
-           sscanf(string, "%d%d", &(extopt->defval.integer_array.num_elements),
-                  extopt->defval.integer_array.elements);
-           break;
-
-        case PPD_UI_CUPS_REAL_ARRAY :
-           extopt->defval.real_array.elements = calloc(1, sizeof(float));
-           sscanf(string, "%d%f", &(extopt->defval.real_array.num_elements),
-                  extopt->defval.real_array.elements);
-           break;
-
-       default :
-            break;
-      }
-    }
-    else if (strcmp(keyword, "cupsUIMinimum") == 0 &&
-             (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING) &&
-            option != NULL)
-    {
-     /*
-      * Define an extended option minimum value...
-      */
-
-      extopt = ppd_get_extopt(ppd, name);
-
-      switch (option->ui)
-      {
-        case PPD_UI_CUPS_INTEGER :
-           sscanf(string, "%d", &(extopt->minval.integer));
-           break;
-
-        case PPD_UI_CUPS_REAL :
-           sscanf(string, "%f", &(extopt->minval.real));
-           break;
-
-        case PPD_UI_CUPS_GAMMA :
-           sscanf(string, "%f", &(extopt->minval.gamma));
-           break;
-
-        case PPD_UI_CUPS_CURVE :
-           sscanf(string, "%f%f%f", &(extopt->minval.curve.start),
-                  &(extopt->minval.curve.end),
-                  &(extopt->minval.curve.gamma));
-           break;
-
-        case PPD_UI_CUPS_INTEGER_ARRAY :
-           extopt->minval.integer_array.elements = calloc(1, sizeof(int));
-           sscanf(string, "%d%d", &(extopt->minval.integer_array.num_elements),
-                  extopt->minval.integer_array.elements);
-           break;
-
-        case PPD_UI_CUPS_REAL_ARRAY :
-           extopt->minval.real_array.elements = calloc(1, sizeof(float));
-           sscanf(string, "%d%f", &(extopt->minval.real_array.num_elements),
-                  extopt->minval.real_array.elements);
-           break;
-
-       default :
-            break;
-      }
-    }
-    else if (strcmp(keyword, "cupsUIMaximum") == 0 &&
-             (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING) &&
-            option != NULL)
-    {
-     /*
-      * Define an extended option maximum value...
-      */
-
-      extopt = ppd_get_extopt(ppd, name);
-
-      switch (option->ui)
-      {
-        case PPD_UI_CUPS_INTEGER :
-           sscanf(string, "%d", &(extopt->maxval.integer));
-           break;
-
-        case PPD_UI_CUPS_REAL :
-           sscanf(string, "%f", &(extopt->maxval.real));
-           break;
-
-        case PPD_UI_CUPS_GAMMA :
-           sscanf(string, "%f", &(extopt->maxval.gamma));
-           break;
-
-        case PPD_UI_CUPS_CURVE :
-           sscanf(string, "%f%f%f", &(extopt->maxval.curve.start),
-                  &(extopt->maxval.curve.end),
-                  &(extopt->maxval.curve.gamma));
-           break;
-
-        case PPD_UI_CUPS_INTEGER_ARRAY :
-           extopt->maxval.integer_array.elements = calloc(1, sizeof(int));
-           sscanf(string, "%d%d", &(extopt->maxval.integer_array.num_elements),
-                  extopt->maxval.integer_array.elements);
-           break;
-
-        case PPD_UI_CUPS_REAL_ARRAY :
-           extopt->maxval.real_array.elements = calloc(1, sizeof(float));
-           sscanf(string, "%d%f", &(extopt->maxval.real_array.num_elements),
-                  extopt->maxval.real_array.elements);
-           break;
-
-       default :
-            break;
-      }
-    }
-    else if (strcmp(keyword, "cupsUICommand") == 0 &&
-             (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING) &&
-            option != NULL)
-    {
-     /*
-      * Define an extended option command...
-      */
-
-      extopt = ppd_get_extopt(ppd, name);
-
-      extopt->command = string;
-      string = NULL;
-    }
-    else if (strcmp(keyword, "OpenSubGroup") != 0 &&
-             strcmp(keyword, "CloseSubGroup") != 0)
-    {
-      char     spec[PPD_MAX_NAME + PPD_MAX_TEXT];
-
-      snprintf(spec, sizeof(spec), "%s/%s", name, text);
-      ppd_add_attr(ppd, keyword, spec, string);
-
-      string = NULL;                   /* Don't free this string below */
-    }
+   /*
+    * Add remaining lines with keywords and string values as attributes...
+    */
 
-    ppd_free(string);
+    if (string &&
+        (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
+      ppd_add_attr(ppd, keyword, name, text, string);
+    else
+      ppd_free(string);
   }
 
  /*
@@ -2102,18 +1888,12 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
 
   cupsLangFree(language);
 
-#ifdef LC_NUMERIC
-  setlocale(LC_NUMERIC, oldlocale);
-#else
-  setlocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
 #ifdef DEBUG
-  if (!feof(fp))
-    printf("Premature EOF at %lu...\n", (unsigned long)ftell(fp));
+  if (!cupsFileEOF(fp))
+    printf("Premature EOF at %lu...\n", (unsigned long)cupsFileTell(fp));
 #endif /* DEBUG */
 
-  if (ppd_status != PPD_OK)
+  if (cg->ppd_status != PPD_OK)
   {
    /*
     * Had an error reading the PPD file, cannot continue!
@@ -2124,103 +1904,69 @@ ppdOpen(FILE *fp)                      /* I - File to read from */
     return (NULL);
   }
 
-#ifndef __APPLE__
  /*
-  * Make sure that all PPD files with an InputSlot option have an
-  * "auto" choice that maps to no specific tray or media type.
+  * Create the sorted options array and set the option back-pointer for
+  * each choice and custom option...
   */
 
-  if ((option = ppdFindOption(ppd, "InputSlot")) != NULL)
-  {
-    for (i = 0; i < option->num_choices; i ++)
-      if (option->choices[i].code == NULL || !option->choices[i].code[0])
-       break;
-
-    if (i >= option->num_choices)
-    {
-     /*
-      * No "auto" input slot, add one...
-      */
-
-      choice = ppd_add_choice(option, "Auto");
-
-      strlcpy(choice->text, cupsLangString(language, CUPS_MSG_AUTO),
-              sizeof(choice->text));
-      choice->code = NULL;
-    }
-  }
-#endif /* !__APPLE__ */
-
- /*
-  * Set the option back-pointer for each choice...
-  */
-
-#ifndef __APPLE__
-  qsort(ppd->groups, ppd->num_groups, sizeof(ppd_group_t),
-        (int (*)(const void *, const void *))ppd_compare_groups);
-#endif /* !__APPLE__ */
+  ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
+                               (cups_ahash_func_t)ppd_hash_option,
+                              PPD_HASHSIZE);
 
   for (i = ppd->num_groups, group = ppd->groups;
        i > 0;
        i --, group ++)
   {
-#ifndef __APPLE__
-    qsort(group->options, group->num_options, sizeof(ppd_option_t),
-          (int (*)(const void *, const void *))ppd_compare_options);
-#endif /* !__APPLE__ */
-
     for (j = group->num_options, option = group->options;
          j > 0;
         j --, option ++)
     {
-      for (k = 0; k < option->num_choices; k ++)
-        option->choices[k].option = (void *)option;
-    }
+      ppd_coption_t    *coption;       /* Custom option */
+
+
+      cupsArrayAdd(ppd->options, option);
 
-#ifndef __APPLE__
-    qsort(group->subgroups, group->num_subgroups, sizeof(ppd_group_t),
-          (int (*)(const void *, const void *))ppd_compare_groups);
-#endif /* !__APPLE__ */
+      for (k = 0; k < option->num_choices; k ++)
+        option->choices[k].option = option;
 
-    for (j = group->num_subgroups, subgroup = group->subgroups;
-         j > 0;
-        j --, subgroup ++)
-    {
-#ifndef __APPLE__
-      qsort(subgroup->options, subgroup->num_options, sizeof(ppd_option_t),
-            (int (*)(const void *, const void *))ppd_compare_options);
-#endif /* !__APPLE__ */
-
-      for (k = group->num_options, option = group->options;
-           k > 0;
-          k --, option ++)
-      {
-        for (m = 0; m < option->num_choices; m ++)
-          option->choices[m].option = (void *)option;
-      }
+      if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
+        coption->option = option;
     }
   }
 
  /*
-  * Set the option pointers for all extended options...
+  * Sort the constraints...
   */
 
-  for (i = 0; i < ppd->num_extended; i ++)
-    ppd->extended[i]->option = ppdFindOption(ppd, ppd->extended[i]->keyword);
+  if (ppd->num_consts > 1)
+    qsort(ppd->consts, ppd->num_consts, sizeof(ppd_const_t),
+          (int (*)(const void *, const void *))ppd_compare_consts);
 
  /*
-  * Sort the attributes...
+  * Create an array to track the marked choices...
   */
 
-  if (ppd->num_attrs > 1)
-    qsort(ppd->attrs, ppd->num_attrs, sizeof(ppd_attr_t *),
-          (int (*)(const void *, const void *))_ppd_attr_compare);
+  ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
 
  /*
   * Return the PPD file structure...
   */
 
   return (ppd);
+
+ /*
+  * Common exit point for errors to save code size...
+  */
+
+  error:
+
+  ppd_free(string);
+
+  ppdClose(ppd);
+
+  cupsLangFree(language);
+
+  return (NULL);
 }
 
 
@@ -2231,15 +1977,17 @@ ppdOpen(FILE *fp)                       /* I - File to read from */
 ppd_file_t *                           /* O - PPD file record */
 ppdOpenFd(int fd)                      /* I - File to read from */
 {
-  FILE         *fp;                    /* File pointer */
-  ppd_file_t   *ppd;                   /* PPD file record */
+  cups_file_t          *fp;            /* CUPS file pointer */
+  ppd_file_t           *ppd;           /* PPD file record */
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Global data */
 
 
  /*
   * Set the line number to 0...
   */
 
-  ppd_line = 0;
+  cg->ppd_line = 0;
 
  /*
   * Range check input...
@@ -2247,7 +1995,7 @@ ppdOpenFd(int fd)                 /* I - File to read from */
 
   if (fd < 0)
   {
-    ppd_status = PPD_NULL_FILE;
+    cg->ppd_status = PPD_NULL_FILE;
 
     return (NULL);
   }
@@ -2256,18 +2004,16 @@ ppdOpenFd(int fd)                       /* I - File to read from */
   * Try to open the file and parse it...
   */
 
-  if ((fp = fdopen(fd, "r")) != NULL)
+  if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
   {
-    setbuf(fp, NULL);
-
-    ppd = ppdOpen(fp);
+    ppd = ppdOpen2(fp);
 
-    fclose(fp);
+    cupsFileClose(fp);
   }
   else
   {
-    ppd_status = PPD_FILE_OPEN_ERROR;
-    ppd        = NULL;
+    cg->ppd_status = PPD_FILE_OPEN_ERROR;
+    ppd            = NULL;
   }
 
   return (ppd);
@@ -2281,15 +2027,17 @@ ppdOpenFd(int fd)                       /* I - File to read from */
 ppd_file_t *                           /* O - PPD file record */
 ppdOpenFile(const char *filename)      /* I - File to read from */
 {
-  FILE         *fp;                    /* File pointer */
-  ppd_file_t   *ppd;                   /* PPD file record */
+  cups_file_t          *fp;            /* File pointer */
+  ppd_file_t           *ppd;           /* PPD file record */
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Global data */
 
 
  /*
   * Set the line number to 0...
   */
 
-  ppd_line = 0;
+  cg->ppd_line = 0;
 
  /*
   * Range check input...
@@ -2297,7 +2045,7 @@ ppdOpenFile(const char *filename) /* I - File to read from */
 
   if (filename == NULL)
   {
-    ppd_status = PPD_NULL_FILE;
+    cg->ppd_status = PPD_NULL_FILE;
 
     return (NULL);
   }
@@ -2306,22 +2054,39 @@ ppdOpenFile(const char *filename)       /* I - File to read from */
   * Try to open the file and parse it...
   */
 
-  if ((fp = fopen(filename, "r")) != NULL)
+  if ((fp = cupsFileOpen(filename, "r")) != NULL)
   {
-    ppd = ppdOpen(fp);
+    ppd = ppdOpen2(fp);
 
-    fclose(fp);
+    cupsFileClose(fp);
   }
   else
   {
-    ppd_status = PPD_FILE_OPEN_ERROR;
-    ppd        = NULL;
+    cg->ppd_status = PPD_FILE_OPEN_ERROR;
+    ppd            = NULL;
   }
 
   return (ppd);
 }
 
 
+/*
+ * 'ppdSetConformance()' - Set the conformance level for PPD files.
+ *
+ * @since CUPS 1.1.20@
+ */
+
+void
+ppdSetConformance(ppd_conform_t c)     /* I - Conformance level */
+{
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Global data */
+
+
+  cg->ppd_conform = c;
+}
+
+
 /*
  * 'ppd_add_attr()' - Add an attribute to the PPD data.
  */
@@ -2330,6 +2095,7 @@ static ppd_attr_t *                       /* O - New attribute */
 ppd_add_attr(ppd_file_t *ppd,          /* I - PPD file data */
              const char *name,         /* I - Attribute name */
              const char *spec,         /* I - Specifier string, if any */
+            const char *text,          /* I - Text string, if any */
             const char *value)         /* I - Value of attribute */
 {
   ppd_attr_t   **ptr,                  /* New array */
@@ -2343,6 +2109,14 @@ ppd_add_attr(ppd_file_t *ppd,            /* I - PPD file data */
   if (ppd == NULL || name == NULL || spec == NULL)
     return (NULL);
 
+ /*
+  * Create the array as needed...
+  */
+
+  if (!ppd->sorted_attrs)
+    ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
+                                     NULL);
+
  /*
   * Allocate memory for the new attribute...
   */
@@ -2371,8 +2145,15 @@ ppd_add_attr(ppd_file_t *ppd,            /* I - PPD file data */
 
   strlcpy(temp->name, name, sizeof(temp->name));
   strlcpy(temp->spec, spec, sizeof(temp->spec));
+  strlcpy(temp->text, text, sizeof(temp->text));
   temp->value = (char *)value;
 
+ /*
+  * Add the attribute to the sorted array...
+  */
+
+  cupsArrayAdd(ppd->sorted_attrs, temp);
+
  /*
   * Return the attribute...
   */
@@ -2442,16 +2223,79 @@ ppd_add_size(ppd_file_t *ppd,           /* I - PPD file */
 }
 
 
-#ifndef __APPLE__
 /*
- * 'ppd_compare_groups()' - Compare two groups.
+ * 'ppd_compare_attrs()' - Compare two attributes.
+ */
+
+static int                             /* O - Result of comparison */
+ppd_compare_attrs(ppd_attr_t *a,       /* I - First attribute */
+                  ppd_attr_t *b)       /* I - Second attribute */
+{
+  int  ret;                            /* Result of comparison */
+
+
+  if ((ret = strcasecmp(a->name, b->name)) != 0)
+    return (ret);
+  else
+    return (strcasecmp(a->spec, b->spec));
+}
+
+
+/*
+ * 'ppd_compare_choices()' - Compare two choices...
+ */
+
+static int                             /* O - Result of comparison */
+ppd_compare_choices(ppd_choice_t *a,   /* I - First choice */
+                    ppd_choice_t *b)   /* I - Second choice */
+{
+  return (a->option - b->option);
+}
+
+
+/*
+ * 'ppd_compare_consts()' - Compare two constraints.
+ */
+
+static int                             /* O - Result of comparison */
+ppd_compare_consts(ppd_const_t *a,     /* I - First constraint */
+                   ppd_const_t *b)     /* I - Second constraint */
+{
+  int  ret;                            /* Result of comparison */
+
+
+  if ((ret = strcmp(a->option1, b->option1)) != 0)
+    return (ret);
+  else if ((ret = strcmp(a->choice1, b->choice1)) != 0)
+    return (ret);
+  else if ((ret = strcmp(a->option2, b->option2)) != 0)
+    return (ret);
+  else
+    return (strcmp(a->choice2, b->choice2));
+}
+
+
+/*
+ * 'ppd_compare_coptions()' - Compare two custom options.
+ */
+
+static int                             /* O - Result of comparison */
+ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
+                     ppd_coption_t *b) /* I - Second option */
+{
+  return (strcasecmp(a->keyword, b->keyword));
+}
+
+
+/*
+ * 'ppd_compare_cparams()' - Compare two custom parameters.
  */
 
 static int                             /* O - Result of comparison */
-ppd_compare_groups(ppd_group_t *g0,    /* I - First group */
-                   ppd_group_t *g1)    /* I - Second group */
+ppd_compare_cparams(ppd_cparam_t *a,   /* I - First parameter */
+                    ppd_cparam_t *b)   /* I - Second parameter */
 {
-  return (strcasecmp(g0->text, g1->text));
+  return (strcasecmp(a->name, b->name));
 }
 
 
@@ -2460,19 +2304,18 @@ ppd_compare_groups(ppd_group_t *g0,     /* I - First group */
  */
 
 static int                             /* O - Result of comparison */
-ppd_compare_options(ppd_option_t *o0,  /* I - First option */
-                    ppd_option_t *o1)  /* I - Second option */
+ppd_compare_options(ppd_option_t *a,   /* I - First option */
+                    ppd_option_t *b)   /* I - Second option */
 {
-  return (strcasecmp(o0->text, o1->text));
+  return (strcasecmp(a->keyword, b->keyword));
 }
-#endif /* !__APPLE__ */
 
 
 /*
  * 'ppd_decode()' - Decode a string value...
  */
 
-static void
+static int                             /* O - Length of decoded string */
 ppd_decode(char *string)               /* I - String to decode */
 {
   char *inptr,                         /* Input pointer */
@@ -2483,14 +2326,14 @@ ppd_decode(char *string)                /* I - String to decode */
   outptr = string;
 
   while (*inptr != '\0')
-    if (*inptr == '<' && isxdigit(inptr[1]))
+    if (*inptr == '<' && isxdigit(inptr[1] & 255))
     {
      /*
       * Convert hex to 8-bit values...
       */
 
       inptr ++;
-      while (isxdigit(*inptr))
+      while (isxdigit(*inptr & 255))
       {
        if (isalpha(*inptr))
          *outptr = (tolower(*inptr) - 'a' + 10) << 4;
@@ -2499,6 +2342,9 @@ ppd_decode(char *string)          /* I - String to decode */
 
        inptr ++;
 
+        if (!isxdigit(*inptr & 255))
+         break;
+
        if (isalpha(*inptr))
          *outptr |= tolower(*inptr) - 'a' + 10;
        else
@@ -2517,61 +2363,9 @@ ppd_decode(char *string)         /* I - String to decode */
       *outptr++ = *inptr++;
 
   *outptr = '\0';
-}
-
-
-#ifndef __APPLE__
-/*
- * 'ppd_fix()' - Fix WinANSI characters in the range 0x80 to 0x9f to be
- *               valid ISO-8859-1 characters...
- */
-
-static void
-ppd_fix(char *string)                  /* IO - String to fix */
-{
-  unsigned char                *p;             /* Pointer into string */
-  static const unsigned char lut[32] = /* Lookup table for characters */
-                       {
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         0x20,
-                         'l',
-                         '`',
-                         '\'',
-                         '^',
-                         '~',
-                         0x20, /* bar */
-                         0x20, /* circumflex */
-                         0x20, /* dot */
-                         0x20, /* double dot */
-                         0x20,
-                         0x20, /* circle */
-                         0x20, /* ??? */
-                         0x20,
-                         '\"', /* should be right quotes */
-                         0x20, /* ??? */
-                         0x20  /* accent */
-                       };
 
-
-  for (p = (unsigned char *)string; *p; p ++)
-    if (*p >= 0x80 && *p < 0xa0)
-      *p = lut[*p - 0x80];
+  return ((int)(outptr - string));
 }
-#endif /* !__APPLE__ */
 
 
 /*
@@ -2624,7 +2418,9 @@ ppd_free_option(ppd_option_t *option)     /* I - Option to free */
     for (i = option->num_choices, choice = option->choices;
          i > 0;
          i --, choice ++)
+    {
       ppd_free(choice->code);
+    }
 
     ppd_free(option->choices);
   }
@@ -2632,59 +2428,84 @@ ppd_free_option(ppd_option_t *option)   /* I - Option to free */
 
 
 /*
- * 'ppd_get_extopt()' - Get an extended option record.
+ * 'ppd_get_coption()' - Get a custom option record.
  */
 
-static ppd_ext_option_t        *               /* O - Extended option... */
-ppd_get_extopt(ppd_file_t *ppd,                /* I - PPD file */
-               const char *name)       /* I - Name of option */
+static ppd_coption_t   *               /* O - Custom option... */
+ppd_get_coption(ppd_file_t *ppd,       /* I - PPD file */
+                const char *name)      /* I - Name of option */
 {
-  ppd_ext_option_t     **temp,         /* New array pointer */
-                       *extopt;        /* New extended option */
+  ppd_coption_t        *copt;                  /* New custom option */
 
 
  /*
   * See if the option already exists...
   */
 
-  if ((extopt = ppdFindExtOption(ppd, name)) != NULL)
-    return (extopt);
+  if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
+    return (copt);
 
  /*
-  * Not found, so create the extended option record...
+  * Not found, so create the custom option record...
   */
 
-  if ((extopt = calloc(sizeof(ppd_ext_option_t), 1)) == NULL)
+  if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
     return (NULL);
 
-  strlcpy(extopt->keyword, name, sizeof(extopt->keyword));
+  strlcpy(copt->keyword, name, sizeof(copt->keyword));
+
+  copt->params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
+
+  cupsArrayAdd(ppd->coptions, copt);
 
  /*
-  * Add this record to the end of the array...
+  * Return the new record...
   */
 
-  if (ppd->num_extended == 0)
-    temp = malloc(sizeof(ppd_ext_option_t *));
-  else
-    temp = realloc(ppd->extended, sizeof(ppd_ext_option_t *) *
-                                  (ppd->num_extended + 1));
+  return (copt);
+}
 
-  if (temp == NULL)
-  {
-    free(extopt);
+
+/*
+ * 'ppd_get_cparam()' - Get a custom parameter record.
+ */
+
+static ppd_cparam_t *                  /* O - Extended option... */
+ppd_get_cparam(ppd_coption_t *opt,     /* I - PPD file */
+               const char    *param,   /* I - Name of parameter */
+              const char    *text)     /* I - Human-readable text */
+{
+  ppd_cparam_t *cparam;                /* New custom parameter */
+
+
+ /*
+  * See if the parameter already exists...
+  */
+
+  if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
+    return (cparam);
+
+ /*
+  * Not found, so create the custom parameter record...
+  */
+
+  if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
     return (NULL);
-  }
 
-  ppd->extended           = temp;
-  temp[ppd->num_extended] = extopt;
+  strlcpy(cparam->name, param, sizeof(cparam->name));
+  strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
 
-  ppd->num_extended ++;
+ /*
+  * Add this record to the array...
+  */
+
+  cupsArrayAdd(opt->params, cparam);
 
  /*
   * Return the new record...
   */
 
-  return (extopt);
+  return (cparam);
 }
 
 
@@ -2693,24 +2514,34 @@ ppd_get_extopt(ppd_file_t *ppd,         /* I - PPD file */
  */
 
 static ppd_group_t *                   /* O - Named group */
-ppd_get_group(ppd_file_t *ppd,         /* I - PPD file */
-              const char *name,                /* I - Name of group */
-             const char *text)         /* I - Text for group */
+ppd_get_group(ppd_file_t      *ppd,    /* I - PPD file */
+              const char      *name,   /* I - Name of group */
+             const char      *text,    /* I - Text for group */
+              _cups_globals_t *cg,     /* I - Global data */
+             cups_encoding_t encoding) /* I - Encoding of text */
 {
   int          i;                      /* Looping var */
   ppd_group_t  *group;                 /* Group */
 
 
-  DEBUG_printf(("ppd_get_group(%p, \"%s\")\n", ppd, name));
+  DEBUG_printf(("ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)\n",
+                ppd, name, text, cg));
 
   for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
-    if (strcmp(group->name, name) == 0)
+    if (!strcmp(group->name, name))
       break;
 
   if (i == 0)
   {
     DEBUG_printf(("Adding group %s...\n", name));
 
+    if (cg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
+    {
+      cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
+
+      return (NULL);
+    }
+           
     if (ppd->num_groups == 0)
       group = malloc(sizeof(ppd_group_t));
     else
@@ -2718,7 +2549,11 @@ ppd_get_group(ppd_file_t *ppd,           /* I - PPD file */
                      (ppd->num_groups + 1) * sizeof(ppd_group_t));
 
     if (group == NULL)
+    {
+      cg->ppd_status = PPD_ALLOC_ERROR;
+
       return (NULL);
+    }
 
     ppd->groups = group;
     group += ppd->num_groups;
@@ -2726,7 +2561,9 @@ ppd_get_group(ppd_file_t *ppd,            /* I - PPD file */
 
     memset(group, 0, sizeof(ppd_group_t));
     strlcpy(group->name, name, sizeof(group->name));
-    strlcpy(group->text, text, sizeof(group->text));
+
+    cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
+                      sizeof(group->text), encoding);
   }
 
   return (group);
@@ -2745,8 +2582,11 @@ ppd_get_option(ppd_group_t *group,       /* I - Group */
   ppd_option_t *option;                /* Option */
 
 
+  DEBUG_printf(("ppd_get_option(group=%p(\"%s\"), name=\"%s\")\n",
+                group, group->name, name));
+
   for (i = group->num_options, option = group->options; i > 0; i --, option ++)
-    if (strcmp(option->keyword, name) == 0)
+    if (!strcmp(option->keyword, name))
       break;
 
   if (i == 0)
@@ -2772,45 +2612,72 @@ ppd_get_option(ppd_group_t *group,      /* I - Group */
 }
 
 
+/*
+ * 'ppd_hash_option()' - Generate a hash of the option name...
+ */
+
+static int                             /* O - Hash index */
+ppd_hash_option(ppd_option_t *option)  /* I - Option */
+{
+  int          hash = 0;               /* Hash index */
+  const char   *k;                     /* Pointer into keyword */
+
+
+  for (hash = option->keyword[0], k = option->keyword + 1; *k;)
+    hash = 33 * hash + *k++;
+
+  return (hash & 511);
+}
+
+
 /*
  * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
  *                necessary.
  */
 
 static int                             /* O - Bitmask of fields read */
-ppd_read(FILE *fp,                     /* I - File to read from */
-         char *keyword,                        /* O - Keyword from line */
-        char *option,                  /* O - Option from line */
-         char *text,                   /* O - Human-readable text from line */
-        char **string)                 /* O - Code/string data */
+ppd_read(cups_file_t    *fp,           /* I - File to read from */
+         char           *keyword,      /* O - Keyword from line */
+        char           *option,        /* O - Option from line */
+         char           *text,         /* O - Human-readable text from line */
+        char           **string,       /* O - Code/string data */
+         int            ignoreblank,   /* I - Ignore blank lines? */
+        _cups_globals_t *cg)           /* I - Global data */
 {
   int          ch,                     /* Character from file */
                col,                    /* Column in line */
                colon,                  /* Colon seen? */
                endquote,               /* Waiting for an end quote */
-               mask;                   /* Mask to be returned */
+               mask,                   /* Mask to be returned */
+               startline,              /* Start line */
+               textlen;                /* Length of text */
   char         *keyptr,                /* Keyword pointer */
                *optptr,                /* Option pointer */
                *textptr,               /* Text pointer */
                *strptr,                /* Pointer into string */
                *lineptr,               /* Current position in line buffer */
-               line[65536];            /* Line buffer (64k) */
-
+               *line;                  /* Line buffer */
+  int          linesize;               /* Current size of line buffer */
 
  /*
   * Range check everything...
   */
 
-  if (fp == NULL || keyword == NULL || option == NULL || text == NULL ||
-      string == NULL)
+  if (!fp || !keyword || !option || !text || !string)
     return (0);
 
  /*
   * Now loop until we have a valid line...
   */
 
-  *string = NULL;
-  col     = 0;
+  *string   = NULL;
+  col       = 0;
+  startline = cg->ppd_line + 1;
+  linesize  = 1024;
+  line      = malloc(linesize);
+
+  if (!line)
+    return (0);
 
   do
   {
@@ -2822,33 +2689,75 @@ ppd_read(FILE *fp,                      /* I - File to read from */
     endquote = 0;
     colon    = 0;
 
-    while ((ch = getc(fp)) != EOF &&
-           (lineptr - line) < (sizeof(line) - 1))
+    while ((ch = cupsFileGetChar(fp)) != EOF)
     {
+      if (lineptr >= (line + linesize - 1))
+      {
+       /*
+        * Expand the line buffer...
+       */
+
+        char *temp;                    /* Temporary line pointer */
+
+
+        linesize += 1024;
+       if (linesize > 262144)
+       {
+        /*
+         * Don't allow lines longer than 256k!
+         */
+
+          cg->ppd_line   = startline;
+          cg->ppd_status = PPD_LINE_TOO_LONG;
+
+         free(line);
+
+         return (0);
+       }
+
+        temp = realloc(line, linesize);
+       if (!temp)
+       {
+          cg->ppd_line   = startline;
+          cg->ppd_status = PPD_LINE_TOO_LONG;
+
+         free(line);
+
+         return (0);
+       }
+
+        lineptr = temp + (lineptr - line);
+       line    = temp;
+      }
+
       if (ch == '\r' || ch == '\n')
       {
        /*
        * Line feed or carriage return...
        */
 
-        ppd_line ++;
+        cg->ppd_line ++;
        col = 0;
 
-       if (lineptr == line)            /* Skip blank lines */
-          continue;
-
        if (ch == '\r')
        {
         /*
           * Check for a trailing line feed...
          */
 
-         if ((ch = getc(fp)) == EOF)
+         if ((ch = cupsFilePeekChar(fp)) == EOF)
+         {
+           ch = '\n';
            break;
-         if (ch != 0x0a)
-           ungetc(ch, fp);
+         }
+
+         if (ch == 0x0a)
+           cupsFileGetChar(fp);
        }
 
+       if (lineptr == line && ignoreblank)
+          continue;                    /* Skip blank lines */
+
        ch = '\n';
 
        if (!endquote)                  /* Continue for multi-line text */
@@ -2856,13 +2765,16 @@ ppd_read(FILE *fp,                      /* I - File to read from */
 
        *lineptr++ = '\n';
       }
-      else if (ch < ' ' && ch != '\t' && ch != 0x1a)
+      else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
       {
        /*
         * Other control characters...
        */
 
-        ppd_status = PPD_ILLEGAL_CHARACTER;
+        cg->ppd_line   = startline;
+        cg->ppd_status = PPD_ILLEGAL_CHARACTER;
+
+        free(line);
 
         return (0);
       }
@@ -2881,7 +2793,10 @@ ppd_read(FILE *fp,                       /* I - File to read from */
           * Line is too long...
          */
 
-          ppd_status = PPD_LINE_TOO_LONG;
+          cg->ppd_line   = startline;
+          cg->ppd_status = PPD_LINE_TOO_LONG;
+
+          free(line);
 
           return (0);
        }
@@ -2890,61 +2805,7 @@ ppd_read(FILE *fp,                       /* I - File to read from */
          colon = 1;
 
        if (ch == '\"' && colon)
-        {
          endquote = !endquote;
-
-          if (!endquote)
-         {
-          /*
-           * End of quoted string; ignore trailing characters...
-           */
-
-           while ((ch = getc(fp)) != EOF)
-             if (ch == '\r' || ch == '\n')
-             {
-                ppd_line ++;
-               col = 0;
-
-               if (ch == '\r')
-               {
-                 ch = getc(fp);
-                 if (ch != '\n')
-                   ungetc(ch, fp);
-
-                 ch = '\n';
-               }
-
-               break;
-             }
-             else if (ch < ' ' && ch != '\t' && ch != 0x1a)
-             {
-              /*
-               * Other control characters...
-               */
-
-               ppd_status = PPD_ILLEGAL_CHARACTER;
-
-               return (0);
-             }
-             else if (ch != 0x1a)
-             {
-               col ++;
-
-               if (col > (PPD_MAX_LINE - 1))
-               {
-                /*
-                 * Line is too long...
-                 */
-
-                 ppd_status = PPD_LINE_TOO_LONG;
-
-                 return (0);
-               }
-             }
-
-            break;
-         }
-       }
       }
     }
 
@@ -2954,12 +2815,12 @@ ppd_read(FILE *fp,                      /* I - File to read from */
       * Didn't finish this quoted string...
       */
 
-      while ((ch = getc(fp)) != EOF)
+      while ((ch = cupsFileGetChar(fp)) != EOF)
         if (ch == '\"')
          break;
        else if (ch == '\r' || ch == '\n')
        {
-         ppd_line ++;
+         cg->ppd_line ++;
          col = 0;
 
          if (ch == '\r')
@@ -2968,21 +2829,24 @@ ppd_read(FILE *fp,                      /* I - File to read from */
             * Check for a trailing line feed...
            */
 
-           if ((ch = getc(fp)) == EOF)
+           if ((ch = cupsFilePeekChar(fp)) == EOF)
              break;
-           if (ch != 0x0a)
-             ungetc(ch, fp);
+           if (ch == 0x0a)
+             cupsFileGetChar(fp);
          }
 
          ch = '\n';
        }
-       else if (ch < ' ' && ch != '\t' && ch != 0x1a)
+       else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
        {
         /*
           * Other control characters...
          */
 
-          ppd_status = PPD_ILLEGAL_CHARACTER;
+          cg->ppd_line   = startline;
+          cg->ppd_status = PPD_ILLEGAL_CHARACTER;
+
+          free(line);
 
           return (0);
        }
@@ -2996,7 +2860,10 @@ ppd_read(FILE *fp,                       /* I - File to read from */
             * Line is too long...
            */
 
-            ppd_status = PPD_LINE_TOO_LONG;
+            cg->ppd_line   = startline;
+            cg->ppd_status = PPD_LINE_TOO_LONG;
+
+            free(line);
 
             return (0);
          }
@@ -3009,14 +2876,14 @@ ppd_read(FILE *fp,                      /* I - File to read from */
       * Didn't finish this line...
       */
 
-      while ((ch = getc(fp)) != EOF)
+      while ((ch = cupsFileGetChar(fp)) != EOF)
        if (ch == '\r' || ch == '\n')
        {
         /*
          * Line feed or carriage return...
          */
 
-          ppd_line ++;
+          cg->ppd_line ++;
          col = 0;
 
          if (ch == '\r')
@@ -3025,21 +2892,24 @@ ppd_read(FILE *fp,                      /* I - File to read from */
             * Check for a trailing line feed...
            */
 
-           if ((ch = getc(fp)) == EOF)
+           if ((ch = cupsFilePeekChar(fp)) == EOF)
              break;
-           if (ch != 0x0a)
-             ungetc(ch, fp);
+           if (ch == 0x0a)
+             cupsFileGetChar(fp);
          }
 
          break;
        }
-       else if (ch < ' ' && ch != '\t' && ch != 0x1a)
+       else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
        {
         /*
           * Other control characters...
          */
 
-          ppd_status = PPD_ILLEGAL_CHARACTER;
+          cg->ppd_line   = startline;
+          cg->ppd_status = PPD_ILLEGAL_CHARACTER;
+
+          free(line);
 
           return (0);
        }
@@ -3053,7 +2923,10 @@ ppd_read(FILE *fp,                       /* I - File to read from */
             * Line is too long...
            */
 
-            ppd_status = PPD_LINE_TOO_LONG;
+            cg->ppd_line   = startline;
+            cg->ppd_status = PPD_LINE_TOO_LONG;
+
+            free(line);
 
             return (0);
          }
@@ -3067,8 +2940,24 @@ ppd_read(FILE *fp,                       /* I - File to read from */
 
 /*    DEBUG_printf(("LINE = \"%s\"\n", line));*/
 
+   /*
+    * The dynamically created PPDs for older style Mac OS X
+    * drivers include a large blob of data inserted as comments
+    * at the end of the file.  As an optimization we can stop
+    * reading the PPD when we get to the start of this data.
+    */
+
+    if (!strcmp(line, "*%APLWORKSET START"))
+    {
+      free(line);
+      return (0);
+    }
+
     if (ch == EOF && lineptr == line)
+    {
+      free(line);
       return (0);
+    }
 
    /*
     * Now parse it...
@@ -3082,12 +2971,31 @@ ppd_read(FILE *fp,                      /* I - File to read from */
     text[0]    = '\0';
     *string    = NULL;
 
-    if (!line[0] ||                    /* Blank line */
-        strcmp(line, "*") == 0 ||      /* (Bad) comment line */
-        strncmp(line, "*%", 2) == 0 || /* Comment line */
-        strncmp(line, "*?", 2) == 0 || /* Query line */
-        strcmp(line, "*End") == 0)     /* End of multi-line string */
+    if ((!line[0] ||                   /* Blank line */
+         !strncmp(line, "*%", 2) ||    /* Comment line */
+         !strcmp(line, "*End")) &&     /* End of multi-line string */
+        ignoreblank)                   /* Ignore these? */
+    {
+      startline = cg->ppd_line + 1;
       continue;
+    }
+
+    if (!strcmp(line, "*"))            /* (Bad) comment line */
+    {
+      if (cg->ppd_conform == PPD_CONFORM_RELAXED)
+      {
+       startline = cg->ppd_line + 1;
+       continue;
+      }
+      else
+      {
+        cg->ppd_line   = startline;
+        cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
+
+        free(line);
+        return (0);
+      }
+    }
 
     if (line[0] != '*')                        /* All lines start with an asterisk */
     {
@@ -3096,16 +3004,22 @@ ppd_read(FILE *fp,                      /* I - File to read from */
       */
 
       for (lineptr = line; *lineptr; lineptr ++)
-        if (!isspace(*lineptr))
+        if (!isspace(*lineptr & 255))
          break;
 
       if (*lineptr)
       {
-        ppd_status = PPD_MISSING_ASTERISK;
+        cg->ppd_status = PPD_MISSING_ASTERISK;
+        free(line);
         return (0);
       }
-      else
+      else if (ignoreblank)
         continue;
+      else
+      {
+        free(line);
+        return (0);
+      }
     }
 
    /*
@@ -3114,12 +3028,13 @@ ppd_read(FILE *fp,                      /* I - File to read from */
 
     keyptr = keyword;
 
-    while (*lineptr != '\0' && *lineptr != ':' && !isspace(*lineptr))
+    while (*lineptr != '\0' && *lineptr != ':' && !isspace(*lineptr & 255))
     {
       if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
           (keyptr - keyword) >= (PPD_MAX_NAME - 1))
       {
-        ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
+        cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
+        free(line);
        return (0);
       }
 
@@ -3128,31 +3043,32 @@ ppd_read(FILE *fp,                      /* I - File to read from */
 
     *keyptr = '\0';
 
-    if (strcmp(keyword, "End") == 0)
+    if (!strcmp(keyword, "End"))
       continue;
 
     mask |= PPD_KEYWORD;
 
 /*    DEBUG_printf(("keyword = \"%s\", lineptr = \"%s\"\n", keyword, lineptr));*/
 
-    if (isspace(*lineptr))
+    if (isspace(*lineptr & 255))
     {
      /*
       * Get an option name...
       */
 
-      while (isspace(*lineptr))
+      while (isspace(*lineptr & 255))
         lineptr ++;
 
       optptr = option;
 
-      while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':' &&
+      while (*lineptr != '\0' && !isspace(*lineptr & 255) && *lineptr != ':' &&
              *lineptr != '/')
       {
        if (*lineptr <= ' ' || *lineptr > 126 ||
            (optptr - option) >= (PPD_MAX_NAME - 1))
         {
-          ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
+          cg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
+          free(line);
          return (0);
        }
 
@@ -3160,6 +3076,17 @@ ppd_read(FILE *fp,                       /* I - File to read from */
       }
 
       *optptr = '\0';
+
+      if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
+      {
+        cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
+        free(line);
+       return (0);
+      }
+
+      while (isspace(*lineptr & 255))
+       lineptr ++;
+
       mask |= PPD_OPTION;
 
 /*      DEBUG_printf(("option = \"%s\", lineptr = \"%s\"\n", option, lineptr));*/
@@ -3179,7 +3106,8 @@ ppd_read(FILE *fp,                        /* I - File to read from */
          if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
              (textptr - text) >= (PPD_MAX_LINE - 1))
          {
-           ppd_status = PPD_ILLEGAL_TRANSLATION;
+           cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
+            free(line);
            return (0);
          }
 
@@ -3187,32 +3115,63 @@ ppd_read(FILE *fp,                      /* I - File to read from */
         }
 
        *textptr = '\0';
-       ppd_decode(text);
+       textlen  = ppd_decode(text);
 
+       if (textlen > PPD_MAX_TEXT && cg->ppd_conform == PPD_CONFORM_STRICT)
+       {
+         cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
+          free(line);
+         return (0);
+       }
+           
        mask |= PPD_TEXT;
       }
 
 /*      DEBUG_printf(("text = \"%s\", lineptr = \"%s\"\n", text, lineptr));*/
     }
 
+    if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
+    {
+      cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
+      free(line);
+      return (0);
+    }
+
+    while (isspace(*lineptr & 255))
+      lineptr ++;
+
     if (*lineptr == ':')
     {
      /*
-      * Get string...
+      * Get string after triming leading and trailing whitespace...
       */
 
-      *string = malloc(strlen(lineptr) + 1);
-
-      while (*lineptr == ':' || isspace(*lineptr))
+      lineptr ++;
+      while (isspace(*lineptr & 255))
         lineptr ++;
 
-      strptr = *string;
+      strptr = lineptr + strlen(lineptr) - 1;
+      while (strptr >= lineptr && isspace(*strptr & 255))
+        *strptr-- = '\0';
+
+      if (*strptr == '\"')
+      {
+       /*
+        * Quoted string by itself...
+       */
+
+       *string = malloc(strlen(lineptr) + 1);
 
-      for (; *lineptr != '\0'; lineptr ++)
-       if (*lineptr != '\"')
-         *strptr++ = *lineptr;
+       strptr = *string;
 
-      *strptr = '\0';
+       for (; *lineptr != '\0'; lineptr ++)
+         if (*lineptr != '\"')
+           *strptr++ = *lineptr;
+
+       *strptr = '\0';
+      }
+      else
+        *string = strdup(lineptr);
 
 /*      DEBUG_printf(("string = \"%s\", lineptr = \"%s\"\n", *string, lineptr));*/
 
@@ -3221,10 +3180,12 @@ ppd_read(FILE *fp,                      /* I - File to read from */
   }
   while (mask == 0);
 
+  free(line);
+
   return (mask);
 }
 
 
 /*
- * End of "$Id: ppd.c,v 1.51.2.42 2003/02/19 18:08:36 mike Exp $".
+ * End of "$Id$".
  */