]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ppd.c
Update svn:keyword properties.
[thirdparty/cups.git] / cups / ppd.c
index 436dd05f293c023105cdf4d25f6147d79e838ae4..6813ea0e640628ff68a53b6492601bdc724c30fd 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: ppd.c 7552 2008-05-12 17:42:15Z mike $"
+ * "$Id$"
  *
- *   PPD file routines for the Common UNIX Printing System (CUPS).
+ *   PPD file routines for CUPS.
  *
- *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 2007-2012 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  *                            LanguageEncoding.
  *   ppdLastError()         - Return the status from the last ppdOpen*().
  *   ppdOpen()              - Read a PPD file into memory.
+ *   _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.
  *   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.
@@ -41,9 +43,9 @@
  *   ppd_compare_attrs()    - Compare two attributes.
  *   ppd_compare_choices()  - Compare two choices...
  *   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_filters()     - Free the filters array.
  *   ppd_free_group()       - Free a single UI group.
  *   ppd_free_option()      - Free a single option.
  *   ppd_get_coption()      - Get a custom option record.
  *   ppd_hash_option()      - Generate a hash of the option name...
  *   ppd_read()             - Read a line from a PPD file, skipping comment
  *                            lines as necessary.
+ *   ppd_update_filters()   - Update the filters array as needed.
  */
 
 /*
  * Include necessary headers.
  */
 
-#include "globals.h"
-#include "debug.h"
-#include <stdlib.h>
+#include "cups-private.h"
+#include "ppd-private.h"
 
 
 /*
@@ -110,9 +112,9 @@ 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_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_filters(ppd_file_t *ppd);
 static void            ppd_free_group(ppd_group_t *group);
 static void            ppd_free_option(ppd_option_t *option);
 static ppd_coption_t   *ppd_get_coption(ppd_file_t *ppd, const char *name);
@@ -128,6 +130,8 @@ static int          ppd_read(cups_file_t *fp, _ppd_line_t *line,
                                 char *keyword, char *option, char *text,
                                 char **string, int ignoreblank,
                                 _cups_globals_t *cg);
+static int             ppd_update_filters(ppd_file_t *ppd,
+                                          _cups_globals_t *cg);
 
 
 /*
@@ -141,7 +145,6 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
   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 */
@@ -160,7 +163,8 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
 
   _cupsStrFree(ppd->lang_encoding);
   _cupsStrFree(ppd->nickname);
-  _cupsStrFree(ppd->patches);
+  if (ppd->patches)
+    free(ppd->patches);
   _cupsStrFree(ppd->jcl_begin);
   _cupsStrFree(ppd->jcl_end);
   _cupsStrFree(ppd->jcl_ps);
@@ -213,13 +217,7 @@ ppdClose(ppd_file_t *ppd)          /* I - PPD file record */
   * Free any filters...
   */
 
-  if (ppd->num_filters > 0)
-  {
-    for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
-      _cupsStrFree(*filter);
-
-    ppd_free(ppd->filters);
-  }
+  ppd_free_filters(ppd);
 
  /*
   * Free any fonts...
@@ -291,6 +289,33 @@ ppdClose(ppd_file_t *ppd)          /* I - PPD file record */
 
   cupsArrayDelete(ppd->coptions);
 
+ /*
+  * Free constraints...
+  */
+
+  if (ppd->cups_uiconstraints)
+  {
+    _ppd_cups_uiconsts_t *consts;      /* Current constraints */
+
+
+    for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
+         consts;
+        consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
+    {
+      free(consts->constraints);
+      free(consts);
+    }
+
+    cupsArrayDelete(ppd->cups_uiconstraints);
+  }
+
+ /*
+  * Free any PPD cache/mapping data...
+  */
+
+  if (ppd->cache)
+    _ppdCacheDestroy(ppd->cache);
+
  /*
   * Free the whole record...
   */
@@ -302,7 +327,7 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
 /*
  * 'ppdErrorString()' - Returns the text assocated with a status.
  *
- * @since CUPS 1.1.19@
+ * @since CUPS 1.1.19/OS X 10.3@
  */
 
 const char *                           /* O - Status string */
@@ -330,11 +355,14 @@ ppdErrorString(ppd_status_t status)       /* I - PPD status */
                  _("Illegal option keyword string"),
                  _("Illegal translation string"),
                  _("Illegal whitespace character"),
-                 _("Bad custom parameter")
+                 _("Bad custom parameter"),
+                 _("Missing option keyword"),
+                 _("Bad value string"),
+                 _("Missing CloseGroup")
                };
 
 
-  if (status < PPD_OK || status > PPD_ILLEGAL_WHITESPACE)
+  if (status < PPD_OK || status >= PPD_MAX_STATUS)
     return (_cupsLangString(cupsLangDefault(), _("Unknown")));
   else
     return (_cupsLangString(cupsLangDefault(), messages[status]));
@@ -349,17 +377,17 @@ ppdErrorString(ppd_status_t status)       /* I - PPD status */
 cups_encoding_t                                /* O - CUPS encoding value */
 _ppdGetEncoding(const char *name)      /* I - LanguageEncoding string */
 {
-  if (!strcasecmp(name, "ISOLatin1"))
+  if (!_cups_strcasecmp(name, "ISOLatin1"))
     return (CUPS_ISO8859_1);
-  else if (!strcasecmp(name, "ISOLatin2"))
+  else if (!_cups_strcasecmp(name, "ISOLatin2"))
     return (CUPS_ISO8859_2);
-  else if (!strcasecmp(name, "ISOLatin5"))
+  else if (!_cups_strcasecmp(name, "ISOLatin5"))
     return (CUPS_ISO8859_5);
-  else if (!strcasecmp(name, "JIS83-RKSJ"))
-    return (CUPS_WINDOWS_932);
-  else if (!strcasecmp(name, "MacStandard"))
+  else if (!_cups_strcasecmp(name, "JIS83-RKSJ"))
+    return (CUPS_JIS_X0213);
+  else if (!_cups_strcasecmp(name, "MacStandard"))
     return (CUPS_MAC_ROMAN);
-  else if (!strcasecmp(name, "WindowsANSI"))
+  else if (!_cups_strcasecmp(name, "WindowsANSI"))
     return (CUPS_WINDOWS_1252);
   else
     return (CUPS_UTF8);
@@ -369,7 +397,7 @@ _ppdGetEncoding(const char *name)   /* I - LanguageEncoding string */
 /*
  * 'ppdLastError()' - Return the status from the last ppdOpen*().
  *
- * @since CUPS 1.1.19@
+ * @since CUPS 1.1.19/OS X 10.3@
  */
 
 ppd_status_t                           /* O - Status code */
@@ -387,47 +415,15 @@ ppdLastError(int *line)                   /* O - Line number */
 
 
 /*
- * 'ppdOpen()' - Read a PPD file into memory.
- */
-
-ppd_file_t *                           /* O - PPD file record */
-ppdOpen(FILE *fp)                      /* I - File to read from */
-{
-  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.
+ * '_ppdOpen()' - Read a PPD file into memory.
  *
- * @since CUPS 1.2@
+ * @since CUPS 1.2/OS X 10.5@
  */
 
 ppd_file_t *                           /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
-ppdOpen2(cups_file_t *fp)              /* I - File to read from */
+_ppdOpen(
+    cups_file_t                *fp,            /* I - File to read from */
+    _ppd_localization_t        localization)   /* I - Localization to load */
 {
   int                  i, j, k;        /* Looping vars */
   int                  count;          /* Temporary count */
@@ -455,15 +451,19 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
   ppd_section_t                section;        /* Order dependency section */
   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_lang_t          *lang;          /* Language data */
   cups_encoding_t      encoding;       /* Encoding of PPD file */
   _cups_globals_t      *cg = _cupsGlobals();
                                        /* Global data */
   char                 custom_name[PPD_MAX_NAME];
                                        /* CustomFoo attribute name */
   ppd_attr_t           *custom_attr;   /* CustomFoo attribute */
+  char                 ll[4],          /* Language + '.' */
+                       ll_CC[7];       /* Language + country + '.' */
+  size_t               ll_len = 0,     /* Language length */
+                       ll_CC_len = 0;  /* Language + country length */
   static const char * const ui_keywords[] =
                        {
 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
@@ -525,7 +525,14 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
                          "PageSize"
 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
                        };
+  static const char * const color_keywords[] = /* Keywords associated with color profiles */
+                       {
+                         ".cupsICCProfile",
+                         ".ColorModel",
+                       };
+
 
+  DEBUG_printf(("_ppdOpen(fp=%p)", fp));
 
  /*
   * Default to "OK" status...
@@ -544,6 +551,25 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
     return (NULL);
   }
 
+ /*
+  * If only loading a single localization set up the strings to match...
+  */
+
+  if (localization == _PPD_LOCALIZATION_DEFAULT)
+  {
+    if ((lang = cupsLangDefault()) == NULL)
+      return (NULL);
+
+    snprintf(ll_CC, sizeof(ll_CC), "%s.", lang->language);
+    snprintf(ll, sizeof(ll), "%2.2s.", lang->language);
+
+    ll_CC_len = strlen(ll_CC);
+    ll_len    = strlen(ll);
+
+    DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
+                  ll_CC, ll));
+  }
+
  /*
   * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
   */
@@ -553,7 +579,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
 
   mask = ppd_read(fp, &line, keyword, name, text, &string, 0, cg);
 
-  DEBUG_printf(("mask=%x, keyword=\"%s\"...\n", mask, keyword));
+  DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask, keyword));
 
   if (mask == 0 ||
       strcmp(keyword, "PPD-Adobe") ||
@@ -567,11 +593,12 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       cg->ppd_status = PPD_MISSING_PPDADOBE4;
 
     _cupsStrFree(string);
+    ppd_free(line.buffer);
 
     return (NULL);
   }
 
-  DEBUG_printf(("ppdOpen: keyword = %s, string = %p\n", keyword, string));
+  DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword, string));
 
   _cupsStrFree(string);
 
@@ -583,6 +610,9 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
   {
     cg->ppd_status = PPD_ALLOC_ERROR;
 
+    _cupsStrFree(string);
+    ppd_free(line.buffer);
+
     return (NULL);
   }
 
@@ -593,13 +623,6 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
   ppd->coptions       = cupsArrayNew((cups_array_func_t)ppd_compare_coptions,
                                      NULL);
 
- /*
-  * Get the default language for the user...
-  */
-
-  language = cupsLangDefault();
-  loc      = localeconv();
-
  /*
   * Read lines from the PPD file and add them to the file record...
   */
@@ -610,14 +633,16 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
   choice     = NULL;
   ui_keyword = 0;
   encoding   = CUPS_ISO8859_1;
+  loc        = localeconv();
 
   while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, cg)) != 0)
   {
-    DEBUG_printf(("mask=%x, keyword=\"%s\", name=\"%s\", text=\"%s\", "
-                  "string=%d chars...", mask, keyword, name, text,
+    DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
+                  "text=\"%s\", string=%d chars...", mask, keyword, name, text,
                  string ? (int)strlen(string) : 0));
 
-    if (strncmp(keyword, "Default", 7) && !string)
+    if (strncmp(keyword, "Default", 7) && !string &&
+        cg->ppd_conform != PPD_CONFORM_RELAXED)
     {
      /*
       * Need a string value!
@@ -627,6 +652,8 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
 
       goto error;
     }
+    else if (!string)
+      continue;
 
    /*
     * Certain main keywords (as defined by the PPD spec) may be used
@@ -647,6 +674,50 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
       ui_keyword = 0;
     }
 
+   /*
+    * If we are filtering out keyword localizations, see if this line needs to
+    * be used...
+    */
+
+    if (localization != _PPD_LOCALIZATION_ALL &&
+        (temp = strchr(keyword, '.')) != NULL &&
+        ((temp - keyword) == 2 || (temp - keyword) == 5) &&
+        _cups_isalpha(keyword[0]) &&
+        _cups_isalpha(keyword[1]) &&
+        (keyword[2] == '.' ||
+         (keyword[2] == '_' && _cups_isalpha(keyword[3]) &&
+          _cups_isalpha(keyword[4]) && keyword[5] == '.')))
+    {
+      if (localization == _PPD_LOCALIZATION_NONE ||
+         (localization == _PPD_LOCALIZATION_DEFAULT &&
+          strncmp(ll_CC, keyword, ll_CC_len) &&
+          strncmp(ll, keyword, ll_len)))
+      {
+       DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
+       continue;
+      }
+      else if (localization == _PPD_LOCALIZATION_ICC_PROFILES)
+      {
+       /*
+        * Only load localizations for the color profile related keywords...
+        */
+
+       for (i = 0;
+            i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0]));
+            i ++)
+       {
+         if (!_cups_strcasecmp(temp, color_keywords[i]))
+           break;
+       }
+
+       if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0])))
+       {
+         DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
+         continue;
+       }
+      }
+    }
+
     if (option == NULL &&
         (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
            (PPD_KEYWORD | PPD_OPTION | PPD_STRING))
@@ -663,7 +734,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
 
         ui_keyword = 1;
 
-        DEBUG_printf(("**** FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!\n",
+        DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
                      keyword));
 
         if (!group)
@@ -672,7 +743,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
                                     encoding)) == NULL)
            goto error;
 
-          DEBUG_printf(("Adding to group %s...\n", group->text));
+          DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
           option = ppd_get_option(group, keyword);
          group  = NULL;
        }
@@ -707,7 +778,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
              !strcmp(ppd->attrs[j]->name + 7, keyword) &&
              ppd->attrs[j]->value)
          {
-           DEBUG_printf(("Setting Default%s to %s via attribute...\n",
+           DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
                          option->keyword, ppd->attrs[j]->value));
            strlcpy(option->defchoice, ppd->attrs[j]->value,
                    sizeof(option->defchoice));
@@ -850,11 +921,10 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       ppd->num_filters ++;
 
      /*
-      * Copy filter string and prevent it from being freed below...
+      * Retain a copy of the filter string...
       */
 
-      *filter = string;
-      string  = NULL;
+      *filter = _cupsStrRetain(string);
     }
     else if (!strcmp(keyword, "Throughput"))
       ppd->throughput = atoi(string);
@@ -876,7 +946,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
 
        goto error;
       }
-      
+
       ppd->fonts                 = tempfonts;
       ppd->fonts[ppd->num_fonts] = _cupsStrAlloc(name);
       ppd->num_fonts ++;
@@ -913,7 +983,8 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
       * Get the parameter data...
       */
 
-      if (sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
+      if (!string ||
+          sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
                  cmaximum) != 4)
       {
         cg->ppd_status = PPD_BAD_CUSTOM_PARAM;
@@ -1005,7 +1076,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
     {
       ppd_option_t     *custom_option; /* Custom option */
 
-      DEBUG_puts("Processing Custom option...");
+      DEBUG_puts("2_ppdOpen: Processing Custom option...");
 
      /*
       * Get the option and custom option...
@@ -1018,7 +1089,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
        goto error;
       }
 
-      if (option && !strcasecmp(option->keyword, keyword + 6))
+      if (option && !_cups_strcasecmp(option->keyword, keyword + 6))
         custom_option = option;
       else
         custom_option = ppdFindOption(ppd, keyword + 6);
@@ -1029,19 +1100,23 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
        * Add the "custom" option...
        */
 
-       if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
-       {
-         DEBUG_puts("Unable to add Custom choice!");
+        if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
+         if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
+         {
+           DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
 
-         cg->ppd_status = PPD_ALLOC_ERROR;
+           cg->ppd_status = PPD_ALLOC_ERROR;
 
-         goto error;
-       }
+           goto error;
+         }
 
        strlcpy(choice->text, text[0] ? text : _("Custom"),
                sizeof(choice->text));
 
        choice->code = _cupsStrAlloc(string);
+
+       if (custom_option->section == PPD_ORDER_JCL)
+         ppd_decode(choice->code);
       }
 
      /*
@@ -1058,21 +1133,22 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
 
        ppd_add_size(ppd, "Custom");
 
-       if (option && !strcasecmp(option->keyword, "PageRegion"))
+       if (option && !_cups_strcasecmp(option->keyword, "PageRegion"))
          custom_option = option;
        else
          custom_option = ppdFindOption(ppd, "PageRegion");
 
         if (custom_option)
        {
-         if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
-         {
-           DEBUG_puts("Unable to add Custom choice!");
+         if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
+           if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
+           {
+             DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
 
-           cg->ppd_status = PPD_ALLOC_ERROR;
+             cg->ppd_status = PPD_ALLOC_ERROR;
 
-           goto error;
-         }
+             goto error;
+           }
 
          strlcpy(choice->text, text[0] ? text : _("Custom"),
                  sizeof(choice->text));
@@ -1142,8 +1218,39 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
     }
     else if (!strcmp(keyword, "JobPatchFile"))
     {
+     /*
+      * CUPS STR #3421: Check for "*JobPatchFile: int: string"
+      */
+
+      if (isdigit(*string & 255))
+      {
+        for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
+
+        if (*sptr == ':')
+        {
+         /*
+          * Found "*JobPatchFile: int: string"...
+          */
+
+          cg->ppd_status = PPD_BAD_VALUE;
+
+         goto error;
+        }
+      }
+
+      if (!name[0] && cg->ppd_conform == PPD_CONFORM_STRICT)
+      {
+       /*
+        * Found "*JobPatchFile: string"...
+        */
+
+        cg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
+
+       goto error;
+      }
+
       if (ppd->patches == NULL)
-        ppd->patches = _cupsStrAlloc(string);
+        ppd->patches = strdup(string);
       else
       {
         temp = realloc(ppd->patches, strlen(ppd->patches) +
@@ -1157,7 +1264,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
 
         ppd->patches = temp;
 
-        strcpy(ppd->patches + strlen(ppd->patches), string);
+        memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1);
       }
     }
     else if (!strcmp(keyword, "OpenUI"))
@@ -1177,15 +1284,15 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
       * Add an option record to the current sub-group, group, or file...
       */
 
-      DEBUG_printf(("name=\"%s\" (%d)\n", name, (int)strlen(name)));
+      DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
 
       if (name[0] == '*')
         _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
 
-      for (i = (int)strlen(name) - 1; i > 0 && isspace(name[i] & 255); i --)
+      for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
         name[i] = '\0'; /* Eliminate trailing spaces */
 
-      DEBUG_printf(("OpenUI of %s in group %s...\n", name,
+      DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name,
                     group ? group->text : "(null)"));
 
       if (subgroup != NULL)
@@ -1196,7 +1303,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
                                   encoding)) == NULL)
          goto error;
 
-        DEBUG_printf(("Adding to group %s...\n", group->text));
+        DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
         option = ppd_get_option(group, name);
        group  = NULL;
       }
@@ -1234,7 +1341,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
            !strcmp(ppd->attrs[j]->name + 7, name) &&
            ppd->attrs[j]->value)
        {
-         DEBUG_printf(("Setting Default%s to %s via attribute...\n",
+         DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
                        option->keyword, ppd->attrs[j]->value));
          strlcpy(option->defchoice, ppd->attrs[j]->value,
                  sizeof(option->defchoice));
@@ -1270,26 +1377,27 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
       * attribute...
       */
 
-      if (!strcasecmp(name, "PageRegion"))
-        strcpy(custom_name, "CustomPageSize");
+      if (!_cups_strcasecmp(name, "PageRegion"))
+        strlcpy(custom_name, "CustomPageSize", sizeof(custom_name));
       else
         snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
 
       if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
       {
-       if ((choice = ppd_add_choice(option, "Custom")) == NULL)
-       {
-         DEBUG_puts("Unable to add Custom choice!");
+        if ((choice = ppdFindChoice(option, "Custom")) == NULL)
+         if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+         {
+           DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
 
-         cg->ppd_status = PPD_ALLOC_ERROR;
+           cg->ppd_status = PPD_ALLOC_ERROR;
 
-         goto error;
-       }
+           goto error;
+         }
 
        strlcpy(choice->text,
                custom_attr->text[0] ? custom_attr->text : _("Custom"),
                sizeof(choice->text));
-        choice->code = _cupsStrAlloc(custom_attr->value);
+        choice->code = _cupsStrRetain(custom_attr->value);
       }
     }
     else if (!strcmp(keyword, "JCLOpenUI"))
@@ -1352,7 +1460,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
            !strcmp(ppd->attrs[j]->name + 7, name) &&
            ppd->attrs[j]->value)
        {
-         DEBUG_printf(("Setting Default%s to %s via attribute...\n",
+         DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
                        option->keyword, ppd->attrs[j]->value));
          strlcpy(option->defchoice, ppd->attrs[j]->value,
                  sizeof(option->defchoice));
@@ -1382,7 +1490,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       {
        if ((choice = ppd_add_choice(option, "Custom")) == NULL)
        {
-         DEBUG_puts("Unable to add Custom choice!");
+         DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
 
          cg->ppd_status = PPD_ALLOC_ERROR;
 
@@ -1392,7 +1500,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
        strlcpy(choice->text,
                custom_attr->text[0] ? custom_attr->text : _("Custom"),
                sizeof(choice->text));
-        choice->code = _cupsStrAlloc(custom_attr->value);
+        choice->code = _cupsStrRetain(custom_attr->value);
       }
     }
     else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
@@ -1555,11 +1663,11 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
         * Set the default as part of the current option...
        */
 
-        DEBUG_printf(("Setting %s to %s...\n", keyword, string));
+        DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
 
         strlcpy(option->defchoice, string, sizeof(option->defchoice));
 
-        DEBUG_printf(("%s is now %s...\n", keyword, option->defchoice));
+        DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
       }
       else
       {
@@ -1572,7 +1680,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
 
         if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
        {
-         DEBUG_printf(("Setting %s to %s...\n", keyword, string));
+         DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
          strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
        }
       }
@@ -1580,6 +1688,12 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
     else if (!strcmp(keyword, "UIConstraints") ||
              !strcmp(keyword, "NonUIConstraints"))
     {
+      if (!string)
+      {
+       cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
+       goto error;
+      }
+
       if (ppd->num_consts == 0)
        constraint = calloc(2, sizeof(ppd_const_t));
       else
@@ -1643,7 +1757,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
             constraint->choice1[0] = '\0';
             constraint->choice2[0] = '\0';
            break;
-           
+
        case 3 : /* Two options, one choice... */
           /*
            * Check for broken constraints like "* Option"...
@@ -1697,7 +1811,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
               constraint->choice2[0] = '\0';
            }
            break;
-           
+
        case 4 : /* Two options, two choices... */
           /*
            * Check for broken constraints like "* Option"...
@@ -1803,7 +1917,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
                 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
             !strcmp(keyword, option->keyword))
     {
-      DEBUG_printf(("group = %p, subgroup = %p\n", group, subgroup));
+      DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
 
       if (!strcmp(keyword, "PageSize"))
       {
@@ -1830,9 +1944,9 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
         cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
                           sizeof(choice->text), encoding);
       else if (!strcmp(name, "True"))
-        strcpy(choice->text, _("Yes"));
+        strlcpy(choice->text, _("Yes"), sizeof(choice->text));
       else if (!strcmp(name, "False"))
-        strcpy(choice->text, _("No"));
+        strlcpy(choice->text, _("No"), sizeof(choice->text));
       else
         strlcpy(choice->text, name, sizeof(choice->text));
 
@@ -1854,18 +1968,25 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
       _cupsStrFree(string);
   }
 
-  if (line.buffer)
-    free(line.buffer);
+ /*
+  * Check for a missing CloseGroup...
+  */
+
+  if (group && cg->ppd_conform == PPD_CONFORM_STRICT)
+  {
+    cg->ppd_status = PPD_MISSING_CLOSE_GROUP;
+    goto error;
+  }
+
+  ppd_free(line.buffer);
 
  /*
   * Reset language preferences...
   */
 
-  cupsLangFree(language);
-
 #ifdef DEBUG
   if (!cupsFileEOF(fp))
-    DEBUG_printf(("Premature EOF at %lu...\n",
+    DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
                   (unsigned long)cupsFileTell(fp)));
 #endif /* DEBUG */
 
@@ -1880,6 +2001,17 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
     return (NULL);
   }
 
+ /*
+  * Update the filters array as needed...
+  */
+
+  if (!ppd_update_filters(ppd, cg))
+  {
+    ppdClose(ppd);
+
+    return (NULL);
+  }
+
  /*
   * Create the sorted options array and set the option back-pointer for
   * each choice and custom option...
@@ -1928,19 +2060,62 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
 
   error:
 
-  if (line.buffer)
-    free(line.buffer);
-
   _cupsStrFree(string);
+  ppd_free(line.buffer);
 
   ppdClose(ppd);
 
-  cupsLangFree(language);
-
   return (NULL);
 }
 
 
+/*
+ * 'ppdOpen()' - Read a PPD file into memory.
+ */
+
+ppd_file_t *                           /* O - PPD file record */
+ppdOpen(FILE *fp)                      /* I - File to read from */
+{
+  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 = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
+
+ /*
+  * Close the CUPS file and return the PPD...
+  */
+
+  cupsFileClose(cf);
+
+  return (ppd);
+}
+
+
+/*
+ * 'ppdOpen2()' - Read a PPD file into memory.
+ *
+ * @since CUPS 1.2/OS X 10.5@
+ */
+
+ppd_file_t *                           /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
+ppdOpen2(cups_file_t *fp)              /* I - File to read from */
+{
+  return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
+}
+
+
 /*
  * 'ppdOpenFd()' - Read a PPD file into memory.
  */
@@ -1992,11 +2167,12 @@ ppdOpenFd(int fd)                       /* I - File to read from */
 
 
 /*
- * 'ppdOpenFile()' - Read a PPD file into memory.
+ * '_ppdOpenFile()' - Read a PPD file into memory.
  */
 
 ppd_file_t *                           /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
-ppdOpenFile(const char *filename)      /* I - File to read from */
+_ppdOpenFile(const char                  *filename,    /* I - File to read from */
+            _ppd_localization_t  localization) /* I - Localization to load */
 {
   cups_file_t          *fp;            /* File pointer */
   ppd_file_t           *ppd;           /* PPD file record */
@@ -2027,7 +2203,7 @@ ppdOpenFile(const char *filename) /* I - File to read from */
 
   if ((fp = cupsFileOpen(filename, "r")) != NULL)
   {
-    ppd = ppdOpen2(fp);
+    ppd = _ppdOpen(fp, localization);
 
     cupsFileClose(fp);
   }
@@ -2041,10 +2217,21 @@ ppdOpenFile(const char *filename)       /* I - File to read from */
 }
 
 
+/*
+ * 'ppdOpenFile()' - Read a PPD file into memory.
+ */
+
+ppd_file_t *                           /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
+ppdOpenFile(const char *filename)      /* I - File to read from */
+{
+  return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
+}
+
+
 /*
  * 'ppdSetConformance()' - Set the conformance level for PPD files.
  *
- * @since CUPS 1.1.20@
+ * @since CUPS 1.1.20/OS X 10.4@
  */
 
 void
@@ -2202,13 +2389,7 @@ 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));
+  return (_cups_strcasecmp(a->name, b->name));
 }
 
 
@@ -2232,19 +2413,7 @@ 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_cparams(ppd_cparam_t *a,   /* I - First parameter */
-                    ppd_cparam_t *b)   /* I - Second parameter */
-{
-  return (strcasecmp(a->name, b->name));
+  return (_cups_strcasecmp(a->keyword, b->keyword));
 }
 
 
@@ -2256,7 +2425,7 @@ static int                                /* O - Result of comparison */
 ppd_compare_options(ppd_option_t *a,   /* I - First option */
                     ppd_option_t *b)   /* I - Second option */
 {
-  return (strcasecmp(a->keyword, b->keyword));
+  return (_cups_strcasecmp(a->keyword, b->keyword));
 }
 
 
@@ -2284,7 +2453,7 @@ ppd_decode(char *string)          /* I - String to decode */
       inptr ++;
       while (isxdigit(*inptr & 255))
       {
-       if (isalpha(*inptr))
+       if (_cups_isalpha(*inptr))
          *outptr = (tolower(*inptr) - 'a' + 10) << 4;
        else
          *outptr = (*inptr - '0') << 4;
@@ -2294,7 +2463,7 @@ ppd_decode(char *string)          /* I - String to decode */
         if (!isxdigit(*inptr & 255))
          break;
 
-       if (isalpha(*inptr))
+       if (_cups_isalpha(*inptr))
          *outptr |= tolower(*inptr) - 'a' + 10;
        else
          *outptr |= *inptr - '0';
@@ -2317,6 +2486,30 @@ ppd_decode(char *string)         /* I - String to decode */
 }
 
 
+/*
+ * 'ppd_free_filters()' - Free the filters array.
+ */
+
+static void
+ppd_free_filters(ppd_file_t *ppd)      /* I - PPD file */
+{
+  int  i;                              /* Looping var */
+  char **filter;                       /* Current filter */
+
+
+  if (ppd->num_filters > 0)
+  {
+    for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
+      _cupsStrFree(*filter);
+
+    ppd_free(ppd->filters);
+
+    ppd->num_filters = 0;
+    ppd->filters     = NULL;
+  }
+}
+
+
 /*
  * 'ppd_free_group()' - Free a single UI group.
  */
@@ -2403,7 +2596,7 @@ ppd_get_coption(ppd_file_t *ppd,  /* I - PPD file */
 
   strlcpy(copt->keyword, name, sizeof(copt->keyword));
 
-  copt->params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
+  copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
 
   cupsArrayAdd(ppd->coptions, copt);
 
@@ -2473,7 +2666,7 @@ ppd_get_group(ppd_file_t      *ppd,       /* I - PPD file */
   ppd_group_t  *group;                 /* Group */
 
 
-  DEBUG_printf(("ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)\n",
+  DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
                 ppd, name, text, cg));
 
   for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
@@ -2482,7 +2675,7 @@ ppd_get_group(ppd_file_t      *ppd,       /* I - PPD file */
 
   if (i == 0)
   {
-    DEBUG_printf(("Adding group %s...\n", name));
+    DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
 
     if (cg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
     {
@@ -2490,7 +2683,7 @@ ppd_get_group(ppd_file_t      *ppd,       /* I - PPD file */
 
       return (NULL);
     }
-           
+
     if (ppd->num_groups == 0)
       group = malloc(sizeof(ppd_group_t));
     else
@@ -2531,7 +2724,7 @@ 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",
+  DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
                 group, group->name, name));
 
   for (i = group->num_options, option = group->options; i > 0; i --, option ++)
@@ -2866,10 +3059,10 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
 
     *lineptr = '\0';
 
-    DEBUG_printf(("LINE=\"%s\"\n", line->buffer));
+    DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
 
    /*
-    * The dynamically created PPDs for older style Mac OS X
+    * The dynamically created PPDs for older style 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.
@@ -2925,7 +3118,7 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
       */
 
       for (lineptr = line->buffer; *lineptr; lineptr ++)
-        if (!isspace(*lineptr & 255))
+        if (*lineptr && !_cups_isspace(*lineptr))
          break;
 
       if (*lineptr)
@@ -2945,7 +3138,7 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
 
     keyptr = keyword;
 
-    while (*lineptr != '\0' && *lineptr != ':' && !isspace(*lineptr & 255))
+    while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
     {
       if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
           (keyptr - keyword) >= (PPD_MAX_NAME - 1))
@@ -2964,20 +3157,18 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
 
     mask |= PPD_KEYWORD;
 
-/*    DEBUG_printf(("keyword = \"%s\", lineptr = \"%s\"\n", keyword, lineptr));*/
-
-    if (isspace(*lineptr & 255))
+    if (_cups_isspace(*lineptr))
     {
      /*
       * Get an option name...
       */
 
-      while (isspace(*lineptr & 255))
+      while (_cups_isspace(*lineptr))
         lineptr ++;
 
       optptr = option;
 
-      while (*lineptr != '\0' && !isspace(*lineptr & 255) && *lineptr != ':' &&
+      while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
              *lineptr != '/')
       {
        if (*lineptr <= ' ' || *lineptr > 126 ||
@@ -2992,19 +3183,17 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
 
       *optptr = '\0';
 
-      if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
+      if (_cups_isspace(*lineptr) && cg->ppd_conform == PPD_CONFORM_STRICT)
       {
         cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
        return (0);
       }
 
-      while (isspace(*lineptr & 255))
+      while (_cups_isspace(*lineptr))
        lineptr ++;
 
       mask |= PPD_OPTION;
 
-/*      DEBUG_printf(("option = \"%s\", lineptr = \"%s\"\n", option, lineptr));*/
-
       if (*lineptr == '/')
       {
        /*
@@ -3012,7 +3201,7 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
        */
 
         lineptr ++;
-       
+
        textptr = text;
 
        while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
@@ -3035,20 +3224,18 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
          cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
          return (0);
        }
-           
+
        mask |= PPD_TEXT;
       }
-
-/*      DEBUG_printf(("text = \"%s\", lineptr = \"%s\"\n", text, lineptr));*/
     }
 
-    if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
+    if (_cups_isspace(*lineptr) && cg->ppd_conform == PPD_CONFORM_STRICT)
     {
       cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
       return (0);
     }
 
-    while (isspace(*lineptr & 255))
+    while (_cups_isspace(*lineptr))
       lineptr ++;
 
     if (*lineptr == ':')
@@ -3058,11 +3245,11 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
       */
 
       lineptr ++;
-      while (isspace(*lineptr & 255))
+      while (_cups_isspace(*lineptr))
         lineptr ++;
 
       strptr = lineptr + strlen(lineptr) - 1;
-      while (strptr >= lineptr && isspace(*strptr & 255))
+      while (strptr >= lineptr && _cups_isspace(*strptr))
         *strptr-- = '\0';
 
       if (*strptr == '\"')
@@ -3077,8 +3264,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
 
       *string = _cupsStrAlloc(lineptr);
 
-/*      DEBUG_printf(("string = \"%s\", lineptr = \"%s\"\n", *string, lineptr));*/
-
       mask |= PPD_STRING;
     }
   }
@@ -3089,5 +3274,125 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
 
 
 /*
- * End of "$Id: ppd.c 7552 2008-05-12 17:42:15Z mike $".
+ * 'ppd_update_filters()' - Update the filters array as needed.
+ *
+ * This function re-populates the filters array with cupsFilter2 entries that
+ * have been stripped of the destination MIME media types and any maxsize hints.
+ *
+ * (All for backwards-compatibility)
+ */
+
+static int                             /* O - 1 on success, 0 on failure */
+ppd_update_filters(ppd_file_t      *ppd,/* I - PPD file */
+                   _cups_globals_t *cg)        /* I - Global data */
+{
+  ppd_attr_t   *attr;                  /* Current cupsFilter2 value */
+  char         srcsuper[16],           /* Source MIME media type */
+               srctype[256],
+               dstsuper[16],           /* Destination MIME media type */
+               dsttype[256],
+               program[1024],          /* Command to run */
+               *ptr,                   /* Pointer into command to run */
+               buffer[1024],           /* Re-written cupsFilter value */
+               **filter;               /* Current filter */
+  int          cost;                   /* Cost of filter */
+
+
+  DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, cg));
+
+ /*
+  * See if we have any cupsFilter2 lines...
+  */
+
+  if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
+  {
+    DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
+    return (1);
+  }
+
+ /*
+  * Yes, free the cupsFilter-defined filters and re-build...
+  */
+
+  ppd_free_filters(ppd);
+
+  do
+  {
+   /*
+    * Parse the cupsFilter2 string:
+    *
+    *   src/type dst/type cost program
+    *   src/type dst/type cost maxsize(n) program
+    */
+
+    DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
+
+    if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
+              srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
+    {
+      DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
+      cg->ppd_status = PPD_BAD_VALUE;
+
+      return (0);
+    }
+
+    DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
+                  "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
+                 srcsuper, srctype, dstsuper, dsttype, cost, program));
+
+    if (!strncmp(program, "maxsize(", 8) &&
+        (ptr = strchr(program + 8, ')')) != NULL)
+    {
+      DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
+
+      ptr ++;
+      while (_cups_isspace(*ptr))
+       ptr ++;
+
+      _cups_strcpy(program, ptr);
+      DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
+    }
+
+   /*
+    * Convert to cupsFilter format:
+    *
+    *   src/type cost program
+    */
+
+    snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
+             program);
+    DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
+
+   /*
+    * Add a cupsFilter-compatible string to the filters array.
+    */
+
+    if (ppd->num_filters == 0)
+      filter = malloc(sizeof(char *));
+    else
+      filter = realloc(ppd->filters, sizeof(char *) * (ppd->num_filters + 1));
+
+    if (filter == NULL)
+    {
+      DEBUG_puts("5ppd_update_filters: Out of memory.");
+      cg->ppd_status = PPD_ALLOC_ERROR;
+
+      return (0);
+    }
+
+    ppd->filters     = filter;
+    filter           += ppd->num_filters;
+    ppd->num_filters ++;
+
+    *filter = _cupsStrAlloc(buffer);
+  }
+  while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
+
+  DEBUG_puts("5ppd_update_filters: Completed OK.");
+  return (1);
+}
+
+
+/*
+ * End of "$Id$".
  */