]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ppd-cache.c
<rdar://problem/13655599> Seed: Print queue JOBS disappear after computer Wakes up...
[thirdparty/cups.git] / cups / ppd-cache.c
index 77a56622c6d1a4fa60edace8b90a0b61a773e191..ceae66df9098260c913339b69d760f55f395909e 100644 (file)
@@ -3,7 +3,7 @@
  *
  *   PPD cache implementation for CUPS.
  *
- *   Copyright 2010-2011 by Apple Inc.
+ *   Copyright 2010-2013 by Apple Inc.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Apple Inc. and are protected by Federal copyright
  * Local functions...
  */
 
+static int     pwg_compare_finishings(_pwg_finishings_t *a,
+                                      _pwg_finishings_t *b);
+static void    pwg_free_finishings(_pwg_finishings_t *f);
 static void    pwg_ppdize_name(const char *ipp, char *name, size_t namesize);
-static void    pwg_unppdize_name(const char *ppd, char *name, size_t namesize);
+static void    pwg_unppdize_name(const char *ppd, char *name, size_t namesize,
+                                 const char *dashchars);
 
 
 /*
@@ -84,8 +88,9 @@ _ppdCacheCreateWithFile(
 {
   cups_file_t  *fp;                    /* File */
   _ppd_cache_t *pc;                    /* PWG mapping data */
-  _pwg_size_t  *size;                  /* Current size */
-  _pwg_map_t   *map;                   /* Current map */
+  pwg_size_t   *size;                  /* Current size */
+  pwg_map_t    *map;                   /* Current map */
+  _pwg_finishings_t *finishings;       /* Current finishings option */
   int          linenum,                /* Current line number */
                num_bins,               /* Number of bins in file */
                num_sizes,              /* Number of sizes in file */
@@ -113,7 +118,7 @@ _ppdCacheCreateWithFile(
 
   if (!filename)
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
     return (NULL);
   }
 
@@ -123,7 +128,7 @@ _ppdCacheCreateWithFile(
 
   if ((fp = cupsFileOpen(filename, "r")) == NULL)
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
     return (NULL);
   }
 
@@ -133,7 +138,7 @@ _ppdCacheCreateWithFile(
 
   if (!cupsFileGets(fp, line, sizeof(line)))
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
     DEBUG_puts("_ppdCacheCreateWithFile: Unable to read first line.");
     cupsFileClose(fp);
     return (NULL);
@@ -141,7 +146,7 @@ _ppdCacheCreateWithFile(
 
   if (strncmp(line, "#CUPS-PPD-CACHE-", 16))
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
     DEBUG_printf(("_ppdCacheCreateWithFile: Wrong first line \"%s\".", line));
     cupsFileClose(fp);
     return (NULL);
@@ -149,7 +154,7 @@ _ppdCacheCreateWithFile(
 
   if (atoi(line + 16) != _PPD_CACHE_VERSION)
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, _("Out of date PPD cache file."), 1);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of date PPD cache file."), 1);
     DEBUG_printf(("_ppdCacheCreateWithFile: Cache file has version %s, "
                   "expected %d.", line + 16, _PPD_CACHE_VERSION));
     cupsFileClose(fp);
@@ -162,11 +167,13 @@ _ppdCacheCreateWithFile(
 
   if ((pc = calloc(1, sizeof(_ppd_cache_t))) == NULL)
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
     DEBUG_puts("_ppdCacheCreateWithFile: Unable to allocate _ppd_cache_t.");
     goto create_error;
   }
 
+  pc->max_copies = 9999;
+
  /*
   * Read the file...
   */
@@ -186,10 +193,10 @@ _ppdCacheCreateWithFile(
     {
       DEBUG_printf(("_ppdCacheCreateWithFile: Missing value on line %d.",
                     linenum));
-      _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
       goto create_error;
     }
-    else if (!strcasecmp(line, "Filter"))
+    else if (!_cups_strcasecmp(line, "Filter"))
     {
       if (!pc->filters)
         pc->filters = cupsArrayNew3(NULL, NULL, NULL, 0,
@@ -198,7 +205,7 @@ _ppdCacheCreateWithFile(
 
       cupsArrayAdd(pc->filters, value);
     }
-    else if (!strcasecmp(line, "PreFilter"))
+    else if (!_cups_strcasecmp(line, "PreFilter"))
     {
       if (!pc->prefilters)
         pc->prefilters = cupsArrayNew3(NULL, NULL, NULL, 0,
@@ -207,15 +214,15 @@ _ppdCacheCreateWithFile(
 
       cupsArrayAdd(pc->prefilters, value);
     }
-    else if (!strcasecmp(line, "Product"))
+    else if (!_cups_strcasecmp(line, "Product"))
     {
       pc->product = _cupsStrAlloc(value);
     }
-    else if (!strcasecmp(line, "SingleFile"))
+    else if (!_cups_strcasecmp(line, "SingleFile"))
     {
-      pc->single_file = !strcasecmp(value, "true");
+      pc->single_file = !_cups_strcasecmp(value, "true");
     }
-    else if (!strcasecmp(line, "IPP"))
+    else if (!_cups_strcasecmp(line, "IPP"))
     {
       off_t    pos = cupsFileTell(fp), /* Position in file */
                length = strtol(value, NULL, 10);
@@ -224,13 +231,13 @@ _ppdCacheCreateWithFile(
       if (attrs && *attrs)
       {
         DEBUG_puts("_ppdCacheCreateWithFile: IPP listed multiple times.");
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
       else if (length <= 0)
       {
         DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP length.");
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -243,10 +250,10 @@ _ppdCacheCreateWithFile(
         *attrs = ippNew();
 
         if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
-                     *attrs) != IPP_DATA)
+                     *attrs) != IPP_STATE_DATA)
        {
          DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
-         _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
          goto create_error;
        }
       }
@@ -262,16 +269,16 @@ _ppdCacheCreateWithFile(
       if (cupsFileTell(fp) != (pos + length))
       {
         DEBUG_puts("_ppdCacheCreateWithFile: Bad IPP data.");
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
     }
-    else if (!strcasecmp(line, "NumBins"))
+    else if (!_cups_strcasecmp(line, "NumBins"))
     {
       if (num_bins > 0)
       {
         DEBUG_puts("_ppdCacheCreateWithFile: NumBins listed multiple times.");
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -279,24 +286,24 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumBins value %d on line "
                      "%d.", num_sizes, linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
-      if ((pc->bins = calloc(num_bins, sizeof(_pwg_map_t))) == NULL)
+      if ((pc->bins = calloc(num_bins, sizeof(pwg_map_t))) == NULL)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d bins.",
                      num_sizes));
-       _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
        goto create_error;
       }
     }
-    else if (!strcasecmp(line, "Bin"))
+    else if (!_cups_strcasecmp(line, "Bin"))
     {
       if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Bin on line %d.", linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -304,7 +311,7 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Bin's on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -314,38 +321,41 @@ _ppdCacheCreateWithFile(
 
       pc->num_bins ++;
     }
-    else if (!strcasecmp(line, "NumSizes"))
+    else if (!_cups_strcasecmp(line, "NumSizes"))
     {
       if (num_sizes > 0)
       {
         DEBUG_puts("_ppdCacheCreateWithFile: NumSizes listed multiple times.");
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
-      if ((num_sizes = atoi(value)) <= 0 || num_sizes > 65536)
+      if ((num_sizes = atoi(value)) < 0 || num_sizes > 65536)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSizes value %d on line "
                      "%d.", num_sizes, linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
-      if ((pc->sizes = calloc(num_sizes, sizeof(_pwg_size_t))) == NULL)
+      if (num_sizes > 0)
       {
-        DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sizes.",
-                     num_sizes));
-       _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
-       goto create_error;
+       if ((pc->sizes = calloc(num_sizes, sizeof(pwg_size_t))) == NULL)
+       {
+         DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sizes.",
+                       num_sizes));
+         _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+         goto create_error;
+       }
       }
     }
-    else if (!strcasecmp(line, "Size"))
+    else if (!_cups_strcasecmp(line, "Size"))
     {
       if (pc->num_sizes >= num_sizes)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Size's on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -357,7 +367,7 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Size on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -366,13 +376,13 @@ _ppdCacheCreateWithFile(
 
       pc->num_sizes ++;
     }
-    else if (!strcasecmp(line, "CustomSize"))
+    else if (!_cups_strcasecmp(line, "CustomSize"))
     {
       if (pc->custom_max_width > 0)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Too many CustomSize's on line "
                      "%d.", linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -384,29 +394,29 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad CustomSize on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
-      _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
-                      pc->custom_max_width, pc->custom_max_length);
+      pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
+                       pc->custom_max_width, pc->custom_max_length, NULL);
       pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
 
-      _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
-                      pc->custom_min_width, pc->custom_min_length);
+      pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
+                       pc->custom_min_width, pc->custom_min_length, NULL);
       pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
     }
-    else if (!strcasecmp(line, "SourceOption"))
+    else if (!_cups_strcasecmp(line, "SourceOption"))
     {
       pc->source_option = _cupsStrAlloc(value);
     }
-    else if (!strcasecmp(line, "NumSources"))
+    else if (!_cups_strcasecmp(line, "NumSources"))
     {
       if (num_sources > 0)
       {
         DEBUG_puts("_ppdCacheCreateWithFile: NumSources listed multiple "
                   "times.");
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -414,25 +424,25 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumSources value %d on "
                      "line %d.", num_sources, linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
-      if ((pc->sources = calloc(num_sources, sizeof(_pwg_map_t))) == NULL)
+      if ((pc->sources = calloc(num_sources, sizeof(pwg_map_t))) == NULL)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d sources.",
                      num_sources));
-       _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
        goto create_error;
       }
     }
-    else if (!strcasecmp(line, "Source"))
+    else if (!_cups_strcasecmp(line, "Source"))
     {
       if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Source on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -440,7 +450,7 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Source's on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -450,12 +460,12 @@ _ppdCacheCreateWithFile(
 
       pc->num_sources ++;
     }
-    else if (!strcasecmp(line, "NumTypes"))
+    else if (!_cups_strcasecmp(line, "NumTypes"))
     {
       if (num_types > 0)
       {
         DEBUG_puts("_ppdCacheCreateWithFile: NumTypes listed multiple times.");
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -463,25 +473,25 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad NumTypes value %d on "
                      "line %d.", num_types, linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
-      if ((pc->types = calloc(num_types, sizeof(_pwg_map_t))) == NULL)
+      if ((pc->types = calloc(num_types, sizeof(pwg_map_t))) == NULL)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Unable to allocate %d types.",
                      num_types));
-       _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
        goto create_error;
       }
     }
-    else if (!strcasecmp(line, "Type"))
+    else if (!_cups_strcasecmp(line, "Type"))
     {
       if (sscanf(value, "%127s%40s", pwg_keyword, ppd_keyword) != 2)
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Type on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -489,7 +499,7 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Too many Type's on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -499,7 +509,7 @@ _ppdCacheCreateWithFile(
 
       pc->num_types ++;
     }
-    else if (!strcasecmp(line, "Preset"))
+    else if (!_cups_strcasecmp(line, "Preset"))
     {
      /*
       * Preset output-mode print-quality name=value ...
@@ -516,7 +526,7 @@ _ppdCacheCreateWithFile(
       {
         DEBUG_printf(("_ppdCacheCreateWithFile: Bad Preset on line %d.",
                      linenum));
-       _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
        goto create_error;
       }
 
@@ -524,14 +534,57 @@ _ppdCacheCreateWithFile(
           cupsParseOptions(valueptr, 0,
                           pc->presets[print_color_mode] + print_quality);
     }
-    else if (!strcasecmp(line, "SidesOption"))
+    else if (!_cups_strcasecmp(line, "SidesOption"))
       pc->sides_option = _cupsStrAlloc(value);
-    else if (!strcasecmp(line, "Sides1Sided"))
+    else if (!_cups_strcasecmp(line, "Sides1Sided"))
       pc->sides_1sided = _cupsStrAlloc(value);
-    else if (!strcasecmp(line, "Sides2SidedLong"))
+    else if (!_cups_strcasecmp(line, "Sides2SidedLong"))
       pc->sides_2sided_long = _cupsStrAlloc(value);
-    else if (!strcasecmp(line, "Sides2SidedShort"))
+    else if (!_cups_strcasecmp(line, "Sides2SidedShort"))
       pc->sides_2sided_short = _cupsStrAlloc(value);
+    else if (!_cups_strcasecmp(line, "Finishings"))
+    {
+      if (!pc->finishings)
+       pc->finishings =
+           cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
+                         NULL, NULL, 0, NULL,
+                         (cups_afree_func_t)pwg_free_finishings);
+
+      if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
+        goto create_error;
+
+      finishings->value       = strtol(value, &valueptr, 10);
+      finishings->num_options = cupsParseOptions(valueptr, 0,
+                                                 &(finishings->options));
+
+      cupsArrayAdd(pc->finishings, finishings);
+    }
+    else if (!_cups_strcasecmp(line, "MaxCopies"))
+      pc->max_copies = atoi(value);
+    else if (!_cups_strcasecmp(line, "ChargeInfoURI"))
+      pc->charge_info_uri = _cupsStrAlloc(value);
+    else if (!_cups_strcasecmp(line, "JobAccountId"))
+      pc->account_id = !_cups_strcasecmp(value, "true");
+    else if (!_cups_strcasecmp(line, "JobAccountingUserId"))
+      pc->accounting_user_id = !_cups_strcasecmp(value, "true");
+    else if (!_cups_strcasecmp(line, "JobPassword"))
+      pc->password = _cupsStrAlloc(value);
+    else if (!_cups_strcasecmp(line, "Mandatory"))
+    {
+      if (pc->mandatory)
+        _cupsArrayAddStrings(pc->mandatory, value, ' ');
+      else
+        pc->mandatory = _cupsArrayNewStrings(value, ' ');
+    }
+    else if (!_cups_strcasecmp(line, "SupportFile"))
+    {
+      if (!pc->support_files)
+        pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
+                                          (cups_acopy_func_t)_cupsStrAlloc,
+                                          (cups_afree_func_t)_cupsStrFree);
+
+      cupsArrayAdd(pc->support_files, value);
+    }
     else
     {
       DEBUG_printf(("_ppdCacheCreateWithFile: Unknown %s on line %d.", line,
@@ -543,7 +596,7 @@ _ppdCacheCreateWithFile(
   {
     DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sizes (%d < %d).",
                   pc->num_sizes, num_sizes));
-    _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
     goto create_error;
   }
 
@@ -551,7 +604,7 @@ _ppdCacheCreateWithFile(
   {
     DEBUG_printf(("_ppdCacheCreateWithFile: Not enough sources (%d < %d).",
                   pc->num_sources, num_sources));
-    _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
     goto create_error;
   }
 
@@ -559,7 +612,7 @@ _ppdCacheCreateWithFile(
   {
     DEBUG_printf(("_ppdCacheCreateWithFile: Not enough types (%d < %d).",
                   pc->num_types, num_types));
-    _cupsSetError(IPP_INTERNAL_ERROR, _("Bad PPD cache file."), 1);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad PPD cache file."), 1);
     goto create_error;
   }
 
@@ -601,24 +654,24 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
                        *color_model,   /* ColorModel option */
                        *duplex;        /* Duplex option */
   ppd_choice_t         *choice;        /* Current InputSlot/MediaType */
-  _pwg_map_t           *map;           /* Current source/type map */
+  pwg_map_t            *map;           /* Current source/type map */
   ppd_attr_t           *ppd_attr;      /* Current PPD preset attribute */
   int                  num_options;    /* Number of preset options and props */
   cups_option_t                *options;       /* Preset options and properties */
   ppd_size_t           *ppd_size;      /* Current PPD size */
-  _pwg_size_t          *pwg_size;      /* Current PWG size */
+  pwg_size_t           *pwg_size;      /* Current PWG size */
   char                 pwg_keyword[3 + PPD_MAX_NAME + 1 + 12 + 1 + 12 + 3],
                                        /* PWG keyword string */
                        ppd_name[PPD_MAX_NAME];
                                        /* Normalized PPD name */
   const char           *pwg_name;      /* Standard PWG media name */
-  _pwg_media_t         *pwg_media;     /* PWG media data */
+  pwg_media_t          *pwg_media;     /* PWG media data */
   _pwg_print_color_mode_t pwg_print_color_mode;
                                        /* print-color-mode index */
   _pwg_print_quality_t pwg_print_quality;
                                        /* print-quality index */
   int                  similar;        /* Are the old and new size similar? */
-  _pwg_size_t           *old_size;     /* Current old size */
+  pwg_size_t           *old_size;      /* Current old size */
   int                  old_imageable,  /* Old imageable length in 2540ths */
                        old_borderless, /* Old borderless state */
                        old_known_pwg;  /* Old PWG name is well-known */
@@ -631,8 +684,9 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)     /* I - PPD file */
                        new_imageable,  /* New imageable length in 2540ths */
                        new_borderless, /* New borderless state */
                        new_known_pwg;  /* New PWG name is well-known */
-  _pwg_size_t           *new_size;     /* New size to add, if any */
+  pwg_size_t           *new_size;      /* New size to add, if any */
   const char           *filter;        /* Current filter */
+  _pwg_finishings_t    *finishings;    /* Current finishings value */
 
 
   DEBUG_printf(("_ppdCacheCreateWithPPD(ppd=%p)", ppd));
@@ -658,143 +712,145 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
   * Copy and convert size data...
   */
 
-  if (ppd->num_sizes == 0)
-  {
-    DEBUG_puts("_ppdCacheCreateWithPPD: No page sizes in PPD.");
-    goto create_error;
-  }
-
-  if ((pc->sizes = calloc(ppd->num_sizes, sizeof(_pwg_size_t))) == NULL)
+  if (ppd->num_sizes > 0)
   {
-    DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
-                  "_pwg_size_t's.", ppd->num_sizes));
-    goto create_error;
-  }
-
-  for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
-       i > 0;
-       i --, ppd_size ++)
-  {
-   /*
-    * Don't copy over custom size...
-    */
-
-    if (!strcasecmp(ppd_size->name, "Custom"))
-      continue;
-
-   /*
-    * Convert the PPD size name to the corresponding PWG keyword name.
-    */
-
-    if ((pwg_media = _pwgMediaForPPD(ppd_size->name)) != NULL)
+    if ((pc->sizes = calloc(ppd->num_sizes, sizeof(pwg_size_t))) == NULL)
     {
-     /*
-      * Standard name, do we have conflicts?
-      */
-
-      for (j = 0; j < pc->num_sizes; j ++)
-        if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
-       {
-         pwg_media = NULL;
-         break;
-       }
+      DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
+                   "pwg_size_t's.", ppd->num_sizes));
+      goto create_error;
     }
 
-    if (pwg_media)
+    for (i = ppd->num_sizes, pwg_size = pc->sizes, ppd_size = ppd->sizes;
+        i > 0;
+        i --, ppd_size ++)
     {
      /*
-      * Standard name and no conflicts, use it!
+      * Don't copy over custom size...
       */
 
-      pwg_name      = pwg_media->pwg;
-      new_known_pwg = 1;
-    }
-    else
-    {
+      if (!_cups_strcasecmp(ppd_size->name, "Custom"))
+       continue;
+
      /*
-      * Not a standard name; convert it to a PWG vendor name of the form:
-      *
-      *     pp_lowerppd_WIDTHxHEIGHTuu
+      * Convert the PPD size name to the corresponding PWG keyword name.
       */
 
-      pwg_name      = pwg_keyword;
-      new_known_pwg = 0;
+      if ((pwg_media = pwgMediaForPPD(ppd_size->name)) != NULL)
+      {
+       /*
+       * Standard name, do we have conflicts?
+       */
 
-      pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name));
-      _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
-                      _PWG_FROMPTS(ppd_size->width),
-                      _PWG_FROMPTS(ppd_size->length));
-    }
+       for (j = 0; j < pc->num_sizes; j ++)
+         if (!strcmp(pc->sizes[j].map.pwg, pwg_media->pwg))
+         {
+           pwg_media = NULL;
+           break;
+         }
+      }
 
-   /*
-    * If we have a similar paper with non-zero margins then we only
-    * want to keep it if it has a larger imageable area length.
-    */
+      if (pwg_media)
+      {
+       /*
+       * Standard name and no conflicts, use it!
+       */
 
-    new_width      = _PWG_FROMPTS(ppd_size->width);
-    new_length     = _PWG_FROMPTS(ppd_size->length);
-    new_left       = _PWG_FROMPTS(ppd_size->left);
-    new_bottom     = _PWG_FROMPTS(ppd_size->bottom);
-    new_right      = _PWG_FROMPTS(ppd_size->width - ppd_size->right);
-    new_top        = _PWG_FROMPTS(ppd_size->length - ppd_size->top);
-    new_imageable  = new_length - new_top - new_bottom;
-    new_borderless = new_bottom == 0 && new_top == 0 &&
-                     new_left == 0 && new_right == 0;
-
-    for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
-         k > 0 && !similar;
-         k --, old_size ++)
-    {
-      old_imageable  = old_size->length - old_size->top - old_size->bottom;
-      old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
-                      old_size->right == 0 && old_size->top == 0;
-      old_known_pwg  = strncmp(old_size->map.pwg, "oe_", 3) &&
-                      strncmp(old_size->map.pwg, "om_", 3);
-
-      similar = old_borderless == new_borderless &&
-                _PWG_EQUIVALENT(old_size->width, new_width) &&
-               _PWG_EQUIVALENT(old_size->length, new_length);
-
-      if (similar &&
-          (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
+       pwg_name      = pwg_media->pwg;
+       new_known_pwg = 1;
+      }
+      else
       {
        /*
-       * The new paper has a larger imageable area so it could replace
-       * the older paper.  Regardless of the imageable area, we always
-       * prefer the size with a well-known PWG name.
+       * Not a standard name; convert it to a PWG vendor name of the form:
+       *
+       *     pp_lowerppd_WIDTHxHEIGHTuu
        */
 
-       new_size = old_size;
-       _cupsStrFree(old_size->map.ppd);
-       _cupsStrFree(old_size->map.pwg);
+       pwg_name      = pwg_keyword;
+       new_known_pwg = 0;
+
+       pwg_unppdize_name(ppd_size->name, ppd_name, sizeof(ppd_name), "_.");
+       pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), NULL, ppd_name,
+                         PWG_FROM_POINTS(ppd_size->width),
+                         PWG_FROM_POINTS(ppd_size->length), NULL);
       }
-    }
 
-    if (!similar)
-    {
      /*
-      * The paper was unique enough to deserve its own entry so add it to the
-      * end.
+      * If we have a similar paper with non-zero margins then we only want to
+      * keep it if it has a larger imageable area length.  The NULL check is for
+      * dimensions that are <= 0...
       */
 
-      new_size = pwg_size ++;
-      pc->num_sizes ++;
-    }
+      if ((pwg_media = pwgMediaForSize(PWG_FROM_POINTS(ppd_size->width),
+                                       PWG_FROM_POINTS(ppd_size->length))) == NULL)
+       continue;
 
-    if (new_size)
-    {
-     /*
-      * Save this size...
-      */
+      new_width      = pwg_media->width;
+      new_length     = pwg_media->length;
+      new_left       = PWG_FROM_POINTS(ppd_size->left);
+      new_bottom     = PWG_FROM_POINTS(ppd_size->bottom);
+      new_right      = PWG_FROM_POINTS(ppd_size->width - ppd_size->right);
+      new_top        = PWG_FROM_POINTS(ppd_size->length - ppd_size->top);
+      new_imageable  = new_length - new_top - new_bottom;
+      new_borderless = new_bottom == 0 && new_top == 0 &&
+                      new_left == 0 && new_right == 0;
+
+      for (k = pc->num_sizes, similar = 0, old_size = pc->sizes, new_size = NULL;
+          k > 0 && !similar;
+          k --, old_size ++)
+      {
+       old_imageable  = old_size->length - old_size->top - old_size->bottom;
+       old_borderless = old_size->left == 0 && old_size->bottom == 0 &&
+                        old_size->right == 0 && old_size->top == 0;
+       old_known_pwg  = strncmp(old_size->map.pwg, "oe_", 3) &&
+                        strncmp(old_size->map.pwg, "om_", 3);
+
+       similar = old_borderless == new_borderless &&
+                 _PWG_EQUIVALENT(old_size->width, new_width) &&
+                 _PWG_EQUIVALENT(old_size->length, new_length);
+
+       if (similar &&
+           (new_known_pwg || (!old_known_pwg && new_imageable > old_imageable)))
+       {
+        /*
+         * The new paper has a larger imageable area so it could replace
+         * the older paper.  Regardless of the imageable area, we always
+         * prefer the size with a well-known PWG name.
+         */
 
-      new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
-      new_size->map.pwg = _cupsStrAlloc(pwg_name);
-      new_size->width   = new_width;
-      new_size->length  = new_length;
-      new_size->left    = new_left;
-      new_size->bottom  = new_bottom;
-      new_size->right   = new_right;
-      new_size->top     = new_top;
+         new_size = old_size;
+         _cupsStrFree(old_size->map.ppd);
+         _cupsStrFree(old_size->map.pwg);
+       }
+      }
+
+      if (!similar)
+      {
+       /*
+       * The paper was unique enough to deserve its own entry so add it to the
+       * end.
+       */
+
+       new_size = pwg_size ++;
+       pc->num_sizes ++;
+      }
+
+      if (new_size)
+      {
+       /*
+       * Save this size...
+       */
+
+       new_size->map.ppd = _cupsStrAlloc(ppd_size->name);
+       new_size->map.pwg = _cupsStrAlloc(pwg_name);
+       new_size->width   = new_width;
+       new_size->length  = new_length;
+       new_size->left    = new_left;
+       new_size->bottom  = new_bottom;
+       new_size->right   = new_right;
+       new_size->top     = new_top;
+      }
     }
   }
 
@@ -804,24 +860,24 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
     * Generate custom size data...
     */
 
-    _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
-                    _PWG_FROMPTS(ppd->custom_max[0]),
-                    _PWG_FROMPTS(ppd->custom_max[1]));
+    pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "max",
+                     PWG_FROM_POINTS(ppd->custom_max[0]),
+                     PWG_FROM_POINTS(ppd->custom_max[1]), NULL);
     pc->custom_max_keyword = _cupsStrAlloc(pwg_keyword);
-    pc->custom_max_width   = _PWG_FROMPTS(ppd->custom_max[0]);
-    pc->custom_max_length  = _PWG_FROMPTS(ppd->custom_max[1]);
+    pc->custom_max_width   = PWG_FROM_POINTS(ppd->custom_max[0]);
+    pc->custom_max_length  = PWG_FROM_POINTS(ppd->custom_max[1]);
 
-    _pwgGenerateSize(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
-                    _PWG_FROMPTS(ppd->custom_min[0]),
-                    _PWG_FROMPTS(ppd->custom_min[1]));
+    pwgFormatSizeName(pwg_keyword, sizeof(pwg_keyword), "custom", "min",
+                     PWG_FROM_POINTS(ppd->custom_min[0]),
+                     PWG_FROM_POINTS(ppd->custom_min[1]), NULL);
     pc->custom_min_keyword = _cupsStrAlloc(pwg_keyword);
-    pc->custom_min_width   = _PWG_FROMPTS(ppd->custom_min[0]);
-    pc->custom_min_length  = _PWG_FROMPTS(ppd->custom_min[1]);
+    pc->custom_min_width   = PWG_FROM_POINTS(ppd->custom_min[0]);
+    pc->custom_min_length  = PWG_FROM_POINTS(ppd->custom_min[1]);
 
-    pc->custom_size.left   = _PWG_FROMPTS(ppd->custom_margins[0]);
-    pc->custom_size.bottom = _PWG_FROMPTS(ppd->custom_margins[1]);
-    pc->custom_size.right  = _PWG_FROMPTS(ppd->custom_margins[2]);
-    pc->custom_size.top    = _PWG_FROMPTS(ppd->custom_margins[3]);
+    pc->custom_size.left   = PWG_FROM_POINTS(ppd->custom_margins[0]);
+    pc->custom_size.bottom = PWG_FROM_POINTS(ppd->custom_margins[1]);
+    pc->custom_size.right  = PWG_FROM_POINTS(ppd->custom_margins[2]);
+    pc->custom_size.top    = PWG_FROM_POINTS(ppd->custom_margins[3]);
   }
 
  /*
@@ -836,10 +892,10 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
     pc->source_option = _cupsStrAlloc(input_slot->keyword);
 
     if ((pc->sources = calloc(input_slot->num_choices,
-                               sizeof(_pwg_map_t))) == NULL)
+                               sizeof(pwg_map_t))) == NULL)
     {
       DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
-                    "_pwg_map_t's for InputSlot.", input_slot->num_choices));
+                    "pwg_map_t's for InputSlot.", input_slot->num_choices));
       goto create_error;
     }
 
@@ -850,34 +906,31 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
         i > 0;
         i --, choice ++, map ++)
     {
-      if (!strncasecmp(choice->choice, "Auto", 4) ||
-          !strcasecmp(choice->choice, "Default"))
+      if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
+          !_cups_strcasecmp(choice->choice, "Default"))
         pwg_name = "auto";
-      else if (!strcasecmp(choice->choice, "Cassette"))
+      else if (!_cups_strcasecmp(choice->choice, "Cassette"))
         pwg_name = "main";
-      else if (!strcasecmp(choice->choice, "PhotoTray"))
+      else if (!_cups_strcasecmp(choice->choice, "PhotoTray"))
         pwg_name = "photo";
-      else if (!strcasecmp(choice->choice, "CDTray"))
+      else if (!_cups_strcasecmp(choice->choice, "CDTray"))
         pwg_name = "disc";
-      else if (!strncasecmp(choice->choice, "Multipurpose", 12) ||
-               !strcasecmp(choice->choice, "MP") ||
-               !strcasecmp(choice->choice, "MPTray"))
-        pwg_name = "alternate";
-      else if (!strcasecmp(choice->choice, "LargeCapacity"))
+      else if (!_cups_strncasecmp(choice->choice, "Multipurpose", 12) ||
+               !_cups_strcasecmp(choice->choice, "MP") ||
+               !_cups_strcasecmp(choice->choice, "MPTray"))
+        pwg_name = "by-pass-tray";
+      else if (!_cups_strcasecmp(choice->choice, "LargeCapacity"))
         pwg_name = "large-capacity";
-      else if (!strncasecmp(choice->choice, "Lower", 5))
+      else if (!_cups_strncasecmp(choice->choice, "Lower", 5))
         pwg_name = "bottom";
-      else if (!strncasecmp(choice->choice, "Middle", 6))
+      else if (!_cups_strncasecmp(choice->choice, "Middle", 6))
         pwg_name = "middle";
-      else if (!strncasecmp(choice->choice, "Upper", 5))
+      else if (!_cups_strncasecmp(choice->choice, "Upper", 5))
         pwg_name = "top";
-      else if (!strncasecmp(choice->choice, "Side", 4))
+      else if (!_cups_strncasecmp(choice->choice, "Side", 4))
         pwg_name = "side";
-      else if (!strcasecmp(choice->choice, "Roll") ||
-               !strcasecmp(choice->choice, "Roll1"))
+      else if (!_cups_strcasecmp(choice->choice, "Roll"))
         pwg_name = "main-roll";
-      else if (!strcasecmp(choice->choice, "Roll2"))
-        pwg_name = "alternate-roll";
       else
       {
        /*
@@ -885,7 +938,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)     /* I - PPD file */
        */
 
         pwg_name = pwg_keyword;
-       pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
+       pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
+                         "_");
       }
 
       map->pwg = _cupsStrAlloc(pwg_name);
@@ -900,10 +954,10 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
   if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
   {
     if ((pc->types = calloc(media_type->num_choices,
-                             sizeof(_pwg_map_t))) == NULL)
+                             sizeof(pwg_map_t))) == NULL)
     {
       DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
-                    "_pwg_map_t's for MediaType.", media_type->num_choices));
+                    "pwg_map_t's for MediaType.", media_type->num_choices));
       goto create_error;
     }
 
@@ -914,31 +968,33 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
         i > 0;
         i --, choice ++, map ++)
     {
-      if (!strncasecmp(choice->choice, "Auto", 4) ||
-          !strcasecmp(choice->choice, "Any") ||
-          !strcasecmp(choice->choice, "Default"))
+      if (!_cups_strncasecmp(choice->choice, "Auto", 4) ||
+          !_cups_strcasecmp(choice->choice, "Any") ||
+          !_cups_strcasecmp(choice->choice, "Default"))
         pwg_name = "auto";
-      else if (!strncasecmp(choice->choice, "Card", 4))
+      else if (!_cups_strncasecmp(choice->choice, "Card", 4))
         pwg_name = "cardstock";
-      else if (!strncasecmp(choice->choice, "Env", 3))
+      else if (!_cups_strncasecmp(choice->choice, "Env", 3))
         pwg_name = "envelope";
-      else if (!strncasecmp(choice->choice, "Gloss", 5))
+      else if (!_cups_strncasecmp(choice->choice, "Gloss", 5))
         pwg_name = "photographic-glossy";
-      else if (!strcasecmp(choice->choice, "HighGloss"))
+      else if (!_cups_strcasecmp(choice->choice, "HighGloss"))
         pwg_name = "photographic-high-gloss";
-      else if (!strcasecmp(choice->choice, "Matte"))
+      else if (!_cups_strcasecmp(choice->choice, "Matte"))
         pwg_name = "photographic-matte";
-      else if (!strncasecmp(choice->choice, "Plain", 5))
+      else if (!_cups_strncasecmp(choice->choice, "Plain", 5))
         pwg_name = "stationery";
-      else if (!strncasecmp(choice->choice, "Coated", 6))
+      else if (!_cups_strncasecmp(choice->choice, "Coated", 6))
         pwg_name = "stationery-coated";
-      else if (!strcasecmp(choice->choice, "Inkjet"))
+      else if (!_cups_strcasecmp(choice->choice, "Inkjet"))
         pwg_name = "stationery-inkjet";
-      else if (!strcasecmp(choice->choice, "Letterhead"))
+      else if (!_cups_strcasecmp(choice->choice, "Letterhead"))
         pwg_name = "stationery-letterhead";
-      else if (!strncasecmp(choice->choice, "Preprint", 8))
+      else if (!_cups_strncasecmp(choice->choice, "Preprint", 8))
         pwg_name = "stationery-preprinted";
-      else if (!strncasecmp(choice->choice, "Transparen", 10))
+      else if (!_cups_strcasecmp(choice->choice, "Recycled"))
+        pwg_name = "stationery-recycled";
+      else if (!_cups_strncasecmp(choice->choice, "Transparen", 10))
         pwg_name = "transparency";
       else
       {
@@ -947,7 +1003,8 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)    /* I - PPD file */
        */
 
         pwg_name = pwg_keyword;
-       pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
+       pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword),
+                         "_");
       }
 
       map->pwg = _cupsStrAlloc(pwg_name);
@@ -962,10 +1019,10 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)  /* I - PPD file */
   if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
   {
     if ((pc->bins = calloc(output_bin->num_choices,
-                             sizeof(_pwg_map_t))) == NULL)
+                           sizeof(pwg_map_t))) == NULL)
     {
       DEBUG_printf(("_ppdCacheCreateWithPPD: Unable to allocate %d "
-                    "_pwg_map_t's for OutputBin.", output_bin->num_choices));
+                    "pwg_map_t's for OutputBin.", output_bin->num_choices));
       goto create_error;
     }
 
@@ -976,7 +1033,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)    /* I - PPD file */
         i > 0;
         i --, choice ++, map ++)
     {
-      pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword));
+      pwg_unppdize_name(choice->choice, pwg_keyword, sizeof(pwg_keyword), "_");
 
       map->pwg = _cupsStrAlloc(pwg_keyword);
       map->ppd = _cupsStrAlloc(choice->choice);
@@ -1057,7 +1114,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
        }
        else if (color_model_val)
        {
-         if (!strcasecmp(color_model_val, "Gray"))
+         if (!_cups_strcasecmp(color_model_val, "Gray"))
            pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
          else
            pwg_print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
@@ -1200,16 +1257,16 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
          i > 0;
         i --, choice ++)
     {
-      if ((!strcasecmp(choice->choice, "None") ||
-          !strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
+      if ((!_cups_strcasecmp(choice->choice, "None") ||
+          !_cups_strcasecmp(choice->choice, "False")) && !pc->sides_1sided)
         pc->sides_1sided = _cupsStrAlloc(choice->choice);
-      else if ((!strcasecmp(choice->choice, "DuplexNoTumble") ||
-               !strcasecmp(choice->choice, "LongEdge") ||
-               !strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
+      else if ((!_cups_strcasecmp(choice->choice, "DuplexNoTumble") ||
+               !_cups_strcasecmp(choice->choice, "LongEdge") ||
+               !_cups_strcasecmp(choice->choice, "Top")) && !pc->sides_2sided_long)
         pc->sides_2sided_long = _cupsStrAlloc(choice->choice);
-      else if ((!strcasecmp(choice->choice, "DuplexTumble") ||
-               !strcasecmp(choice->choice, "ShortEdge") ||
-               !strcasecmp(choice->choice, "Bottom")) &&
+      else if ((!_cups_strcasecmp(choice->choice, "DuplexTumble") ||
+               !_cups_strcasecmp(choice->choice, "ShortEdge") ||
+               !_cups_strcasecmp(choice->choice, "Bottom")) &&
               !pc->sides_2sided_short)
         pc->sides_2sided_short = _cupsStrAlloc(choice->choice);
     }
@@ -1249,13 +1306,13 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
   for (filter = (const char *)cupsArrayFirst(pc->filters);
        filter;
        filter = (const char *)cupsArrayNext(pc->filters))
-    if (!strncasecmp(filter, "application/vnd.cups-command", 28) &&
+    if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
         _cups_isspace(filter[28]))
       break;
 
   if (!filter &&
       ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) == NULL ||
-       strcasecmp(ppd_attr->value, "none")))
+       _cups_strcasecmp(ppd_attr->value, "none")))
   {
    /*
     * No command filter and no cupsCommands keyword telling us not to use one.
@@ -1266,13 +1323,14 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd) /* I - PPD file */
     for (filter = (const char *)cupsArrayFirst(pc->filters);
         filter;
         filter = (const char *)cupsArrayNext(pc->filters))
-      if (!strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
+      if (!_cups_strncasecmp(filter, "application/vnd.cups-postscript", 31) &&
          _cups_isspace(filter[31]))
        break;
 
     if (filter)
       cupsArrayAdd(pc->filters,
-                   "application/vnd.cups-command application/postscript 0 -");
+                   "application/vnd.cups-command application/postscript 100 "
+                   "commandtops");
   }
 
   if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL)
@@ -1289,7 +1347,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
   }
 
   if ((ppd_attr = ppdFindAttr(ppd, "cupsSingleFile", NULL)) != NULL)
-    pc->single_file = !strcasecmp(ppd_attr->value, "true");
+    pc->single_file = !_cups_strcasecmp(ppd_attr->value, "true");
 
  /*
   * Copy the product string, if any...
@@ -1298,6 +1356,79 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)  /* I - PPD file */
   if (ppd->product)
     pc->product = _cupsStrAlloc(ppd->product);
 
+ /*
+  * Copy finishings mapping data...
+  */
+
+  if ((ppd_attr = ppdFindAttr(ppd, "cupsIPPFinishings", NULL)) != NULL)
+  {
+    pc->finishings = cupsArrayNew3((cups_array_func_t)pwg_compare_finishings,
+                                   NULL, NULL, 0, NULL,
+                                   (cups_afree_func_t)pwg_free_finishings);
+
+    do
+    {
+      if ((finishings = calloc(1, sizeof(_pwg_finishings_t))) == NULL)
+        goto create_error;
+
+      finishings->value       = atoi(ppd_attr->spec);
+      finishings->num_options = _ppdParseOptions(ppd_attr->value, 0,
+                                                 &(finishings->options),
+                                                 _PPD_PARSE_OPTIONS);
+
+      cupsArrayAdd(pc->finishings, finishings);
+    }
+    while ((ppd_attr = ppdFindNextAttr(ppd, "cupsIPPFinishings",
+                                       NULL)) != NULL);
+  }
+
+ /*
+  * Max copies...
+  */
+
+  if ((ppd_attr = ppdFindAttr(ppd, "cupsMaxCopies", NULL)) != NULL)
+    pc->max_copies = atoi(ppd_attr->value);
+  else if (ppd->manual_copies)
+    pc->max_copies = 1;
+  else
+    pc->max_copies = 9999;
+
+ /*
+  * cupsChargeInfoURI, cupsJobAccountId, cupsJobAccountingUserId,
+  * cupsJobPassword, and cupsMandatory.
+  */
+
+  if ((ppd_attr = ppdFindAttr(ppd, "cupsChargeInfoURI", NULL)) != NULL)
+    pc->charge_info_uri = _cupsStrAlloc(ppd_attr->value);
+
+  if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountId", NULL)) != NULL)
+    pc->account_id = !_cups_strcasecmp(ppd_attr->value, "true");
+
+  if ((ppd_attr = ppdFindAttr(ppd, "cupsJobAccountingUserId", NULL)) != NULL)
+    pc->accounting_user_id = !_cups_strcasecmp(ppd_attr->value, "true");
+
+  if ((ppd_attr = ppdFindAttr(ppd, "cupsJobPassword", NULL)) != NULL)
+    pc->password = _cupsStrAlloc(ppd_attr->value);
+
+  if ((ppd_attr = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
+    pc->mandatory = _cupsArrayNewStrings(ppd_attr->value, ' ');
+
+ /*
+  * Support files...
+  */
+
+  pc->support_files = cupsArrayNew3(NULL, NULL, NULL, 0,
+                                   (cups_acopy_func_t)_cupsStrAlloc,
+                                   (cups_afree_func_t)_cupsStrFree);
+
+  for (ppd_attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
+       ppd_attr;
+       ppd_attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
+    cupsArrayAdd(pc->support_files, ppd_attr->value);
+
+  if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL)
+    cupsArrayAdd(pc->support_files, ppd_attr->value);
+
  /*
   * Return the cache data...
   */
@@ -1310,7 +1441,7 @@ _ppdCacheCreateWithPPD(ppd_file_t *ppd)   /* I - PPD file */
 
   create_error:
 
-  _cupsSetError(IPP_INTERNAL_ERROR, _("Out of memory."), 1);
+  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Out of memory."), 1);
   _ppdCacheDestroy(pc);
 
   return (NULL);
@@ -1325,8 +1456,8 @@ void
 _ppdCacheDestroy(_ppd_cache_t *pc)     /* I - PPD cache and mapping data */
 {
   int          i;                      /* Looping var */
-  _pwg_map_t   *map;                   /* Current map */
-  _pwg_size_t  *size;                  /* Current size */
+  pwg_map_t    *map;                   /* Current map */
+  pwg_size_t   *size;                  /* Current size */
 
 
  /*
@@ -1396,6 +1527,14 @@ _ppdCacheDestroy(_ppd_cache_t *pc)       /* I - PPD cache and mapping data */
   _cupsStrFree(pc->product);
   cupsArrayDelete(pc->filters);
   cupsArrayDelete(pc->prefilters);
+  cupsArrayDelete(pc->finishings);
+
+  _cupsStrFree(pc->charge_info_uri);
+  _cupsStrFree(pc->password);
+
+  cupsArrayDelete(pc->mandatory);
+
+  cupsArrayDelete(pc->support_files);
 
   free(pc);
 }
@@ -1427,13 +1566,134 @@ _ppdCacheGetBin(
 
 
   for (i = 0; i < pc->num_bins; i ++)
-    if (!strcasecmp(output_bin, pc->bins[i].ppd))
+    if (!_cups_strcasecmp(output_bin, pc->bins[i].ppd))
       return (pc->bins[i].pwg);
 
   return (NULL);
 }
 
 
+/*
+ * '_ppdCacheGetFinishingOptions()' - Get PPD finishing options for the given
+ *                                    IPP finishings value(s).
+ */
+
+int                                    /* O  - New number of options */
+_ppdCacheGetFinishingOptions(
+    _ppd_cache_t     *pc,              /* I  - PPD cache and mapping data */
+    ipp_t            *job,             /* I  - Job attributes or NULL */
+    ipp_finishings_t value,            /* I  - IPP finishings value of IPP_FINISHINGS_NONE */
+    int              num_options,      /* I  - Number of options */
+    cups_option_t    **options)                /* IO - Options */
+{
+  int                  i;              /* Looping var */
+  _pwg_finishings_t    *f,             /* PWG finishings options */
+                       key;            /* Search key */
+  ipp_attribute_t      *attr;          /* Finishings attribute */
+  cups_option_t                *option;        /* Current finishings option */
+
+
+ /*
+  * Range check input...
+  */
+
+  if (!pc || cupsArrayCount(pc->finishings) == 0 || !options ||
+      (!job && value == IPP_FINISHINGS_NONE))
+    return (num_options);
+
+ /*
+  * Apply finishing options...
+  */
+
+  if (job && (attr = ippFindAttribute(job, "finishings", IPP_TAG_ENUM)) != NULL)
+  {
+    int        num_values = ippGetCount(attr); /* Number of values */
+
+    for (i = 0; i < num_values; i ++)
+    {
+      key.value = ippGetInteger(attr, i);
+
+      if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
+      {
+        int    j;                      /* Another looping var */
+
+        for (j = f->num_options, option = f->options; j > 0; j --, option ++)
+          num_options = cupsAddOption(option->name, option->value,
+                                      num_options, options);
+      }
+    }
+  }
+  else if (value != IPP_FINISHINGS_NONE)
+  {
+    key.value = value;
+
+    if ((f = cupsArrayFind(pc->finishings, &key)) != NULL)
+    {
+      int      j;                      /* Another looping var */
+
+      for (j = f->num_options, option = f->options; j > 0; j --, option ++)
+       num_options = cupsAddOption(option->name, option->value,
+                                   num_options, options);
+    }
+  }
+
+  return (num_options);
+}
+
+
+/*
+ * '_ppdCacheGetFinishingValues()' - Get IPP finishings value(s) from the given
+ *                                   PPD options.
+ */
+
+int                                    /* O - Number of finishings values */
+_ppdCacheGetFinishingValues(
+    _ppd_cache_t  *pc,                 /* I - PPD cache and mapping data */
+    int           num_options,         /* I - Number of options */
+    cups_option_t *options,            /* I - Options */
+    int           max_values,          /* I - Maximum number of finishings values */
+    int           *values)             /* O - Finishings values */
+{
+  int                  i,              /* Looping var */
+                       num_values = 0; /* Number of values */
+  _pwg_finishings_t    *f;             /* Current finishings option */
+  cups_option_t                *option;        /* Current option */
+  const char           *val;           /* Value for option */
+
+
+ /*
+  * Range check input...
+  */
+
+  if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
+    return (0);
+
+ /*
+  * Go through the finishings options and see what is set...
+  */
+
+  for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
+       f;
+       f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
+  {
+    for (i = f->num_options, option = f->options; i > 0; i --, option ++)
+      if ((val = cupsGetOption(option->name, num_options, options)) == NULL ||
+          _cups_strcasecmp(option->value, val))
+        break;
+
+    if (i == 0)
+    {
+      values[num_values ++] = f->value;
+
+      if (num_values >= max_values)
+        break;
+    }
+  }
+
+  return (num_values);
+}
+
+
 /*
  * '_ppdCacheGetInputSlot()' - Get the PPD InputSlot associated with the job
  *                        attributes or a keyword string.
@@ -1460,12 +1720,12 @@ _ppdCacheGetInputSlot(
 
     ipp_attribute_t    *media_col,     /* media-col attribute */
                        *media_source;  /* media-source attribute */
-    _pwg_size_t                size;           /* Dimensional size */
+    pwg_size_t         size;           /* Dimensional size */
     int                        margins_set;    /* Were the margins set? */
 
     media_col = ippFindAttribute(job, "media-col", IPP_TAG_BEGIN_COLLECTION);
     if (media_col &&
-        (media_source = ippFindAttribute(media_col->values[0].collection,
+        (media_source = ippFindAttribute(ippGetCollection(media_col, 0),
                                          "media-source",
                                         IPP_TAG_KEYWORD)) != NULL)
     {
@@ -1473,9 +1733,9 @@ _ppdCacheGetInputSlot(
       * Use the media-source value from media-col...
       */
 
-      keyword = media_source->values[0].string.text;
+      keyword = ippGetString(media_source, 0, NULL);
     }
-    else if (_pwgInitSize(&size, job, &margins_set))
+    else if (pwgInitSize(&size, job, &margins_set))
     {
      /*
       * For media <= 5x7, look for a photo tray...
@@ -1491,7 +1751,7 @@ _ppdCacheGetInputSlot(
     int        i;                              /* Looping var */
 
     for (i = 0; i < pc->num_sources; i ++)
-      if (!strcasecmp(keyword, pc->sources[i].pwg))
+      if (!_cups_strcasecmp(keyword, pc->sources[i].pwg))
         return (pc->sources[i].ppd);
   }
 
@@ -1545,7 +1805,7 @@ _ppdCacheGetMediaType(
     int        i;                              /* Looping var */
 
     for (i = 0; i < pc->num_types; i ++)
-      if (!strcasecmp(keyword, pc->types[i].pwg))
+      if (!_cups_strcasecmp(keyword, pc->types[i].pwg))
         return (pc->types[i].ppd);
   }
 
@@ -1579,7 +1839,7 @@ _ppdCacheGetOutputBin(
 
 
   for (i = 0; i < pc->num_bins; i ++)
-    if (!strcasecmp(output_bin, pc->bins[i].pwg))
+    if (!_cups_strcasecmp(output_bin, pc->bins[i].pwg))
       return (pc->bins[i].ppd);
 
   return (NULL);
@@ -1599,7 +1859,7 @@ _ppdCacheGetPageSize(
     int          *exact)               /* O - 1 if exact match, 0 otherwise */
 {
   int          i;                      /* Looping var */
-  _pwg_size_t  *size,                  /* Current size */
+  pwg_size_t   *size,                  /* Current size */
                *closest,               /* Closest size */
                jobsize;                /* Size data from job */
   int          margins_set,            /* Were the margins set? */
@@ -1667,8 +1927,8 @@ _ppdCacheGetPageSize(
       DEBUG_printf(("2_ppdCacheGetPageSize: size[%d]=[\"%s\" \"%s\"]",
                     (int)(size - pc->sizes), size->map.pwg, size->map.ppd));
 
-      if (!strcasecmp(ppd_name, size->map.ppd) ||
-          !strcasecmp(ppd_name, size->map.pwg))
+      if (!_cups_strcasecmp(ppd_name, size->map.ppd) ||
+          !_cups_strcasecmp(ppd_name, size->map.pwg))
       {
        if (exact)
          *exact = 1;
@@ -1687,7 +1947,7 @@ _ppdCacheGetPageSize(
     * media-col.
     */
 
-    if (!_pwgInitSize(&jobsize, job, &margins_set))
+    if (!pwgInitSize(&jobsize, job, &margins_set))
       return (NULL);
   }
   else
@@ -1696,12 +1956,12 @@ _ppdCacheGetPageSize(
     * Get the size using a media keyword...
     */
 
-    _pwg_media_t       *media;         /* Media definition */
+    pwg_media_t        *media;         /* Media definition */
 
 
-    if ((media = _pwgMediaForPWG(keyword)) == NULL)
-      if ((media = _pwgMediaForLegacy(keyword)) == NULL)
-        if ((media = _pwgMediaForPPD(keyword)) == NULL)
+    if ((media = pwgMediaForPWG(keyword)) == NULL)
+      if ((media = pwgMediaForLegacy(keyword)) == NULL)
+        if ((media = pwgMediaForPPD(keyword)) == NULL)
          return (NULL);
 
     jobsize.width  = media->width;
@@ -1717,8 +1977,8 @@ _ppdCacheGetPageSize(
   closest  = NULL;
   dclosest = 999999999;
 
-  if (!ppd_name || strncasecmp(ppd_name, "Custom.", 7) ||
-      strncasecmp(ppd_name, "custom_", 7))
+  if (!ppd_name || _cups_strncasecmp(ppd_name, "Custom.", 7) ||
+      _cups_strncasecmp(ppd_name, "custom_", 7))
   {
     for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
     {
@@ -1794,7 +2054,7 @@ _ppdCacheGetPageSize(
     */
 
     snprintf(pc->custom_ppd_size, sizeof(pc->custom_ppd_size), "Custom.%dx%d",
-             (int)_PWG_TOPTS(jobsize.width), (int)_PWG_TOPTS(jobsize.length));
+             (int)PWG_TO_POINTS(jobsize.width), (int)PWG_TO_POINTS(jobsize.length));
 
     if (margins_set && exact)
     {
@@ -1830,13 +2090,14 @@ _ppdCacheGetPageSize(
  * '_ppdCacheGetSize()' - Get the PWG size associated with a PPD PageSize.
  */
 
-_pwg_size_t *                          /* O - PWG size or NULL */
+pwg_size_t *                           /* O - PWG size or NULL */
 _ppdCacheGetSize(
     _ppd_cache_t *pc,                  /* I - PPD cache and mapping data */
     const char   *page_size)           /* I - PPD PageSize */
 {
-  int          i;
-  _pwg_size_t  *size;                  /* Current size */
+  int          i;                      /* Looping var */
+  pwg_media_t  *media;                 /* Media */
+  pwg_size_t   *size;                  /* Current size */
 
 
  /*
@@ -1846,7 +2107,7 @@ _ppdCacheGetSize(
   if (!pc || !page_size)
     return (NULL);
 
-  if (!strncasecmp(page_size, "Custom.", 7))
+  if (!_cups_strncasecmp(page_size, "Custom.", 7))
   {
    /*
     * Custom size; size name can be one of the following:
@@ -1872,27 +2133,27 @@ _ppdCacheGetSize(
     if (!ptr)
       return (NULL);
 
-    if (!strcasecmp(ptr, "in"))
+    if (!_cups_strcasecmp(ptr, "in"))
     {
       w *= 2540.0;
       l *= 2540.0;
     }
-    else if (!strcasecmp(ptr, "ft"))
+    else if (!_cups_strcasecmp(ptr, "ft"))
     {
       w *= 12.0 * 2540.0;
       l *= 12.0 * 2540.0;
     }
-    else if (!strcasecmp(ptr, "mm"))
+    else if (!_cups_strcasecmp(ptr, "mm"))
     {
       w *= 100.0;
       l *= 100.0;
     }
-    else if (!strcasecmp(ptr, "cm"))
+    else if (!_cups_strcasecmp(ptr, "cm"))
     {
       w *= 1000.0;
       l *= 1000.0;
     }
-    else if (!strcasecmp(ptr, "m"))
+    else if (!_cups_strcasecmp(ptr, "m"))
     {
       w *= 100000.0;
       l *= 100000.0;
@@ -1914,9 +2175,26 @@ _ppdCacheGetSize(
   */
 
   for (i = pc->num_sizes, size = pc->sizes; i > 0; i --, size ++)
-    if (!strcasecmp(page_size, size->map.ppd))
+    if (!_cups_strcasecmp(page_size, size->map.ppd) ||
+        !_cups_strcasecmp(page_size, size->map.pwg))
       return (size);
 
+ /*
+  * Look up standard sizes...
+  */
+
+  if ((media = pwgMediaForPPD(page_size)) == NULL)
+    if ((media = pwgMediaForLegacy(page_size)) == NULL)
+      media = pwgMediaForPWG(page_size);
+
+  if (media)
+  {
+    pc->custom_size.width  = media->width;
+    pc->custom_size.length = media->length;
+
+    return (&(pc->custom_size));
+  }
+
   return (NULL);
 }
 
@@ -1932,7 +2210,7 @@ _ppdCacheGetSource(
     const char   *input_slot)          /* I - PPD InputSlot */
 {
   int          i;                      /* Looping var */
-  _pwg_map_t   *source;                /* Current source */
+  pwg_map_t    *source;                /* Current source */
 
 
  /*
@@ -1943,7 +2221,7 @@ _ppdCacheGetSource(
     return (NULL);
 
   for (i = pc->num_sources, source = pc->sources; i > 0; i --, source ++)
-    if (!strcasecmp(input_slot, source->ppd))
+    if (!_cups_strcasecmp(input_slot, source->ppd))
       return (source->pwg);
 
   return (NULL);
@@ -1961,7 +2239,7 @@ _ppdCacheGetType(
     const char   *media_type)          /* I - PPD MediaType */
 {
   int          i;                      /* Looping var */
-  _pwg_map_t   *type;                  /* Current type */
+  pwg_map_t    *type;                  /* Current type */
 
 
  /*
@@ -1972,7 +2250,7 @@ _ppdCacheGetType(
     return (NULL);
 
   for (i = pc->num_types, type = pc->types; i > 0; i --, type ++)
-    if (!strcasecmp(media_type, type->ppd))
+    if (!_cups_strcasecmp(media_type, type->ppd))
       return (type->pwg);
 
   return (NULL);
@@ -1989,12 +2267,14 @@ _ppdCacheWriteFile(
     const char   *filename,            /* I - File to write */
     ipp_t        *attrs)               /* I - Attributes to write, if any */
 {
-  int          i, j, k;                /* Looping vars */
-  cups_file_t  *fp;                    /* Output file */
-  _pwg_size_t  *size;                  /* Current size */
-  _pwg_map_t   *map;                   /* Current map */
-  cups_option_t        *option;                /* Current option */
-  const char   *value;                 /* Filter/pre-filter value */
+  int                  i, j, k;        /* Looping vars */
+  cups_file_t          *fp;            /* Output file */
+  pwg_size_t           *size;          /* Current size */
+  pwg_map_t            *map;           /* Current map */
+  _pwg_finishings_t    *f;             /* Current finishing option */
+  cups_option_t                *option;        /* Current option */
+  const char           *value;         /* Filter/pre-filter value */
+  char                 newfile[1024];  /* New filename */
 
 
  /*
@@ -2003,7 +2283,7 @@ _ppdCacheWriteFile(
 
   if (!pc || !filename)
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
     return (0);
   }
 
@@ -2011,9 +2291,10 @@ _ppdCacheWriteFile(
   * Open the file and write with compression...
   */
 
-  if ((fp = cupsFileOpen(filename, "w9")) == NULL)
+  snprintf(newfile, sizeof(newfile), "%s.N", filename);
+  if ((fp = cupsFileOpen(newfile, "w9")) == NULL)
   {
-    _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
     return (0);
   }
 
@@ -2126,6 +2407,54 @@ _ppdCacheWriteFile(
 
   cupsFilePrintf(fp, "SingleFile %s\n", pc->single_file ? "true" : "false");
 
+ /*
+  * Finishing options...
+  */
+
+  for (f = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings);
+       f;
+       f = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
+  {
+    cupsFilePrintf(fp, "Finishings %d", f->value);
+    for (i = f->num_options, option = f->options; i > 0; i --, option ++)
+      cupsFilePrintf(fp, " %s=%s", option->name, option->value);
+    cupsFilePutChar(fp, '\n');
+  }
+
+ /*
+  * Max copies...
+  */
+
+  cupsFilePrintf(fp, "MaxCopies %d\n", pc->max_copies);
+
+ /*
+  * Accounting/quota/PIN/managed printing values...
+  */
+
+  if (pc->charge_info_uri)
+    cupsFilePutConf(fp, "ChargeInfoURI", pc->charge_info_uri);
+
+  cupsFilePrintf(fp, "AccountId %s\n", pc->account_id ? "true" : "false");
+  cupsFilePrintf(fp, "AccountingUserId %s\n",
+                 pc->accounting_user_id ? "true" : "false");
+
+  if (pc->password)
+    cupsFilePutConf(fp, "Password", pc->password);
+
+  for (value = (char *)cupsArrayFirst(pc->mandatory);
+       value;
+       value = (char *)cupsArrayNext(pc->mandatory))
+    cupsFilePutConf(fp, "Mandatory", value);
+
+ /*
+  * Support files...
+  */
+
+  for (value = (char *)cupsArrayFirst(pc->support_files);
+       value;
+       value = (char *)cupsArrayNext(pc->support_files))
+    cupsFilePutConf(fp, "SupportFile", value);
+
  /*
   * IPP attributes, if any...
   */
@@ -2134,7 +2463,7 @@ _ppdCacheWriteFile(
   {
     cupsFilePrintf(fp, "IPP " CUPS_LLFMT "\n", CUPS_LLCAST ippLength(attrs));
 
-    attrs->state = IPP_IDLE;
+    attrs->state = IPP_STATE_IDLE;
     ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, attrs);
   }
 
@@ -2142,7 +2471,14 @@ _ppdCacheWriteFile(
   * Close and return...
   */
 
-  return (!cupsFileClose(fp));
+  if (cupsFileClose(fp))
+  {
+    unlink(newfile);
+    return (0);
+  }
+
+  unlink(filename);
+  return (!rename(newfile, filename));
 }
 
 
@@ -2164,27 +2500,27 @@ _pwgInputSlotForSource(
   if (!media_source || !name || namesize < PPD_MAX_NAME)
     return (NULL);
 
-  if (strcasecmp(media_source, "main"))
+  if (_cups_strcasecmp(media_source, "main"))
     strlcpy(name, "Cassette", namesize);
-  else if (strcasecmp(media_source, "alternate"))
+  else if (_cups_strcasecmp(media_source, "alternate"))
     strlcpy(name, "Multipurpose", namesize);
-  else if (strcasecmp(media_source, "large-capacity"))
+  else if (_cups_strcasecmp(media_source, "large-capacity"))
     strlcpy(name, "LargeCapacity", namesize);
-  else if (strcasecmp(media_source, "bottom"))
+  else if (_cups_strcasecmp(media_source, "bottom"))
     strlcpy(name, "Lower", namesize);
-  else if (strcasecmp(media_source, "middle"))
+  else if (_cups_strcasecmp(media_source, "middle"))
     strlcpy(name, "Middle", namesize);
-  else if (strcasecmp(media_source, "top"))
+  else if (_cups_strcasecmp(media_source, "top"))
     strlcpy(name, "Upper", namesize);
-  else if (strcasecmp(media_source, "rear"))
+  else if (_cups_strcasecmp(media_source, "rear"))
     strlcpy(name, "Rear", namesize);
-  else if (strcasecmp(media_source, "side"))
+  else if (_cups_strcasecmp(media_source, "side"))
     strlcpy(name, "Side", namesize);
-  else if (strcasecmp(media_source, "envelope"))
+  else if (_cups_strcasecmp(media_source, "envelope"))
     strlcpy(name, "Envelope", namesize);
-  else if (strcasecmp(media_source, "main-roll"))
+  else if (_cups_strcasecmp(media_source, "main-roll"))
     strlcpy(name, "Roll", namesize);
-  else if (strcasecmp(media_source, "alternate-roll"))
+  else if (_cups_strcasecmp(media_source, "alternate-roll"))
     strlcpy(name, "Roll2", namesize);
   else
     pwg_ppdize_name(media_source, name, namesize);
@@ -2211,29 +2547,29 @@ _pwgMediaTypeForType(
   if (!media_type || !name || namesize < PPD_MAX_NAME)
     return (NULL);
 
-  if (strcasecmp(media_type, "auto"))
+  if (_cups_strcasecmp(media_type, "auto"))
     strlcpy(name, "Auto", namesize);
-  else if (strcasecmp(media_type, "cardstock"))
+  else if (_cups_strcasecmp(media_type, "cardstock"))
     strlcpy(name, "Cardstock", namesize);
-  else if (strcasecmp(media_type, "envelope"))
+  else if (_cups_strcasecmp(media_type, "envelope"))
     strlcpy(name, "Envelope", namesize);
-  else if (strcasecmp(media_type, "photographic-glossy"))
+  else if (_cups_strcasecmp(media_type, "photographic-glossy"))
     strlcpy(name, "Glossy", namesize);
-  else if (strcasecmp(media_type, "photographic-high-gloss"))
+  else if (_cups_strcasecmp(media_type, "photographic-high-gloss"))
     strlcpy(name, "HighGloss", namesize);
-  else if (strcasecmp(media_type, "photographic-matte"))
+  else if (_cups_strcasecmp(media_type, "photographic-matte"))
     strlcpy(name, "Matte", namesize);
-  else if (strcasecmp(media_type, "stationery"))
+  else if (_cups_strcasecmp(media_type, "stationery"))
     strlcpy(name, "Plain", namesize);
-  else if (strcasecmp(media_type, "stationery-coated"))
+  else if (_cups_strcasecmp(media_type, "stationery-coated"))
     strlcpy(name, "Coated", namesize);
-  else if (strcasecmp(media_type, "stationery-inkjet"))
+  else if (_cups_strcasecmp(media_type, "stationery-inkjet"))
     strlcpy(name, "Inkjet", namesize);
-  else if (strcasecmp(media_type, "stationery-letterhead"))
+  else if (_cups_strcasecmp(media_type, "stationery-letterhead"))
     strlcpy(name, "Letterhead", namesize);
-  else if (strcasecmp(media_type, "stationery-preprinted"))
+  else if (_cups_strcasecmp(media_type, "stationery-preprinted"))
     strlcpy(name, "Preprinted", namesize);
-  else if (strcasecmp(media_type, "transparency"))
+  else if (_cups_strcasecmp(media_type, "transparency"))
     strlcpy(name, "Transparency", namesize);
   else
     pwg_ppdize_name(media_type, name, namesize);
@@ -2248,7 +2584,7 @@ _pwgMediaTypeForType(
 
 const char *                           /* O - PageSize name */
 _pwgPageSizeForMedia(
-    _pwg_media_t *media,               /* I - Media */
+    pwg_media_t *media,                /* I - Media */
     char         *name,                        /* I - PageSize name buffer */
     size_t       namesize)             /* I - Size of name buffer */
 {
@@ -2284,8 +2620,8 @@ _pwgPageSizeForMedia(
     * Use a name of the form "wNNNhNNN"...
     */
 
-    snprintf(name, namesize, "w%dh%d", (int)_PWG_TOPTS(media->width),
-             (int)_PWG_TOPTS(media->length));
+    snprintf(name, namesize, "w%dh%d", (int)PWG_TO_POINTS(media->width),
+             (int)PWG_TO_POINTS(media->length));
   }
   else
   {
@@ -2301,6 +2637,32 @@ _pwgPageSizeForMedia(
 }
 
 
+/*
+ * 'pwg_compare_finishings()' - Compare two finishings values.
+ */
+
+static int                             /* O- Result of comparison */
+pwg_compare_finishings(
+    _pwg_finishings_t *a,              /* I - First finishings value */
+    _pwg_finishings_t *b)              /* I - Second finishings value */
+{
+  return (b->value - a->value);
+}
+
+
+/*
+ * 'pwg_free_finishings()' - Free a finishings value.
+ */
+
+static void
+pwg_free_finishings(
+    _pwg_finishings_t *f)              /* I - Finishings value */
+{
+  cupsFreeOptions(f->num_options, f->options);
+  free(f);
+}
+
+
 /*
  * 'pwg_ppdize_name()' - Convert an IPP keyword to a PPD keyword.
  */
@@ -2338,7 +2700,8 @@ pwg_ppdize_name(const char *ipp,  /* I - IPP keyword */
 static void
 pwg_unppdize_name(const char *ppd,     /* I - PPD keyword */
                  char       *name,     /* I - Name buffer */
-                  size_t     namesize) /* I - Size of name buffer */
+                  size_t     namesize, /* I - Size of name buffer */
+                  const char *dashchars)/* I - Characters to be replaced by dashes */
 {
   char *ptr,                           /* Pointer into name buffer */
        *end;                           /* End of name buffer */
@@ -2348,8 +2711,10 @@ pwg_unppdize_name(const char *ppd,       /* I - PPD keyword */
   {
     if (_cups_isalnum(*ppd) || *ppd == '-')
       *ptr++ = tolower(*ppd & 255);
-    else if (*ppd == '_' || *ppd == '.')
+    else if (strchr(dashchars, *ppd))
       *ptr++ = '-';
+    else
+      *ptr++ = *ppd;
 
     if (!_cups_isupper(*ppd) && _cups_isalnum(*ppd) &&
        _cups_isupper(ppd[1]) && ptr < end)