]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ppd.c
Merge changes from CUPS 1.6svn-r9939.
[thirdparty/cups.git] / cups / ppd.c
index ea9eb86b77fc3e8ef1327ce2240ad5b1839eb9fe..325c4de5a09bb0c1f59aee6a97f9a2a7ed876c40 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: ppd.c 6937 2007-09-10 21:13:31Z mike $"
+ * "$Id: ppd.c 9900 2011-08-17 20:59:46Z mike $"
  *
- *   PPD file routines for the Common UNIX Printing System (CUPS).
+ *   PPD file routines for CUPS.
  *
- *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 2007-2011 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  *   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_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"
 
 
 /*
@@ -109,12 +108,11 @@ 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);
 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_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);
@@ -130,6 +128,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);
 
 
 /*
@@ -143,7 +143,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 */
@@ -162,7 +161,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);
@@ -215,13 +215,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...
@@ -293,6 +287,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...
   */
@@ -304,7 +325,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/Mac OS X 10.3@
  */
 
 const char *                           /* O - Status string */
@@ -332,11 +353,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]));
@@ -351,17 +375,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);
@@ -371,7 +395,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/Mac OS X 10.3@
  */
 
 ppd_status_t                           /* O - Status code */
@@ -425,10 +449,10 @@ ppdOpen(FILE *fp)                 /* I - File to read from */
 /*
  * 'ppdOpen2()' - Read a PPD file into memory.
  *
- * @since CUPS 1.2@
+ * @since CUPS 1.2/Mac OS X 10.5@
  */
 
-ppd_file_t *                           /* O - PPD file record */
+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 */
 {
   int                  i, j, k;        /* Looping vars */
@@ -457,7 +481,6 @@ 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_encoding_t      encoding;       /* Encoding of PPD file */
@@ -529,6 +552,8 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
                        };
 
 
+  DEBUG_printf(("ppdOpen2(fp=%p)", fp));
+
  /*
   * Default to "OK" status...
   */
@@ -555,7 +580,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(("2ppdOpen2: mask=%x, keyword=\"%s\"...", mask, keyword));
 
   if (mask == 0 ||
       strcmp(keyword, "PPD-Adobe") ||
@@ -569,11 +594,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(("2ppdOpen2: keyword=%s, string=%p", keyword, string));
 
   _cupsStrFree(string);
 
@@ -585,23 +611,19 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
   {
     cg->ppd_status = PPD_ALLOC_ERROR;
 
+    _cupsStrFree(string);
+    ppd_free(line.buffer);
+
     return (NULL);
   }
 
   ppd->language_level = 2;
   ppd->color_device   = 0;
-  ppd->colorspace     = PPD_CS_GRAY;
+  ppd->colorspace     = PPD_CS_N;
   ppd->landscape      = -90;
   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...
   */
@@ -612,34 +634,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)
   {
-#ifdef DEBUG
-    printf("mask = %x, keyword = \"%s\"", mask, keyword);
+    DEBUG_printf(("2ppdOpen2: mask=%x, keyword=\"%s\", name=\"%s\", "
+                  "text=\"%s\", string=%d chars...", mask, keyword, name, text,
+                 string ? (int)strlen(string) : 0));
 
-    if (name[0] != '\0')
-      printf(", name = \"%s\"", name);
-
-    if (text[0] != '\0')
-      printf(", text = \"%s\"", text);
-
-    if (string != NULL)
-    {
-      if (strlen(string) > 40)
-        printf(", string = %p", string);
-      else
-        printf(", string = \"%s\"", string);
-    }
-
-    puts("");
-#endif /* DEBUG */
-
-    if (strcmp(keyword, "CloseUI") && strcmp(keyword, "CloseGroup") &&
-       strcmp(keyword, "CloseSubGroup") && strncmp(keyword, "Default", 7) &&
-        strcmp(keyword, "JCLCloseUI") && strcmp(keyword, "JCLOpenUI") &&
-       strcmp(keyword, "OpenUI") && strcmp(keyword, "OpenGroup") &&
-       strcmp(keyword, "OpenSubGroup") && string == NULL)
+    if (strncmp(keyword, "Default", 7) && !string &&
+        cg->ppd_conform != PPD_CONFORM_RELAXED)
     {
      /*
       * Need a string value!
@@ -649,6 +653,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
@@ -685,7 +691,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(("2ppdOpen2: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
                      keyword));
 
         if (!group)
@@ -694,7 +700,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(("2ppdOpen2: Adding to group %s...", group->text));
           option = ppd_get_option(group, keyword);
          group  = NULL;
        }
@@ -729,7 +735,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(("2ppdOpen2: Setting Default%s to %s via attribute...",
                          option->keyword, ppd->attrs[j]->value));
            strlcpy(option->defchoice, ppd->attrs[j]->value,
                    sizeof(option->defchoice));
@@ -872,11 +878,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);
@@ -898,7 +903,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 ++;
@@ -935,7 +940,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;
@@ -1027,7 +1033,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("2ppdOpen2: Processing Custom option...");
 
      /*
       * Get the option and custom option...
@@ -1040,7 +1046,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);
@@ -1051,19 +1057,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("1ppdOpen2: 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);
       }
 
      /*
@@ -1080,21 +1090,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("1ppdOpen2: 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));
@@ -1108,7 +1119,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       else if (!strcmp(string, "Plus90"))
         ppd->landscape = 90;
     }
-    else if (!strcmp(keyword, "Emulators"))
+    else if (!strcmp(keyword, "Emulators") && string)
     {
       for (count = 1, sptr = string; sptr != NULL;)
         if ((sptr = strchr(sptr, ' ')) != NULL)
@@ -1164,8 +1175,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) +
@@ -1199,15 +1241,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, strlen(name)));
+      DEBUG_printf(("2ppdOpen2: 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(("2ppdOpen2: OpenUI of %s in group %s...", name,
                     group ? group->text : "(null)"));
 
       if (subgroup != NULL)
@@ -1218,7 +1260,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(("2ppdOpen2: Adding to group %s...", group->text));
         option = ppd_get_option(group, name);
        group  = NULL;
       }
@@ -1256,7 +1298,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(("2ppdOpen2: Setting Default%s to %s via attribute...",
                        option->keyword, ppd->attrs[j]->value));
          strlcpy(option->defchoice, ppd->attrs[j]->value,
                  sizeof(option->defchoice));
@@ -1292,26 +1334,27 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
       * attribute...
       */
 
-      if (!strcasecmp(name, "PageRegion"))
+      if (!_cups_strcasecmp(name, "PageRegion"))
         strcpy(custom_name, "CustomPageSize");
       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("1ppdOpen2: 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"))
@@ -1374,7 +1417,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(("2ppdOpen2: Setting Default%s to %s via attribute...",
                        option->keyword, ppd->attrs[j]->value));
          strlcpy(option->defchoice, ppd->attrs[j]->value,
                  sizeof(option->defchoice));
@@ -1404,7 +1447,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("1ppdOpen2: Unable to add Custom choice!");
 
          cg->ppd_status = PPD_ALLOC_ERROR;
 
@@ -1414,7 +1457,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"))
@@ -1577,11 +1620,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(("2ppdOpen2: 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(("2ppdOpen2: %s is now %s...", keyword, option->defchoice));
       }
       else
       {
@@ -1594,7 +1637,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(("2ppdOpen2: Setting %s to %s...", keyword, string));
          strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
        }
       }
@@ -1602,6 +1645,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
@@ -1665,7 +1714,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"...
@@ -1719,7 +1768,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"...
@@ -1767,52 +1816,6 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
            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...
       */
@@ -1871,7 +1874,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(("2ppdOpen2: group=%p, subgroup=%p", group, subgroup));
 
       if (!strcmp(keyword, "PageSize"))
       {
@@ -1922,18 +1925,26 @@ 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))
-    printf("Premature EOF at %lu...\n", (unsigned long)cupsFileTell(fp));
+    DEBUG_printf(("1ppdOpen2: Premature EOF at %lu...\n",
+                  (unsigned long)cupsFileTell(fp)));
 #endif /* DEBUG */
 
   if (cg->ppd_status != PPD_OK)
@@ -1947,6 +1958,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...
@@ -1977,14 +1999,6 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
     }
   }
 
- /*
-  * Sort the constraints...
-  */
-
-  if (ppd->num_consts > 1)
-    qsort(ppd->consts, ppd->num_consts, sizeof(ppd_const_t),
-          (int (*)(const void *, const void *))ppd_compare_consts);
-
  /*
   * Create an array to track the marked choices...
   */
@@ -2003,15 +2017,11 @@ 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);
 }
 
@@ -2020,7 +2030,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
  * 'ppdOpenFd()' - Read a PPD file into memory.
  */
 
-ppd_file_t *                           /* O - PPD file record */
+ppd_file_t *                           /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
 ppdOpenFd(int fd)                      /* I - File to read from */
 {
   cups_file_t          *fp;            /* CUPS file pointer */
@@ -2070,7 +2080,7 @@ ppdOpenFd(int fd)                 /* I - File to read from */
  * 'ppdOpenFile()' - Read a PPD file into memory.
  */
 
-ppd_file_t *                           /* O - PPD file record */
+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 */
 {
   cups_file_t          *fp;            /* File pointer */
@@ -2119,7 +2129,7 @@ ppdOpenFile(const char *filename) /* I - File to read from */
 /*
  * 'ppdSetConformance()' - Set the conformance level for PPD files.
  *
- * @since CUPS 1.1.20@
+ * @since CUPS 1.1.20/Mac OS X 10.4@
  */
 
 void
@@ -2277,13 +2287,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));
 }
 
 
@@ -2299,28 +2303,6 @@ ppd_compare_choices(ppd_choice_t *a,     /* I - First choice */
 }
 
 
-/*
- * '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.
  */
@@ -2329,19 +2311,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));
 }
 
 
@@ -2353,7 +2323,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));
 }
 
 
@@ -2381,7 +2351,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;
@@ -2391,7 +2361,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';
@@ -2414,6 +2384,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.
  */
@@ -2500,7 +2494,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);
 
@@ -2570,7 +2564,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 ++)
@@ -2579,7 +2573,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))
     {
@@ -2587,7 +2581,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
@@ -2628,7 +2622,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 ++)
@@ -2869,8 +2863,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
            if (ch == 0x0a)
              cupsFileGetChar(fp);
          }
-
-         ch = '\n';
        }
        else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
        {
@@ -2965,7 +2957,7 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
 
     *lineptr = '\0';
 
-    DEBUG_printf(("LINE = \"%s\"\n", line));
+    DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
 
    /*
     * The dynamically created PPDs for older style Mac OS X
@@ -3024,7 +3016,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)
@@ -3044,7 +3036,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))
@@ -3063,20 +3055,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 ||
@@ -3091,19 +3081,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 == '/')
       {
        /*
@@ -3111,7 +3099,7 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
        */
 
         lineptr ++;
-       
+
        textptr = text;
 
        while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
@@ -3134,20 +3122,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 == ':')
@@ -3157,11 +3143,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 == '\"')
@@ -3176,8 +3162,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;
     }
   }
@@ -3188,5 +3172,125 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
 
 
 /*
- * End of "$Id: ppd.c 6937 2007-09-10 21:13:31Z 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: ppd.c 9900 2011-08-17 20:59:46Z mike $".
  */