]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ppd.c
Remove svn:keywords since they cause svn_load_dirs.pl to complain about every file.
[thirdparty/cups.git] / cups / ppd.c
index 672ee27d3febe2722822e8af7bece94905a600ba..ff68d4c77d9a640d7b19215729604adcaaa4b52d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: ppd.c 4990 2006-01-26 02:21:45Z mike $"
+ * "$Id: ppd.c 177 2006-06-21 00:20:03Z jlovell $"
  *
  *   PPD file routines for the Common UNIX Printing System (CUPS).
  *
  *
  * Contents:
  *
- *   _ppd_attr_compare()    - Compare two attributes.
  *   ppdClose()             - Free all memory used by the PPD file.
  *   ppdErrorString()       - Returns the text assocated with a status.
+ *   _ppdGetEncoding()      - Get the CUPS encoding value for the given
+ *                            LanguageEncoding.
  *   ppdLastError()         - Return the status from the last ppdOpen*().
  *   ppdOpen()              - Read a PPD file into memory.
+ *   ppdOpen2()             - Read a PPD file into memory.
  *   ppdOpenFd()            - Read a PPD file into memory.
  *   ppdOpenFile()          - Read a PPD file into memory.
  *   ppdSetConformance()    - Set the conformance level for PPD files.
  *   ppd_add_attr()         - Add an attribute to the PPD data.
  *   ppd_add_choice()       - Add a choice to an option.
  *   ppd_add_size()         - Add a page size.
+ *   ppd_compare_attrs()    - Compare two attributes.
  *   ppd_compare_coptions() - Compare two custom options.
  *   ppd_compare_cparams()  - Compare two custom parameters.
  *   ppd_compare_options()  - Compare two options.
@@ -97,7 +100,9 @@ static ppd_attr_t    *ppd_add_attr(ppd_file_t *ppd, const char *name,
                                      const char *value);
 static ppd_choice_t    *ppd_add_choice(ppd_option_t *option, const char *name);
 static ppd_size_t      *ppd_add_size(ppd_file_t *ppd, const char *name);
-static int             ppd_compare_coptions(ppd_coption_t *a, ppd_coption_t *b);
+static int             ppd_compare_attrs(ppd_attr_t *a, ppd_attr_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);
@@ -108,33 +113,14 @@ static ppd_cparam_t       *ppd_get_cparam(ppd_coption_t *opt,
                                        const char *param,
                                        const char *text);
 static ppd_group_t     *ppd_get_group(ppd_file_t *ppd, const char *name,
-                                      const char *text, _cups_globals_t *cg);
+                                      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,
                                 _cups_globals_t *cg);
 
 
-/*
- * '_ppd_attr_compare()' - Compare two attributes.
- */
-
-int                                    /* O - Result of comparison */
-_ppd_attr_compare(ppd_attr_t **a,      /* I - First attribute */
-                  ppd_attr_t **b)      /* I - Second attribute */
-{
-  int  ret;                            /* Result of comparison */
-
-
-  if ((ret = strcasecmp((*a)->name, (*b)->name)) != 0)
-    return (ret);
-  else if ((*a)->spec[0] && (*b)->spec[0])
-    return (strcasecmp((*a)->spec, (*b)->spec));
-  else
-    return (0);
-}
-
-
 /*
  * 'ppdClose()' - Free all memory used by the PPD file.
  */
@@ -163,6 +149,8 @@ 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);
@@ -261,6 +249,8 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
     ppd_free(ppd->attrs);
   }
 
+  cupsArrayDelete(ppd->sorted_attrs);
+
  /*
   * Free custom options...
   */
@@ -279,8 +269,6 @@ ppdClose(ppd_file_t *ppd)           /* I - PPD file record */
         case PPD_CUSTOM_PASSWORD :
         case PPD_CUSTOM_STRING :
             ppd_free(cparam->current.custom_string);
-            ppd_free(cparam->minimum.custom_string);
-            ppd_free(cparam->maximum.custom_string);
            break;
 
        default :
@@ -347,6 +335,31 @@ ppdErrorString(ppd_status_t status)        /* I - PPD status */
 }
 
 
+/*
+ * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
+ *                       LanguageEncoding.
+ */
+
+cups_encoding_t                                /* O - CUPS encoding value */
+_ppdGetEncoding(const char *name)      /* I - LanguageEncoding string */
+{
+  if (!strcasecmp(name, "ISOLatin1"))
+    return (CUPS_ISO8859_1);
+  else if (!strcasecmp(name, "ISOLatin2"))
+    return (CUPS_ISO8859_2);
+  else if (!strcasecmp(name, "ISOLatin5"))
+    return (CUPS_ISO8859_5);
+  else if (!strcasecmp(name, "JIS83-RKSJ"))
+    return (CUPS_WINDOWS_932);
+  else if (!strcasecmp(name, "MacStandard"))
+    return (CUPS_MAC_ROMAN);
+  else if (!strcasecmp(name, "WindowsANSI"))
+    return (CUPS_WINDOWS_1252);
+  else
+    return (CUPS_UTF8);
+}
+
+
 /*
  * 'ppdLastError()' - Return the status from the last ppdOpen*().
  *
@@ -410,7 +423,6 @@ ppdOpen(FILE *fp)                   /* I - File to read from */
 ppd_file_t *                           /* O - PPD file record */
 ppdOpen2(cups_file_t *fp)              /* I - File to read from */
 {
-  char                 *oldlocale;     /* Old locale settings */
   int                  i, j, k;        /* Looping vars */
   int                  count;          /* Temporary count */
   ppd_file_t           *ppd;           /* PPD file record */
@@ -437,11 +449,23 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
   ppd_profile_t                *profile;       /* Pointer to color profile */
   char                 **filter;       /* Pointer to filter */
   cups_lang_t          *language;      /* Default language */
+  struct lconv         *loc;           /* Locale data */
   int                  ui_keyword;     /* Is this line a UI keyword? */
+  cups_encoding_t      encoding;       /* Encoding of PPD file */
   _cups_globals_t      *cg = _cupsGlobals();
                                        /* Global data */
   static const char * const ui_keywords[] =
                        {
+#ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
+ /*
+  * Adobe defines some 41 keywords as "UI", meaning that they are
+  * user interface elements and that they should be treated as such
+  * even if the PPD creator doesn't use Open/CloseUI around them.
+  *
+  * Since this can cause previously invisible options to appear and
+  * confuse users, the default is to only treat the PageSize and
+  * PageRegion keywords this way.
+  */
                          /* Boolean keywords */
                          "BlackSubstitution",
                          "Booklet",
@@ -486,6 +510,10 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
                          "StapleWhen",
                          "StapleX",
                          "StapleY"
+#else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
+                         "PageRegion",
+                         "PageSize"
+#endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
                        };
 
 
@@ -557,12 +585,7 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
   */
 
   language = cupsLangDefault();
-
-#ifdef LC_NUMERIC
-  oldlocale = _cupsSaveLocale(LC_NUMERIC, "C");
-#else
-  oldlocale = _cupsSaveLocale(LC_ALL, "C");
-#endif /* LC_NUMERIC */
+  loc      = localeconv();
 
  /*
   * Read lines from the PPD file and add them to the file record...
@@ -573,6 +596,7 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
   option     = NULL;
   choice     = NULL;
   ui_keyword = 0;
+  encoding   = CUPS_ISO8859_1;
 
   while ((mask = ppd_read(fp, keyword, name, text, &string, 1, cg)) != 0)
   {
@@ -651,17 +675,8 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
 
         if (!group)
        {
-          if (strcmp(keyword, "Collate") && strcmp(keyword, "Duplex") &&
-              strcmp(keyword, "InputSlot") && strcmp(keyword, "ManualFeed") &&
-              strcmp(keyword, "MediaType") && strcmp(keyword, "MediaColor") &&
-              strcmp(keyword, "MediaWeight") && strcmp(keyword, "OutputBin") &&
-              strcmp(keyword, "OutputMode") && strcmp(keyword, "OutputOrder") &&
-             strcmp(keyword, "PageSize") && strcmp(keyword, "PageRegion"))
-           group = ppd_get_group(ppd, "Extra", _("Extra"), cg);
-         else
-           group = ppd_get_group(ppd, "General", _("General"), cg);
-
-          if (group == NULL)
+          if ((group = ppd_get_group(ppd, "General", _("General"), cg,
+                                    encoding)) == NULL)
            goto error;
 
           DEBUG_printf(("Adding to group %s...\n", group->text));
@@ -724,7 +739,14 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
     if (!strcmp(keyword, "LanguageLevel"))
       ppd->language_level = atoi(string);
     else if (!strcmp(keyword, "LanguageEncoding"))
-      ppd->lang_encoding = string;
+    {
+     /*
+      * Say all PPD files are UTF-8, since we convert to UTF-8...
+      */
+
+      ppd->lang_encoding = strdup("UTF-8");
+      encoding           = _ppdGetEncoding(string);
+    }
     else if (!strcmp(keyword, "LanguageVersion"))
       ppd->lang_version = string;
     else if (!strcmp(keyword, "Manufacturer"))
@@ -736,7 +758,18 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
     else if (!strcmp(keyword, "PCFileName"))
       ppd->pcfilename = string;
     else if (!strcmp(keyword, "NickName"))
-      ppd->nickname = string;
+    {
+      if (encoding != CUPS_UTF8)
+      {
+        cups_utf8_t    utf8[256];      /* UTF-8 version of NickName */
+
+
+        cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
+       ppd->nickname = strdup((char *)utf8);
+      }
+      else
+        ppd->nickname = strdup(string);
+    }
     else if (!strcmp(keyword, "Product"))
       ppd->product = string;
     else if (!strcmp(keyword, "ShortNickName"))
@@ -785,13 +818,18 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       memset(profile, 0, sizeof(ppd_profile_t));
       strlcpy(profile->resolution, name, sizeof(profile->resolution));
       strlcpy(profile->media_type, text, sizeof(profile->media_type));
-      sscanf(string, "%f%f%f%f%f%f%f%f%f%f%f", &(profile->density),
-            &(profile->gamma),
-            profile->matrix[0] + 0, profile->matrix[0] + 1,
-            profile->matrix[0] + 2, profile->matrix[1] + 0,
-            profile->matrix[1] + 1, profile->matrix[1] + 2,
-            profile->matrix[2] + 0, profile->matrix[2] + 1,
-            profile->matrix[2] + 2);
+
+      profile->density      = _cupsStrScand(string, &sptr, loc);
+      profile->gamma        = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[0][0] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[0][1] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[0][2] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[1][0] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[1][1] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[1][2] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[2][0] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[2][1] = _cupsStrScand(sptr, &sptr, loc);
+      profile->matrix[2][2] = _cupsStrScand(sptr, &sptr, loc);
     }
     else if (!strcmp(keyword, "cupsFilter"))
     {
@@ -845,17 +883,6 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
       ppd->fonts[ppd->num_fonts] = strdup(name);
       ppd->num_fonts ++;
     }
-#if 0
-    else if (!strcmp(keyword, "ParamCustomPageSize"))
-    {
-      if (!strcmp(name, "Width"))
-        sscanf(string, "%*s%*s%f%f", ppd->custom_min + 0,
-              ppd->custom_max + 0);
-      else if (!strcmp(name, "Height"))
-        sscanf(string, "%*s%*s%f%f", ppd->custom_min + 1,
-              ppd->custom_max + 1);
-    }
-#endif /* 0 */
     else if (!strncmp(keyword, "ParamCustom", 11))
     {
       ppd_coption_t    *coption;       /* Custom option */
@@ -901,8 +928,8 @@ ppdOpen2(cups_file_t *fp)           /* I - File to read from */
       if (!strcmp(ctype, "curve"))
       {
         cparam->type = PPD_CUSTOM_CURVE;
-       cparam->minimum.custom_curve = atof(cminimum);
-       cparam->maximum.custom_curve = atof(cmaximum);
+       cparam->minimum.custom_curve = _cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_curve = _cupsStrScand(cmaximum, NULL, loc);
       }
       else if (!strcmp(ctype, "int"))
       {
@@ -913,38 +940,38 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       else if (!strcmp(ctype, "invcurve"))
       {
         cparam->type = PPD_CUSTOM_INVCURVE;
-       cparam->minimum.custom_invcurve = atof(cminimum);
-       cparam->maximum.custom_invcurve = atof(cmaximum);
+       cparam->minimum.custom_invcurve = _cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_invcurve = _cupsStrScand(cmaximum, NULL, loc);
       }
       else if (!strcmp(ctype, "passcode"))
       {
         cparam->type = PPD_CUSTOM_PASSCODE;
-       cparam->minimum.custom_passcode = strdup(cminimum);
-       cparam->maximum.custom_passcode = strdup(cmaximum);
+       cparam->minimum.custom_passcode = atoi(cminimum);
+       cparam->maximum.custom_passcode = atoi(cmaximum);
       }
       else if (!strcmp(ctype, "password"))
       {
         cparam->type = PPD_CUSTOM_PASSWORD;
-       cparam->minimum.custom_password = strdup(cminimum);
-       cparam->maximum.custom_password = strdup(cmaximum);
+       cparam->minimum.custom_password = atoi(cminimum);
+       cparam->maximum.custom_password = atoi(cmaximum);
       }
       else if (!strcmp(ctype, "points"))
       {
         cparam->type = PPD_CUSTOM_POINTS;
-       cparam->minimum.custom_points = atof(cminimum);
-       cparam->maximum.custom_points = atof(cmaximum);
+       cparam->minimum.custom_points = _cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_points = _cupsStrScand(cmaximum, NULL, loc);
       }
       else if (!strcmp(ctype, "real"))
       {
         cparam->type = PPD_CUSTOM_REAL;
-       cparam->minimum.custom_real = atof(cminimum);
-       cparam->maximum.custom_real = atof(cmaximum);
+       cparam->minimum.custom_real = _cupsStrScand(cminimum, NULL, loc);
+       cparam->maximum.custom_real = _cupsStrScand(cmaximum, NULL, loc);
       }
       else if (!strcmp(ctype, "string"))
       {
         cparam->type = PPD_CUSTOM_STRING;
-       cparam->minimum.custom_string = strdup(cminimum);
-       cparam->maximum.custom_string = strdup(cmaximum);
+       cparam->minimum.custom_string = atoi(cminimum);
+       cparam->maximum.custom_string = atoi(cmaximum);
       }
       else
       {
@@ -972,14 +999,12 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       }
     }
     else if (!strcmp(keyword, "HWMargins"))
-      sscanf(string, "%f%f%f%f", ppd->custom_margins + 0,
-             ppd->custom_margins + 1, ppd->custom_margins + 2,
-             ppd->custom_margins + 3);
+    {
+      for (i = 0, sptr = string; i < 4; i ++)
+        ppd->custom_margins[i] = _cupsStrScand(sptr, &sptr, loc);
+    }
     else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True"))
     {
-      ppd_coption_t    *coption;       /* Custom option */
-
-
       DEBUG_puts("Processing Custom option...");
 
      /*
@@ -993,7 +1018,8 @@ ppdOpen2(cups_file_t *fp)          /* I - File to read from */
 
         DEBUG_printf(("%s option not found for %s...\n", keyword + 6, keyword));
 
-       if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg)) == NULL)
+       if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg,
+                                  encoding)) == NULL)
        {
          DEBUG_puts("Unable to get general group!");
 
@@ -1010,7 +1036,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
        }
       }
 
-      if ((coption = ppd_get_coption(ppd, keyword + 6)) == NULL)
+      if (!ppd_get_coption(ppd, keyword + 6))
       {
         cg->ppd_status = PPD_ALLOC_ERROR;
 
@@ -1158,17 +1184,8 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
         option = ppd_get_option(subgroup, name);
       else if (group == NULL)
       {
-        if (strcmp(name, "Collate") && strcmp(name, "Duplex") &&
-            strcmp(name, "InputSlot") && strcmp(name, "ManualFeed") &&
-            strcmp(name, "MediaType") && strcmp(name, "MediaColor") &&
-            strcmp(name, "MediaWeight") && strcmp(name, "OutputBin") &&
-            strcmp(name, "OutputMode") && strcmp(name, "OutputOrder") &&
-           strcmp(name, "PageSize") && strcmp(name, "PageRegion"))
-         group = ppd_get_group(ppd, "Extra", _("Extra"), cg);
-       else
-         group = ppd_get_group(ppd, "General", _("General"), cg);
-
-        if (group == NULL)
+       if ((group = ppd_get_group(ppd, "General", _("General"), cg,
+                                  encoding)) == NULL)
          goto error;
 
         DEBUG_printf(("Adding to group %s...\n", group->text));
@@ -1217,7 +1234,8 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
        }
 
       if (text[0])
-        strlcpy(option->text, text, sizeof(option->text));
+        cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
+                          sizeof(option->text), encoding);
       else
       {
         if (!strcmp(name, "PageSize"))
@@ -1256,7 +1274,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       * Find the JCL group, and add if needed...
       */
 
-      group = ppd_get_group(ppd, "JCL", _("JCL"), cg);
+      group = ppd_get_group(ppd, "JCL", _("JCL"), cg, encoding);
 
       if (group == NULL)
        goto error;
@@ -1306,7 +1324,11 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
          break;
        }
 
-      strlcpy(option->text, text, sizeof(option->text));
+      if (text[0])
+        cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
+                          sizeof(option->text), encoding);
+      else
+        strlcpy(option->text, name, sizeof(option->text));
 
       option->section = PPD_ORDER_JCL;
       group = NULL;
@@ -1360,7 +1382,7 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
       * Find/add the group...
       */
 
-      group = ppd_get_group(ppd, string, sptr, cg);
+      group = ppd_get_group(ppd, string, sptr, cg, encoding);
 
       if (group == NULL)
        goto error;
@@ -1378,7 +1400,9 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
     else if (!strcmp(keyword, "OrderDependency") ||
              !strcmp(keyword, "NonUIOrderDependency"))
     {
-      if (sscanf(string, "%f%40s%40s", &order, name, keyword) != 3)
+      order = _cupsStrScand(string, &sptr, loc);
+
+      if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
       {
         cg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
 
@@ -1594,7 +1618,8 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
        goto error;
       }
 
-      sscanf(string, "%f%f", &(size->width), &(size->length));
+      size->width  = _cupsStrScand(string, &sptr, loc);
+      size->length = _cupsStrScand(sptr, NULL, loc);
 
       ppd_free(string);
       string = NULL;
@@ -1615,8 +1640,10 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
        goto error;
       }
 
-      sscanf(string, "%f%f%f%f", &(size->left), &(size->bottom),
-            &(size->right), &(size->top));
+      size->left   = _cupsStrScand(string, &sptr, loc);
+      size->bottom = _cupsStrScand(sptr, &sptr, loc);
+      size->right  = _cupsStrScand(sptr, &sptr, loc);
+      size->top    = _cupsStrScand(sptr, NULL, loc);
 
       ppd_free(string);
       string = NULL;
@@ -1644,8 +1671,9 @@ ppdOpen2(cups_file_t *fp)         /* I - File to read from */
 
       choice = ppd_add_choice(option, name);
 
-      if (mask & PPD_TEXT)
-        strlcpy(choice->text, text, sizeof(choice->text));
+      if (text[0])
+        cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
+                          sizeof(choice->text), encoding);
       else if (!strcmp(name, "True"))
         strcpy(choice->text, _("Yes"));
       else if (!strcmp(name, "False"))
@@ -1677,12 +1705,6 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
 
   cupsLangFree(language);
 
-#ifdef LC_NUMERIC
-  _cupsRestoreLocale(LC_NUMERIC, oldlocale);
-#else
-  _cupsRestoreLocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
 #ifdef DEBUG
   if (!feof(fp))
     printf("Premature EOF at %lu...\n", (unsigned long)ftell(fp));
@@ -1727,14 +1749,6 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
     }
   }
 
- /*
-  * Sort the attributes...
-  */
-
-  if (ppd->num_attrs > 1)
-    qsort(ppd->attrs, ppd->num_attrs, sizeof(ppd_attr_t *),
-          (int (*)(const void *, const void *))_ppd_attr_compare);
-
  /*
   * Return the PPD file structure...
   */
@@ -1753,12 +1767,6 @@ ppdOpen2(cups_file_t *fp)                /* I - File to read from */
 
   cupsLangFree(language);
 
-#ifdef LC_NUMERIC
-  _cupsRestoreLocale(LC_NUMERIC, oldlocale);
-#else
-  _cupsRestoreLocale(LC_ALL, oldlocale);
-#endif /* LC_NUMERIC */
-
   return (NULL);
 }
 
@@ -1902,6 +1910,14 @@ ppd_add_attr(ppd_file_t *ppd,            /* I - PPD file data */
   if (ppd == NULL || name == NULL || spec == NULL)
     return (NULL);
 
+ /*
+  * Create the array as needed...
+  */
+
+  if (!ppd->sorted_attrs)
+    ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
+                                     NULL);
+
  /*
   * Allocate memory for the new attribute...
   */
@@ -1933,6 +1949,12 @@ ppd_add_attr(ppd_file_t *ppd,            /* I - PPD file data */
   strlcpy(temp->text, text, sizeof(temp->text));
   temp->value = (char *)value;
 
+ /*
+  * Add the attribute to the sorted array...
+  */
+
+  cupsArrayAdd(ppd->sorted_attrs, temp);
+
  /*
   * Return the attribute...
   */
@@ -2002,6 +2024,26 @@ ppd_add_size(ppd_file_t *ppd,            /* I - PPD file */
 }
 
 
+/*
+ * 'ppd_compare_attrs()' - Compare two attributes.
+ */
+
+static int                             /* O - Result of comparison */
+ppd_compare_attrs(ppd_attr_t *a,       /* I - First attribute */
+                  ppd_attr_t *b)       /* I - Second attribute */
+{
+  int  ret;                            /* Result of comparison */
+
+
+  if ((ret = strcasecmp(a->name, b->name)) != 0)
+    return (ret);
+  else if (a->spec[0] && b->spec[0])
+    return (strcasecmp(a->spec, b->spec));
+  else
+    return (0);
+}
+
+
 /*
  * 'ppd_compare_coptions()' - Compare two custom options.
  */
@@ -2241,10 +2283,11 @@ ppd_get_cparam(ppd_coption_t *opt,      /* I - PPD file */
  */
 
 static ppd_group_t *                   /* O - Named group */
-ppd_get_group(ppd_file_t     *ppd,     /* I - PPD file */
-              const char     *name,    /* I - Name of group */
-             const char     *text,     /* I - Text for group */
-              _cups_globals_t *cg)     /* I - Global data */
+ppd_get_group(ppd_file_t      *ppd,    /* I - PPD file */
+              const char      *name,   /* I - Name of group */
+             const char      *text,    /* I - Text for group */
+              _cups_globals_t *cg,     /* I - Global data */
+             cups_encoding_t encoding) /* I - Encoding of text */
 {
   int          i;                      /* Looping var */
   ppd_group_t  *group;                 /* Group */
@@ -2287,7 +2330,9 @@ ppd_get_group(ppd_file_t     *ppd,        /* I - PPD file */
 
     memset(group, 0, sizeof(ppd_group_t));
     strlcpy(group->name, name, sizeof(group->name));
-    strlcpy(group->text, text, sizeof(group->text));
+
+    cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
+                      sizeof(group->text), encoding);
   }
 
   return (group);
@@ -2646,6 +2691,19 @@ ppd_read(cups_file_t    *fp,             /* I - File to read from */
 
     DEBUG_printf(("LINE = \"%s\"\n", line));
 
+   /*
+    * The dynamically created PPDs for older style Mac OS X
+    * drivers include a large blob of data inserted as comments
+    * at the end of the file.  As an optimization we can stop
+    * reading the PPD when we get to the start of this data.
+    */
+
+    if (!strcmp(line, "*%APLWORKSET START"))
+    {
+      free(line);
+      return (0);
+    }
+
     if (ch == EOF && lineptr == line)
     {
       free(line);
@@ -2880,5 +2938,5 @@ ppd_read(cups_file_t    *fp,              /* I - File to read from */
 
 
 /*
- * End of "$Id: ppd.c 4990 2006-01-26 02:21:45Z mike $".
+ * End of "$Id: ppd.c 177 2006-06-21 00:20:03Z jlovell $".
  */