]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ppd.c
Import CUPS trunk (1.4svn) r7116.
[thirdparty/cups.git] / cups / ppd.c
index 37d0f8d6b5fa0bbd0c982d577efa5a27c43778c0..50ad5787f08325cf435330279d86a564bd56d826 100644 (file)
@@ -1,25 +1,16 @@
 /*
- * "$Id: ppd.c 6188 2007-01-10 16:23:06Z mike $"
+ * "$Id: ppd.c 6937 2007-09-10 21:13:31Z mike $"
  *
  *   PPD file routines for the Common UNIX Printing System (CUPS).
  *
+ *   Copyright 2007 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   PostScript is a trademark of Adobe Systems, Inc.
  *
@@ -48,6 +39,8 @@
  *   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.
@@ -58,6 +51,7 @@
  *   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.
  */
 #define PPD_TEXT       4               /* Line contained human-readable text */
 #define PPD_STRING     8               /* Line contained a string or code */
 
+#define PPD_HASHSIZE   512             /* Size of hash */
+
+
+/*
+ * Line buffer structure...
+ */
+
+typedef struct _ppd_line_s
+{
+  char         *buffer;                /* Pointer to buffer */
+  size_t       bufsize;                /* Size of the buffer */
+} _ppd_line_t;
+
 
 /*
  * Local functions...
@@ -101,6 +108,8 @@ static ppd_attr_t   *ppd_add_attr(ppd_file_t *ppd, const char *name,
 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);
@@ -116,8 +125,10 @@ static ppd_group_t *ppd_get_group(ppd_file_t *ppd, const char *name,
                                       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(cups_file_t *fp, char *keyword, char *option,
-                                char *text, char **string, int ignoreblank,
+static int             ppd_hash_option(ppd_option_t *option);
+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);
 
 
@@ -149,12 +160,12 @@ ppdClose(ppd_file_t *ppd)         /* I - PPD file record */
   * 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_end);
-  ppd_free(ppd->jcl_ps);
+  _cupsStrFree(ppd->lang_encoding);
+  _cupsStrFree(ppd->nickname);
+  _cupsStrFree(ppd->patches);
+  _cupsStrFree(ppd->jcl_begin);
+  _cupsStrFree(ppd->jcl_end);
+  _cupsStrFree(ppd->jcl_ps);
 
  /*
   * Free any emulations...
@@ -164,8 +175,8 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
   {
     for (i = ppd->num_emulations, emul = ppd->emulations; i > 0; i --, emul ++)
     {
-      ppd_free(emul->start);
-      ppd_free(emul->stop);
+      _cupsStrFree(emul->start);
+      _cupsStrFree(emul->stop);
     }
 
     ppd_free(ppd->emulations);
@@ -184,6 +195,7 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
   }
 
   cupsArrayDelete(ppd->options);
+  cupsArrayDelete(ppd->marked);
 
  /*
   * Free any page sizes...
@@ -206,9 +218,7 @@ 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);
-    }
+      _cupsStrFree(*filter);
 
     ppd_free(ppd->filters);
   }
@@ -220,9 +230,7 @@ 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);
-    }
+      _cupsStrFree(*font);
 
     ppd_free(ppd->fonts);
   }
@@ -242,7 +250,7 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
   {
     for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++)
     {
-      ppd_free((*attr)->value);
+      _cupsStrFree((*attr)->value);
       ppd_free(*attr);
     }
 
@@ -268,7 +276,7 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
         case PPD_CUSTOM_PASSCODE :
         case PPD_CUSTOM_PASSWORD :
         case PPD_CUSTOM_STRING :
-            ppd_free(cparam->current.custom_string);
+            _cupsStrFree(cparam->current.custom_string);
            break;
 
        default :
@@ -425,6 +433,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
 {
   int                  i, j, k;        /* Looping vars */
   int                  count;          /* Temporary count */
+  _ppd_line_t          line;           /* Line buffer */
   ppd_file_t           *ppd;           /* PPD file record */
   ppd_group_t          *group,         /* Current group */
                        *subgroup;      /* Current sub-group */
@@ -454,6 +463,9 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
   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 */
   static const char * const ui_keywords[] =
                        {
 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
@@ -538,7 +550,10 @@ ppdOpen2(cups_file_t *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, 0, cg);
+  line.buffer  = NULL;
+  line.bufsize = 0;
+
+  mask = ppd_read(fp, &line, keyword, name, text, &string, 0, cg);
 
   DEBUG_printf(("mask=%x, keyword=\"%s\"...\n", mask, keyword));
 
@@ -553,14 +568,14 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
     if (cg->ppd_status == PPD_OK)
       cg->ppd_status = PPD_MISSING_PPDADOBE4;
 
-    ppd_free(string);
+    _cupsStrFree(string);
 
     return (NULL);
   }
 
   DEBUG_printf(("ppdOpen: keyword = %s, string = %p\n", keyword, string));
 
-  ppd_free(string);
+  _cupsStrFree(string);
 
  /*
   * Allocate memory for the PPD file record...
@@ -573,7 +588,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
     return (NULL);
   }
 
-  ppd->language_level = 1;
+  ppd->language_level = 2;
   ppd->color_device   = 0;
   ppd->colorspace     = PPD_CS_GRAY;
   ppd->landscape      = -90;
@@ -598,7 +613,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
   ui_keyword = 0;
   encoding   = CUPS_ISO8859_1;
 
-  while ((mask = ppd_read(fp, keyword, name, text, &string, 1, cg)) != 0)
+  while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, cg)) != 0)
   {
 #ifdef DEBUG
     printf("mask = %x, keyword = \"%s\"", mask, keyword);
@@ -744,7 +759,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
       * Say all PPD files are UTF-8, since we convert to UTF-8...
       */
 
-      ppd->lang_encoding = strdup("UTF-8");
+      ppd->lang_encoding = _cupsStrAlloc("UTF-8");
       encoding           = _ppdGetEncoding(string);
     }
     else if (!strcmp(keyword, "LanguageVersion"))
@@ -765,10 +780,10 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
 
 
         cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
-       ppd->nickname = strdup((char *)utf8);
+       ppd->nickname = _cupsStrAlloc((char *)utf8);
       }
       else
-        ppd->nickname = strdup(string);
+        ppd->nickname = _cupsStrAlloc(string);
     }
     else if (!strcmp(keyword, "Product"))
       ppd->product = string;
@@ -778,17 +793,17 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       ppd->ttrasterizer = string;
     else if (!strcmp(keyword, "JCLBegin"))
     {
-      ppd->jcl_begin = strdup(string);
+      ppd->jcl_begin = _cupsStrAlloc(string);
       ppd_decode(ppd->jcl_begin);      /* Decode quoted string */
     }
     else if (!strcmp(keyword, "JCLEnd"))
     {
-      ppd->jcl_end = strdup(string);
+      ppd->jcl_end = _cupsStrAlloc(string);
       ppd_decode(ppd->jcl_end);                /* Decode quoted string */
     }
     else if (!strcmp(keyword, "JCLToPSInterpreter"))
     {
-      ppd->jcl_ps = strdup(string);
+      ppd->jcl_ps = _cupsStrAlloc(string);
       ppd_decode(ppd->jcl_ps);         /* Decode quoted string */
     }
     else if (!strcmp(keyword, "AccurateScreensSupport"))
@@ -840,8 +855,6 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
 
       if (filter == NULL)
       {
-        ppd_free(filter);
-
         cg->ppd_status = PPD_ALLOC_ERROR;
 
        goto error;
@@ -880,7 +893,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
       }
       
       ppd->fonts                 = tempfonts;
-      ppd->fonts[ppd->num_fonts] = strdup(name);
+      ppd->fonts[ppd->num_fonts] = _cupsStrAlloc(name);
       ppd->num_fonts ++;
     }
     else if (!strncmp(keyword, "ParamCustom", 11))
@@ -1003,39 +1016,16 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
       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"))
+    else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
     {
+      ppd_option_t     *custom_option; /* Custom option */
+
       DEBUG_puts("Processing Custom option...");
 
      /*
       * Get the option and custom option...
       */
 
-      if ((option = ppdFindOption(ppd, keyword + 6)) == NULL)
-      {
-       ppd_group_t     *gtemp;         /* Temporary group */
-
-
-        DEBUG_printf(("%s option not found for %s...\n", keyword + 6, keyword));
-
-       if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg,
-                                  encoding)) == NULL)
-       {
-         DEBUG_puts("Unable to get general group!");
-
-         goto error;
-       }
-
-       if ((option = ppd_get_option(gtemp, keyword + 6)) == NULL)
-       {
-         DEBUG_printf(("Unable to get %s option!\n", keyword + 6));
-
-          cg->ppd_status = PPD_ALLOC_ERROR;
-
-         goto error;
-       }
-      }
-
       if (!ppd_get_coption(ppd, keyword + 6))
       {
         cg->ppd_status = PPD_ALLOC_ERROR;
@@ -1043,25 +1033,31 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
        goto error;
       }
 
-     /*
-      * Add the "custom" option...
-      */
+      if (option && !strcasecmp(option->keyword, keyword + 6))
+        custom_option = option;
+      else
+        custom_option = ppdFindOption(ppd, keyword + 6);
 
-      if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+      if (custom_option)
       {
-       DEBUG_puts("Unable to add Custom choice!");
+       /*
+       * Add the "custom" option...
+       */
 
-        cg->ppd_status = PPD_ALLOC_ERROR;
+       if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
+       {
+         DEBUG_puts("Unable to add Custom choice!");
 
-       goto error;
-      }
+         cg->ppd_status = PPD_ALLOC_ERROR;
 
-      strlcpy(choice->text, text[0] ? text : _("Custom"),
-              sizeof(choice->text));
+         goto error;
+       }
 
-      choice->code = string;
-      string       = NULL;             /* Don't add as an attribute below */
-      option       = NULL;
+       strlcpy(choice->text, text[0] ? text : _("Custom"),
+               sizeof(choice->text));
+
+       choice->code = _cupsStrAlloc(string);
+      }
 
      /*
       * Now process custom page sizes specially...
@@ -1069,13 +1065,33 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
 
       if (!strcmp(keyword, "CustomPageSize"))
       {
-       ppd->variable_sizes = 1;
-
        /*
        * Add a "Custom" page size entry...
        */
 
+       ppd->variable_sizes = 1;
+
        ppd_add_size(ppd, "Custom");
+
+       if (option && !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!");
+
+           cg->ppd_status = PPD_ALLOC_ERROR;
+
+           goto error;
+         }
+
+         strlcpy(choice->text, text[0] ? text : _("Custom"),
+                 sizeof(choice->text));
+        }
       }
     }
     else if (!strcmp(keyword, "LandscapeOrientation"))
@@ -1137,7 +1153,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
     else if (!strcmp(keyword, "JobPatchFile"))
     {
       if (ppd->patches == NULL)
-        ppd->patches = strdup(string);
+        ppd->patches = _cupsStrAlloc(string);
       else
       {
         temp = realloc(ppd->patches, strlen(ppd->patches) +
@@ -1171,6 +1187,8 @@ 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)));
+
       if (name[0] == '*')
         _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
 
@@ -1254,8 +1272,35 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
 
       option->section = PPD_ORDER_ANY;
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
+
+     /*
+      * Add a custom option choice if we have already seen a CustomFoo
+      * attribute...
+      */
+
+      if (!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!");
+
+         cg->ppd_status = PPD_ALLOC_ERROR;
+
+         goto error;
+       }
+
+       strlcpy(choice->text,
+               custom_attr->text[0] ? custom_attr->text : _("Custom"),
+               sizeof(choice->text));
+        choice->code = _cupsStrAlloc(custom_attr->value);
+      }
     }
     else if (!strcmp(keyword, "JCLOpenUI"))
     {
@@ -1333,14 +1378,38 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
       option->section = PPD_ORDER_JCL;
       group = NULL;
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
+
+     /*
+      * Add a custom option choice if we have already seen a CustomFoo
+      * attribute...
+      */
+
+      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!");
+
+         cg->ppd_status = PPD_ALLOC_ERROR;
+
+         goto error;
+       }
+
+       strlcpy(choice->text,
+               custom_attr->text[0] ? custom_attr->text : _("Custom"),
+               sizeof(choice->text));
+        choice->code = _cupsStrAlloc(custom_attr->value);
+      }
     }
     else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
     {
       option = NULL;
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
     }
     else if (!strcmp(keyword, "OpenGroup"))
@@ -1387,18 +1456,17 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
       if (group == NULL)
        goto error;
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
     }
     else if (!strcmp(keyword, "CloseGroup"))
     {
       group = NULL;
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
     }
-    else if (!strcmp(keyword, "OrderDependency") ||
-             !strcmp(keyword, "NonUIOrderDependency"))
+    else if (!strcmp(keyword, "OrderDependency"))
     {
       order = (float)_cupsStrScand(string, &sptr, loc);
 
@@ -1453,7 +1521,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
        option->order   = order;
       }
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
     }
     else if (!strncmp(keyword, "Default", 7))
@@ -1523,10 +1591,10 @@ ppdOpen2(cups_file_t *fp)               /* I - File to read from */
              !strcmp(keyword, "NonUIConstraints"))
     {
       if (ppd->num_consts == 0)
-       constraint = calloc(1, sizeof(ppd_const_t));
+       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)
       {
@@ -1687,6 +1755,34 @@ 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...
       */
@@ -1709,7 +1805,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       * Don't add this one as an attribute...
       */
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
     }
     else if (!strcmp(keyword, "PaperDimension"))
@@ -1731,7 +1827,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       size->width  = (float)_cupsStrScand(string, &sptr, loc);
       size->length = (float)_cupsStrScand(sptr, NULL, loc);
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
     }
     else if (!strcmp(keyword, "ImageableArea"))
@@ -1755,7 +1851,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       size->right  = (float)_cupsStrScand(sptr, &sptr, loc);
       size->top    = (float)_cupsStrScand(sptr, NULL, loc);
 
-      ppd_free(string);
+      _cupsStrFree(string);
       string = NULL;
     }
     else if (option != NULL &&
@@ -1806,9 +1902,12 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
         (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
       ppd_add_attr(ppd, keyword, name, text, string);
     else
-      ppd_free(string);
+      _cupsStrFree(string);
   }
 
+  if (line.buffer)
+    free(line.buffer);
+
  /*
   * Reset language preferences...
   */
@@ -1816,8 +1915,8 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
   cupsLangFree(language);
 
 #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 (cg->ppd_status != PPD_OK)
@@ -1836,7 +1935,9 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
   * each choice and custom option...
   */
 
-  ppd->options = cupsArrayNew((cups_array_func_t)ppd_compare_options, NULL);
+  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;
@@ -1859,6 +1960,20 @@ 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...
+  */
+
+  ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
+
  /*
   * Return the PPD file structure...
   */
@@ -1871,7 +1986,10 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
 
   error:
 
-  ppd_free(string);
+  if (line.buffer)
+    free(line.buffer);
+
+  _cupsStrFree(string);
 
   ppdClose(ppd);
 
@@ -2152,6 +2270,40 @@ ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
 }
 
 
+/*
+ * '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 (strcmp(a->option->keyword, b->option->keyword));
+}
+
+
+/*
+ * '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.
  */
@@ -2296,7 +2448,7 @@ ppd_free_option(ppd_option_t *option)     /* I - Option to free */
          i > 0;
          i --, choice ++)
     {
-      ppd_free(choice->code);
+      _cupsStrFree(choice->code);
     }
 
     ppd_free(option->choices);
@@ -2489,6 +2641,24 @@ 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.
@@ -2496,6 +2666,7 @@ ppd_get_option(ppd_group_t *group,        /* I - Group */
 
 static int                             /* O - Bitmask of fields read */
 ppd_read(cups_file_t    *fp,           /* I - File to read from */
+         _ppd_line_t    *line,         /* I - Line buffer */
          char           *keyword,      /* O - Keyword from line */
         char           *option,        /* O - Option from line */
          char           *text,         /* O - Human-readable text from line */
@@ -2514,16 +2685,8 @@ ppd_read(cups_file_t    *fp,             /* I - File to read from */
                *optptr,                /* Option pointer */
                *textptr,               /* Text pointer */
                *strptr,                /* Pointer into string */
-               *lineptr,               /* Current position in line buffer */
-               *line;                  /* Line buffer */
-  int          linesize;               /* Current size of line buffer */
+               *lineptr;               /* Current position in line buffer */
 
- /*
-  * Range check everything...
-  */
-
-  if (!fp || !keyword || !option || !text || !string)
-    return (0);
 
  /*
   * Now loop until we have a valid line...
@@ -2532,11 +2695,15 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
   *string   = NULL;
   col       = 0;
   startline = cg->ppd_line + 1;
-  linesize  = 1024;
-  line      = malloc(linesize);
 
-  if (!line)
-    return (0);
+  if (!line->buffer)
+  {
+    line->bufsize = 1024;
+    line->buffer  = malloc(1024);
+
+    if (!line->buffer)
+      return (0);
+  }
 
   do
   {
@@ -2544,13 +2711,13 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
     * Read the line...
     */
 
-    lineptr  = line;
+    lineptr  = line->buffer;
     endquote = 0;
     colon    = 0;
 
     while ((ch = cupsFileGetChar(fp)) != EOF)
     {
-      if (lineptr >= (line + linesize - 1))
+      if (lineptr >= (line->buffer + line->bufsize - 1))
       {
        /*
         * Expand the line buffer...
@@ -2559,8 +2726,8 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
         char *temp;                    /* Temporary line pointer */
 
 
-        linesize += 1024;
-       if (linesize > 262144)
+        line->bufsize += 1024;
+       if (line->bufsize > 262144)
        {
         /*
          * Don't allow lines longer than 256k!
@@ -2569,24 +2736,20 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
           cg->ppd_line   = startline;
           cg->ppd_status = PPD_LINE_TOO_LONG;
 
-         free(line);
-
          return (0);
        }
 
-        temp = realloc(line, linesize);
+        temp = realloc(line->buffer, line->bufsize);
        if (!temp)
        {
           cg->ppd_line   = startline;
           cg->ppd_status = PPD_LINE_TOO_LONG;
 
-         free(line);
-
          return (0);
        }
 
-        lineptr = temp + (lineptr - line);
-       line    = temp;
+        lineptr      = temp + (lineptr - line->buffer);
+       line->buffer = temp;
       }
 
       if (ch == '\r' || ch == '\n')
@@ -2614,7 +2777,7 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
            cupsFileGetChar(fp);
        }
 
-       if (lineptr == line && ignoreblank)
+       if (lineptr == line->buffer && ignoreblank)
           continue;                    /* Skip blank lines */
 
        ch = '\n';
@@ -2633,8 +2796,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
         cg->ppd_line   = startline;
         cg->ppd_status = PPD_ILLEGAL_CHARACTER;
 
-        free(line);
-
         return (0);
       }
       else if (ch != 0x1a)
@@ -2655,12 +2816,10 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
           cg->ppd_line   = startline;
           cg->ppd_status = PPD_LINE_TOO_LONG;
 
-          free(line);
-
           return (0);
        }
 
-       if (ch == ':' && strncmp(line, "*%", 2) != 0)
+       if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
          colon = 1;
 
        if (ch == '\"' && colon)
@@ -2705,8 +2864,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
           cg->ppd_line   = startline;
           cg->ppd_status = PPD_ILLEGAL_CHARACTER;
 
-          free(line);
-
           return (0);
        }
        else if (ch != 0x1a)
@@ -2722,8 +2879,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
             cg->ppd_line   = startline;
             cg->ppd_status = PPD_LINE_TOO_LONG;
 
-            free(line);
-
             return (0);
          }
        }
@@ -2768,8 +2923,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
           cg->ppd_line   = startline;
           cg->ppd_status = PPD_ILLEGAL_CHARACTER;
 
-          free(line);
-
           return (0);
        }
        else if (ch != 0x1a)
@@ -2785,14 +2938,12 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
             cg->ppd_line   = startline;
             cg->ppd_status = PPD_LINE_TOO_LONG;
 
-            free(line);
-
             return (0);
          }
        }
     }
 
-    if (lineptr > line && lineptr[-1] == '\n')
+    if (lineptr > line->buffer && lineptr[-1] == '\n')
       lineptr --;
 
     *lineptr = '\0';
@@ -2806,40 +2957,34 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
     * reading the PPD when we get to the start of this data.
     */
 
-    if (!strcmp(line, "*%APLWORKSET START"))
-    {
-      free(line);
+    if (!strcmp(line->buffer, "*%APLWORKSET START"))
       return (0);
-    }
 
-    if (ch == EOF && lineptr == line)
-    {
-      free(line);
+    if (ch == EOF && lineptr == line->buffer)
       return (0);
-    }
 
    /*
     * Now parse it...
     */
 
     mask    = 0;
-    lineptr = line + 1;
+    lineptr = line->buffer + 1;
 
     keyword[0] = '\0';
     option[0]  = '\0';
     text[0]    = '\0';
     *string    = NULL;
 
-    if ((!line[0] ||                   /* Blank line */
-         !strncmp(line, "*%", 2) ||    /* Comment line */
-         !strcmp(line, "*End")) &&     /* End of multi-line string */
+    if ((!line->buffer[0] ||           /* Blank line */
+         !strncmp(line->buffer, "*%", 2) || /* Comment line */
+         !strcmp(line->buffer, "*End")) && /* End of multi-line string */
         ignoreblank)                   /* Ignore these? */
     {
       startline = cg->ppd_line + 1;
       continue;
     }
 
-    if (!strcmp(line, "*"))            /* (Bad) comment line */
+    if (!strcmp(line->buffer, "*"))    /* (Bad) comment line */
     {
       if (cg->ppd_conform == PPD_CONFORM_RELAXED)
       {
@@ -2851,34 +2996,29 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
         cg->ppd_line   = startline;
         cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
 
-        free(line);
         return (0);
       }
     }
 
-    if (line[0] != '*')                        /* All lines start with an asterisk */
+    if (line->buffer[0] != '*')                /* All lines start with an asterisk */
     {
      /*
       * Allow lines consisting of just whitespace...
       */
 
-      for (lineptr = line; *lineptr; lineptr ++)
+      for (lineptr = line->buffer; *lineptr; lineptr ++)
         if (!isspace(*lineptr & 255))
          break;
 
       if (*lineptr)
       {
         cg->ppd_status = PPD_MISSING_ASTERISK;
-        free(line);
         return (0);
       }
       else if (ignoreblank)
         continue;
       else
-      {
-        free(line);
         return (0);
-      }
     }
 
    /*
@@ -2893,7 +3033,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
           (keyptr - keyword) >= (PPD_MAX_NAME - 1))
       {
         cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
-        free(line);
        return (0);
       }
 
@@ -2927,7 +3066,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
            (optptr - option) >= (PPD_MAX_NAME - 1))
         {
           cg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
-          free(line);
          return (0);
        }
 
@@ -2939,7 +3077,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
       if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
       {
         cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
-        free(line);
        return (0);
       }
 
@@ -2966,7 +3103,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
              (textptr - text) >= (PPD_MAX_LINE - 1))
          {
            cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
-            free(line);
            return (0);
          }
 
@@ -2979,7 +3115,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
        if (textlen > PPD_MAX_TEXT && cg->ppd_conform == PPD_CONFORM_STRICT)
        {
          cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
-          free(line);
          return (0);
        }
            
@@ -2992,7 +3127,6 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
     if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
     {
       cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
-      free(line);
       return (0);
     }
 
@@ -3016,21 +3150,14 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
       if (*strptr == '\"')
       {
        /*
-        * Quoted string by itself...
+        * Quoted string by itself, remove quotes...
        */
 
-       *string = malloc(strlen(lineptr) + 1);
-
-       strptr = *string;
-
-       for (; *lineptr != '\0'; lineptr ++)
-         if (*lineptr != '\"')
-           *strptr++ = *lineptr;
-
-       *strptr = '\0';
+        *strptr = '\0';
+       lineptr ++;
       }
-      else
-        *string = strdup(lineptr);
+
+      *string = _cupsStrAlloc(lineptr);
 
 /*      DEBUG_printf(("string = \"%s\", lineptr = \"%s\"\n", *string, lineptr));*/
 
@@ -3039,12 +3166,10 @@ ppd_read(cups_file_t    *fp,            /* I - File to read from */
   }
   while (mask == 0);
 
-  free(line);
-
   return (mask);
 }
 
 
 /*
- * End of "$Id: ppd.c 6188 2007-01-10 16:23:06Z mike $".
+ * End of "$Id: ppd.c 6937 2007-09-10 21:13:31Z mike $".
  */