]> git.ipfire.org Git - thirdparty/cups-filters.git/commitdiff
Ported CUPS' PPD compiler into cups-filters
authorTill Kamppeter <till.kamppeter@gmail.com>
Mon, 25 Oct 2021 23:02:28 +0000 (01:02 +0200)
committerTill Kamppeter <till.kamppeter@gmail.com>
Mon, 25 Oct 2021 23:02:28 +0000 (01:02 +0200)
Together with PPD files the PPD compiler, which generates PPD files
from driver information files (*.drv) is deprecated in CUPS and will
get removed in CUPS 3.x.

CUPS 3.x and later (and also earlier CUPS versions when sandboxed,
like in a Snap) do not support classic CUPS drivers any more and need
Printer Applications (emulations of driverless IPP printers) for
non-driverless printers.

For retro-fitting classic CUPS drivers, consisting of PPD files, CUPS
filters, and CUPS backends, into Printer Applications without needing
to rewrite the driver (which can be old and the printer it supports
not at hand for testing) we wrap the driver into the Printer
Application without changing the driver itself (usually with the
pappl-retrofit library). Therefore we need to be able to handle PPD
files and especially also the driver information files (*.drv) which
some drivers use to generate their PPD files on-the-fly. Therefore we
conserve CUPS' PPD compiler here to be able to pre-build the PPDs (or
even also generate them on-the-fly) when retro-fitting the drivers
into Printer Applications.

In contrary to recent CUPS we provide the PPD compiler code as a
shared library (libppdc), so that other projects, like pappl-retrofit
can use it.

Note that this is only for retro-fitting existing printer drivers.
This should not motivate you to create new printer drivers using PPD
files or driver information files. Therefore we will also not add new
features to this code or to the corresponding file formats.

33 files changed:
Makefile.am
configure.ac
cupsfilters/ppdgenerator.c
cupsfilters/ppdgenerator.h
libppdc.pc.in [new file with mode: 0644]
ppdc/drv2ppd.cxx [new file with mode: 0644]
ppdc/epson.h [new file with mode: 0644]
ppdc/font.defs [new file with mode: 0644]
ppdc/hp.h [new file with mode: 0644]
ppdc/label.h [new file with mode: 0644]
ppdc/media.defs [new file with mode: 0644]
ppdc/ppdc-array.cxx [new file with mode: 0644]
ppdc/ppdc-attr.cxx [new file with mode: 0644]
ppdc/ppdc-catalog.cxx [new file with mode: 0644]
ppdc/ppdc-choice.cxx [new file with mode: 0644]
ppdc/ppdc-constraint.cxx [new file with mode: 0644]
ppdc/ppdc-driver.cxx [new file with mode: 0644]
ppdc/ppdc-file.cxx [new file with mode: 0644]
ppdc/ppdc-filter.cxx [new file with mode: 0644]
ppdc/ppdc-font.cxx [new file with mode: 0644]
ppdc/ppdc-group.cxx [new file with mode: 0644]
ppdc/ppdc-import.cxx [new file with mode: 0644]
ppdc/ppdc-mediasize.cxx [new file with mode: 0644]
ppdc/ppdc-message.cxx [new file with mode: 0644]
ppdc/ppdc-option.cxx [new file with mode: 0644]
ppdc/ppdc-private.h [new file with mode: 0644]
ppdc/ppdc-profile.cxx [new file with mode: 0644]
ppdc/ppdc-shared.cxx [new file with mode: 0644]
ppdc/ppdc-source.cxx [new file with mode: 0644]
ppdc/ppdc-string.cxx [new file with mode: 0644]
ppdc/ppdc-variable.cxx [new file with mode: 0644]
ppdc/ppdc.h [new file with mode: 0644]
ppdc/raster.defs [new file with mode: 0644]

index fef3a9514ffbe186fde0cdce5fc161a231ec2fa2..510d36ca96f04125fcbc8b25bbcdad4e1bdbb3b9 100644 (file)
@@ -5,6 +5,10 @@ pkgconf_DATA = \
        libcupsfilters.pc \
        libppd.pc \
        libfontembed.pc
+if ENABLE_LIBPPDC
+pkgconf_DATA += \
+       libppdc.pc
+endif
 
 doc_DATA = \
        ABOUT-NLS \
@@ -21,6 +25,7 @@ EXTRA_DIST = \
        ln-srf \
        libcupsfilters.pc.in \
        libppd.pc.in \
+       libppdc.pc.in \
        libfontembed.pc.in \
        utils/cups-browsed.service \
        utils/cups-browsed-upstart.conf \
@@ -41,6 +46,13 @@ EXTRA_DIST += \
        data/unclassified.ps \
        drv/custom-media-lines
 
+# =========
+# utilities
+# =========
+
+pkgutilsdir = $(bindir)
+pkgutils_PROGRAMS =
+
 # ========
 # Backends
 # ========
@@ -218,6 +230,83 @@ EXTRA_DIST += \
        ppd/test2.ppd \
        ppd/README.md
 
+# ===========================
+# PPDC legacy support library
+# ===========================
+pkgppdcincludefiles = \
+       ppdc/ppdc.h
+
+pkgppdcdefsfiles = \
+       ppdc/epson.h \
+       ppdc/hp.h \
+       ppdc/label.h \
+       ppdc/font.defs \
+       ppdc/media.defs \
+       ppdc/raster.defs
+
+if ENABLE_LIBPPDC
+pkgppdcincludedir = $(includedir)/ppdc
+pkgppdcinclude_DATA = \
+       $(pkgppdcincludefiles)
+pkgppdcdefsdir = $(datadir)/ppdc
+pkgppdcdefs_DATA = \
+       $(pkgppdcdefsfiles)
+lib_LTLIBRARIES += libppdc.la
+endif
+
+libppdc_la_SOURCES = \
+       ppdc/ppdc-array.cxx \
+       ppdc/ppdc-attr.cxx \
+       ppdc/ppdc-catalog.cxx \
+       ppdc/ppdc-choice.cxx \
+       ppdc/ppdc-constraint.cxx \
+       ppdc/ppdc-driver.cxx \
+       ppdc/ppdc-file.cxx \
+       ppdc/ppdc-filter.cxx \
+       ppdc/ppdc-font.cxx \
+       ppdc/ppdc-group.cxx \
+       ppdc/ppdc-import.cxx \
+       ppdc/ppdc-mediasize.cxx \
+       ppdc/ppdc-message.cxx \
+       ppdc/ppdc-option.cxx \
+       ppdc/ppdc-private.h \
+       ppdc/ppdc-profile.cxx \
+       ppdc/ppdc-shared.cxx \
+       ppdc/ppdc-source.cxx \
+       ppdc/ppdc-string.cxx \
+       ppdc/ppdc-variable.cxx \
+       $(pkgppdcincludefiles) \
+       $(pkgppdcdefsfiles)
+libppdc_la_LIBADD = \
+       libcupsfilters.la \
+       libppd.la \
+       $(CUPS_LIBS)
+libppdc_la_CFLAGS = \
+       -I$(srcdir)/cupsfilters/ \
+       -I$(srcdir)/ppd/ \
+       $(CUPS_CFLAGS)
+libppdc_la_LDFLAGS = \
+       -no-undefined \
+       -version-info 1
+
+EXTRA_DIST += \
+       $(pkgppdcincludefiles) \
+       $(pkgppdcdefsfiles)
+
+if ENABLE_PPDC
+pkgutils_PROGRAMS += \
+       drv2ppd
+endif
+
+drv2ppd_SOURCES = \
+       ppdc/drv2ppd.cxx
+drv2ppd_LDADD = \
+       libppdc.la \
+       $(CUPS_LIBS)
+drv2ppd_CFLAGS = \
+       -I$(srcdir)/ppdc/ \
+       $(CUPS_CFLAGS)
+
 # =================
 # Fontembed library
 # =================
@@ -1163,6 +1252,11 @@ if ENABLE_DRIVERLESS
        $(LN_SRF) $(DESTDIR)$(pkgppdgendir)/driverless-fax $(DESTDIR)$(bindir)
        $(LN_SRF) $(DESTDIR)$(pkgppdgendir)/driverless-fax $(DESTDIR)$(pkgbackenddir)
 endif
+if ENABLE_LIBPPDC
+if ENABLE_PPDC
+       $(LN_SRF) $(DESTDIR)$(bindir)/drv2ppd $(DESTDIR)$(bindir)/ppdc
+endif
+endif
 if ENABLE_BRAILLE
        $(LN_S) -f imagetobrf $(DESTDIR)$(pkgfilterdir)/imagetoubrl
        $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/svgtopdf
@@ -1210,6 +1304,11 @@ if ENABLE_DRIVERLESS
        $(RM) $(DESTDIR)$(bindir)/driverless-fax
        $(RM) $(DESTDIR)$(pkgbackenddir)/driverless-fax
 endif
+if ENABLE_LIBPPDC
+if ENABLE_PPDC
+       $(RM) $(DESTDIR)$(bindir)/ppdc
+endif
+endif
 if ENABLE_BRAILLE
        $(RM) $(DESTDIR)$(pkgfilterdir)/imagetoubrl
        $(RM) $(DESTDIR)$(pkgfilterdir)/svgtopdf
index 3b5c6081e573045c13f70333c5d2d98946675f9a..07f3ed3f1e4fdb632d133f7b9cc8cd96431430e2 100644 (file)
@@ -177,6 +177,34 @@ else
        fi
 fi
 
+AC_ARG_ENABLE([libppdc], [AS_HELP_STRING([--enable-libppdc], [enable libppdc, to handle driver information files (*.drv).])],
+        [enable_libppdc="$enableval"],
+        [enable_libppdc=yes]
+)
+AM_CONDITIONAL([ENABLE_LIBPPDC],
+[test "x$enable_libppdc" != "xno"])
+
+# Do not overwrite a ppdc which is already there from CUPS, in this case
+# default to not include a ppdc utility
+if test ! -e "$bindir/ppdc" || test -h "$bindir/ppdc"; then
+        no_cups_ppdc=yes
+else
+        no_cups_ppdc=no
+fi
+AC_ARG_ENABLE([ppdc], [AS_HELP_STRING([--enable-ppdc], [enable ppdc utility, to build PPD files from driver information files (*.drv).])],
+        [enable_ppdc="$enableval"],
+        [enable_ppdc="$no_cups_ppdc"]
+)
+if test "x$enable_libppdc" = "xno"; then
+       enable_ppdc=no
+fi
+AM_CONDITIONAL([ENABLE_PPDC],
+[test "x$enable_ppdc" != "xno"])
+
+PPDC_DATADIR="$datadir/ppdc"
+AC_DEFINE_UNQUOTED(PPDC_DATADIR, "$PPDC_DATADIR", [ppdc include dir])
+AC_SUBST(PPDC_DATADIR)
+
 AC_SEARCH_LIBS([dlopen],
        [dl],
        [AS_IF([test "$ac_cv_search_dlopen" != "none required"], [
@@ -929,6 +957,7 @@ fi
 AC_CONFIG_FILES([
        libcupsfilters.pc
        libppd.pc
+       libppdc.pc
        libfontembed.pc
        Makefile
        utils/cups-browsed
@@ -999,6 +1028,8 @@ Build configuration:
        driverless:      ${enable_driverless}
        apple-raster:    ${APPLE_RASTER_FILTER}
        pclm:            ${enable_pclm}
+       libppdc:         ${enable_libppdc}
+       ppdc:            ${enable_ppdc}
        local queue naming for remote CUPS queues: ${REMOTE_CUPS_LOCAL_QUEUE_NAMING}
        keep generated queues during shutdown:     ${SAVING_CREATED_QUEUES}
        all ipp printer auto-setup: ${enable_auto_setup_all}
index 5cd7d80de3a47201f143d4d2c4aa8f1b5c1cd4a7..8cd1c9bb3202a4a57e1c042afb05731fa9df5afc 100644 (file)
@@ -141,14 +141,14 @@ strlcpy(char       *dst,          /* O - Destination string */
 #endif /* !HAVE_STRLCPY */
 
 /*
- * 'str_formatd()' - Format a floating-point number.
+ * 'cfStrFormatd()' - Format a floating-point number.
  */
 
 char *                                 /* O - Pointer to end of string */
-str_formatd(char         *buf, /* I - String */
-                char         *bufend,  /* I - End of string buffer */
-               double       number,    /* I - Number to format */
-                struct lconv *loc)     /* I - Locale data */
+cfStrFormatd(char         *buf,        /* I - String */
+            char         *bufend,      /* I - End of string buffer */
+            double       number,       /* I - Number to format */
+            struct lconv *loc) /* I - Locale data */
 {
   char         *bufptr,                /* Pointer into buffer */
                temp[1024],             /* Temporary string */
@@ -2162,9 +2162,9 @@ cfCreatePPDFromIPP2(char         *buffer,          /* I - Filename buffer */
                   "*DefaultPageSize: %s\n", "Media Size", ppdname);
     for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
         size = (cups_size_t *)cupsArrayNext(sizes)) {
-      str_formatd(twidth, twidth + sizeof(twidth),
+      cfStrFormatd(twidth, twidth + sizeof(twidth),
                      size->width * 72.0 / 2540.0, loc);
-      str_formatd(tlength, tlength + sizeof(tlength),
+      cfStrFormatd(tlength, tlength + sizeof(tlength),
                      size->length * 72.0 / 2540.0, loc);
       strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
       if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) {
@@ -2207,9 +2207,9 @@ cfCreatePPDFromIPP2(char         *buffer,          /* I - Filename buffer */
                   "*DefaultPageRegion: %s\n", "Media Size", ppdname);
     for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
         size = (cups_size_t *)cupsArrayNext(sizes)) {
-      str_formatd(twidth, twidth + sizeof(twidth),
+      cfStrFormatd(twidth, twidth + sizeof(twidth),
                      size->width * 72.0 / 2540.0, loc);
-      str_formatd(tlength, tlength + sizeof(tlength),
+      cfStrFormatd(tlength, tlength + sizeof(tlength),
                      size->length * 72.0 / 2540.0, loc);
       strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
       if ((ippsizename = strchr(ppdsizename, ' ')) != NULL) {
@@ -2252,17 +2252,17 @@ cfCreatePPDFromIPP2(char         *buffer,          /* I - Filename buffer */
 
     for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
         size = (cups_size_t *)cupsArrayNext(sizes)) {
-      str_formatd(tleft, tleft + sizeof(tleft),
+      cfStrFormatd(tleft, tleft + sizeof(tleft),
                      size->left * 72.0 / 2540.0, loc);
-      str_formatd(tbottom, tbottom + sizeof(tbottom),
+      cfStrFormatd(tbottom, tbottom + sizeof(tbottom),
                      size->bottom * 72.0 / 2540.0, loc);
-      str_formatd(tright, tright + sizeof(tright),
+      cfStrFormatd(tright, tright + sizeof(tright),
                      (size->width - size->right) * 72.0 / 2540.0, loc);
-      str_formatd(ttop, ttop + sizeof(ttop),
+      cfStrFormatd(ttop, ttop + sizeof(ttop),
                      (size->length - size->top) * 72.0 / 2540.0, loc);
-      str_formatd(twidth, twidth + sizeof(twidth),
+      cfStrFormatd(twidth, twidth + sizeof(twidth),
                      size->width * 72.0 / 2540.0, loc);
-      str_formatd(tlength, tlength + sizeof(tlength),
+      cfStrFormatd(tlength, tlength + sizeof(tlength),
                      size->length * 72.0 / 2540.0, loc);
       strlcpy(ppdsizename, size->media, sizeof(ppdsizename));
       if ((ippsizename = strchr(ppdsizename, ' ')) != NULL)
@@ -2288,26 +2288,26 @@ cfCreatePPDFromIPP2(char         *buffer,          /* I - Filename buffer */
        min_length < INT_MAX) {
       char     tmax[256], tmin[256];   /* Min/max values */
 
-      str_formatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
-      str_formatd(tbottom, tbottom + sizeof(tbottom),
+      cfStrFormatd(tleft, tleft + sizeof(tleft), left * 72.0 / 2540.0, loc);
+      cfStrFormatd(tbottom, tbottom + sizeof(tbottom),
                      bottom * 72.0 / 2540.0, loc);
-      str_formatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0,
+      cfStrFormatd(tright, tright + sizeof(tright), right * 72.0 / 2540.0,
                      loc);
-      str_formatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
+      cfStrFormatd(ttop, ttop + sizeof(ttop), top * 72.0 / 2540.0, loc);
 
       cupsFilePrintf(fp, "*HWMargins: \"%s %s %s %s\"\n", tleft, tbottom,
                     tright, ttop);
 
-      str_formatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0,
+      cfStrFormatd(tmax, tmax + sizeof(tmax), max_width * 72.0 / 2540.0,
                      loc);
-      str_formatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0,
+      cfStrFormatd(tmin, tmin + sizeof(tmin), min_width * 72.0 / 2540.0,
                      loc);
       cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s\n", tmin,
                     tmax);
 
-      str_formatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0,
+      cfStrFormatd(tmax, tmax + sizeof(tmax), max_length * 72.0 / 2540.0,
                      loc);
-      str_formatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0,
+      cfStrFormatd(tmin, tmin + sizeof(tmin), min_length * 72.0 / 2540.0,
                      loc);
       cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s\n", tmin,
                     tmax);
index 3c47cba2620b39d122da47b28f6b55d397d1fc6c..f163c465b9214b7747e9e12ce8a09fabe7b324d4 100644 (file)
@@ -69,6 +69,8 @@ char            *cfCreatePPDFromIPP2(char *buffer, size_t bufsize,
                                     cups_array_t *sizes,char* default_pagesize,
                                     const char *default_cluster_color,
                                     char *status_msg, size_t status_msg_size);
+char            *cfStrFormatd(char *buf, char *bufend, double number,
+                             struct lconv *loc);
 int             cfCompareResolutions(void *resolution_a, void *resolution_b,
                                     void *user_data);
 void            *cfCopyResolution(void *resolution, void *user_data);
diff --git a/libppdc.pc.in b/libppdc.pc.in
new file mode 100644 (file)
index 0000000..8bd44b7
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+                                                                       
+Name: libppdc
+Description: Library for generating PPD files from *.drv files
+Version: @VERSION@
+
+Libs: -L${libdir} -lppdc
+Libs.private: @CUPS_LIBS@ -lcupsfilters -lppd
+Cflags: -I${includedir}/ppdc
diff --git a/ppdc/drv2ppd.cxx b/ppdc/drv2ppd.cxx
new file mode 100644 (file)
index 0000000..394e04d
--- /dev/null
@@ -0,0 +1,464 @@
+//
+// PPD file compiler main entry for the CUPS PPD Compiler.
+//
+// Copyright 2007-2014 by Apple Inc.
+// Copyright 2002-2007 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#include <cups/array.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+//
+// Local globals...
+//
+
+const char *progname;
+
+
+//
+// Local functions...
+//
+
+static void    usage(void);
+
+
+//
+// 'main()' - Main entry for the PPD compiler.
+//
+
+int                                    // O - Exit status
+main(int  argc,                                // I - Number of command-line arguments
+     char *argv[])                     // I - Command-line arguments
+{
+  int                  i, j;           // Looping vars
+  ppdcCatalog          *catalog;       // Message catalog
+  const char           *outdir;        // Output directory
+  ppdcSource           *src;           // PPD source file data
+  ppdcDriver           *d;             // Current driver
+  cups_file_t          *fp;            // PPD file
+  char                 *opt,           // Current option
+                       *value,         // Value in option
+                       *outname,       // Output filename
+                       make_model[1024],
+                                       // Make and model
+                       pcfilename[1024],
+                                       // Lowercase pcfilename
+                       filename[2048]; // PPD filename
+  int                  comp,           // Compress
+                       do_test,        // Test PPD files
+                       single_language,// Generate single-language files
+                       use_model_name, // Use ModelName for filename
+                       verbose;        // Verbosity
+  ppdcLineEnding       le;             // Line ending to use
+  ppdcArray            *locales;       // List of locales
+  cups_array_t         *filenames;     // List of generated filenames
+
+
+  // Scan the command-line...
+  catalog         = NULL;
+  comp            = 0;
+  do_test         = 0;
+  le              = PPDC_LFONLY;
+  locales         = NULL;
+  outdir          = "ppd";
+  single_language = 0;
+  src             = new ppdcSource();
+  use_model_name  = 0;
+  verbose         = 0;
+  filenames       = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
+
+  progname        = strrchr(argv[0], '/');
+  if (progname)
+    progname ++;
+  else
+    progname = argv[0];
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+          case 'D' :                   // Define variable
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if ((value = strchr(argv[i], '=')) != NULL)
+             {
+               *value++ = '\0';
+
+               src->set_variable(argv[i], value);
+             }
+             else
+               src->set_variable(argv[i], "1");
+              break;
+
+          case 'I' :                   // Include directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (verbose > 1)
+               fprintf(stdout,
+                       _("%s: Adding include directory \"%s\".\n"),
+                       progname, argv[i]);
+
+             ppdcSource::add_include(argv[i]);
+             break;
+
+         case 'c' :                    // Message catalog...
+             i ++;
+              if (i >= argc)
+                usage();
+
+              if (verbose > 1)
+               fprintf(stdout,
+                       _("%s: Loading messages from \"%s\".\n"),
+                       progname, argv[i]);
+
+              if (!catalog)
+               catalog = new ppdcCatalog("en");
+
+              if (catalog->load_messages(argv[i]))
+             {
+               fprintf(stderr,
+                       _("%s: Unable to load localization file "
+                         "\"%s\" - %s\n"), progname, argv[i], strerror(errno));
+                return (1);
+             }
+             break;
+
+          case 'd' :                   // Output directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (verbose > 1)
+               fprintf(stdout,
+                       _("%s: Writing PPD files to directory "
+                         "\"%s\".\n"), progname, argv[i]);
+
+             outdir = argv[i];
+             break;
+
+          case 'l' :                   // Language(s)...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (strchr(argv[i], ','))
+             {
+               // Comma-delimited list of languages...
+               char    temp[1024],     // Copy of language list
+                       *start,         // Start of current locale name
+                       *end;           // End of current locale name
+
+
+               locales = new ppdcArray();
+
+               strncpy(temp, argv[i], sizeof(temp) - 1);
+               for (start = temp; *start; start = end)
+               {
+                 if ((end = strchr(start, ',')) != NULL)
+                   *end++ = '\0';
+                 else
+                   end = start + strlen(start);
+
+                  if (end > start)
+                   locales->add(new ppdcString(start));
+               }
+             }
+             else
+             {
+               single_language = 1;
+
+               if (verbose > 1)
+                 fprintf(stdout,
+                         _("%s: Loading messages for locale "
+                           "\"%s\".\n"), progname, argv[i]);
+
+               if (catalog)
+                 catalog->release();
+
+               catalog = new ppdcCatalog(argv[i]);
+
+               if (catalog->messages->count == 0 && strcmp(argv[i], "en"))
+               {
+                 fprintf(stderr,
+                         _("%s: Unable to find localization for "
+                           "\"%s\" - %s\n"), progname, argv[i],
+                         strerror(errno));
+                  return (1);
+               }
+             }
+             break;
+
+          case 'm' :                   // Use ModelName for filename
+             use_model_name = 1;
+             break;
+
+          case 't' :                   // Test PPDs instead of generating them
+             do_test = 1;
+             break;
+
+          case 'v' :                   // Be verbose...
+             verbose ++;
+             break;
+
+          case 'z' :                   // Compress files...
+             comp = 1;
+             break;
+
+         case '-' :                    // --option
+             if (!strcmp(opt, "-lf"))
+             {
+               le  = PPDC_LFONLY;
+               opt += strlen(opt) - 1;
+               break;
+             }
+             else if (!strcmp(opt, "-cr"))
+             {
+               le  = PPDC_CRONLY;
+               opt += strlen(opt) - 1;
+               break;
+             }
+             else if (!strcmp(opt, "-crlf"))
+             {
+               le  = PPDC_CRLF;
+               opt += strlen(opt) - 1;
+               break;
+             }
+
+         default :                     // Unknown
+             usage();
+       }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      if (verbose > 1)
+        fprintf(stdout,
+               _("%s: Loading driver information file \"%s\".\n"),
+               progname, argv[i]);
+
+      src->read_file(argv[i]);
+    }
+
+
+  if (src->drivers->count > 0)
+  {
+    // Create the output directory...
+    if (mkdir(outdir, 0777))
+    {
+      if (errno != EEXIST)
+      {
+       fprintf(stderr,
+               _("%s: Unable to create output directory %s: %s\n"),
+               progname, outdir, strerror(errno));
+        return (1);
+      }
+    }
+
+    // Write PPD files...
+    for (d = (ppdcDriver *)src->drivers->first();
+         d;
+        d = (ppdcDriver *)src->drivers->next())
+    {
+      if (do_test)
+      {
+        // Test the PPD file for this driver...
+       int     pid,                    // Process ID
+               fds[2];                 // Pipe file descriptors
+
+
+        if (pipe(fds))
+       {
+         fprintf(stderr,
+                 _("%s: Unable to create output pipes: %s\n"),
+                 progname, strerror(errno));
+         return (1);
+       }
+
+       if ((pid = fork()) == 0)
+       {
+         // Child process comes here...
+         dup2(fds[0], 0);
+
+         close(fds[0]);
+         close(fds[1]);
+
+         execlp("cupstestppd", "cupstestppd", "-", (char *)0);
+
+         fprintf(stderr,
+                 _("%s: Unable to execute cupstestppd: %s\n"),
+                 progname, strerror(errno));
+         return (errno);
+       }
+       else if (pid < 0)
+       {
+         fprintf(stderr, _("%s: Unable to execute cupstestppd: %s\n"),
+                 progname, strerror(errno));
+         return (errno);
+       }
+
+       close(fds[0]);
+       fp = cupsFileOpenFd(fds[1], "w");
+      }
+      else
+      {
+       // Write the PPD file for this driver...
+       if (use_model_name)
+       {
+         if (!strncasecmp(d->model_name->value, d->manufacturer->value,
+                          strlen(d->manufacturer->value)))
+         {
+           // Model name already starts with the manufacturer...
+            outname = d->model_name->value;
+         }
+         else
+         {
+           // Add manufacturer to the front of the model name...
+           snprintf(make_model, sizeof(make_model), "%s %s",
+                    d->manufacturer->value, d->model_name->value);
+           outname = make_model;
+         }
+       }
+       else if (d->file_name)
+         outname = d->file_name->value;
+       else
+         outname = d->pc_file_name->value;
+
+       if (strstr(outname, ".PPD"))
+       {
+         // Convert PCFileName to lowercase...
+         for (j = 0;
+              outname[j] && j < (int)(sizeof(pcfilename) - 1);
+              j ++)
+           pcfilename[j] = (char)tolower(outname[j] & 255);
+
+         pcfilename[j] = '\0';
+       }
+       else
+       {
+         // Leave PCFileName as-is...
+         strncpy(pcfilename, outname, sizeof(pcfilename));
+       }
+
+       // Open the PPD file for writing...
+       if (comp)
+         snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename);
+       else
+         snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename);
+
+        if (cupsArrayFind(filenames, filename))
+         fprintf(stderr,
+                 _("%s: Warning - overlapping filename \"%s\".\n"),
+                 progname, filename);
+       else
+         cupsArrayAdd(filenames, strdup(filename));
+
+       fp = cupsFileOpen(filename, comp ? "w9" : "w");
+       if (!fp)
+       {
+         fprintf(stderr,
+                 _("%s: Unable to create PPD file \"%s\" - %s.\n"),
+                 progname, filename, strerror(errno));
+         return (1);
+       }
+
+       if (verbose)
+         fprintf(stdout, _("%s: Writing %s.\n"), progname, filename);
+      }
+
+     /*
+      * Write the PPD file...
+      */
+
+      ppdcArray *templocales = locales;
+
+      if (!templocales && !single_language)
+      {
+       templocales = new ppdcArray();
+       for (ppdcCatalog *tempcatalog = (ppdcCatalog *)src->po_files->first();
+            tempcatalog;
+            tempcatalog = (ppdcCatalog *)src->po_files->next())
+       {
+         tempcatalog->locale->retain();
+         templocales->add(tempcatalog->locale);
+       }
+      }
+
+      if (d->write_ppd_file(fp, catalog, templocales, src, le))
+      {
+       cupsFileClose(fp);
+       return (1);
+      }
+
+      if (templocales && templocales != locales)
+        templocales->release();
+
+      cupsFileClose(fp);
+    }
+  }
+  else
+    usage();
+
+  // Delete the printer driver information...
+  src->release();
+
+  // Message catalog...
+  if (catalog)
+    catalog->release();
+
+  // Return with no errors.
+  return (0);
+}
+
+
+//
+// 'usage()' - Show usage and exit.
+//
+
+static void
+usage(void)
+{
+  fprintf(stdout, _("Usage: %s [options] filename.drv [ ... "
+                   "filenameN.drv ]\n"), progname);
+  fprintf(stdout, _("Options:\n"));
+  fprintf(stdout, _("  -D name=value           Set named variable to "
+                   "value.\n"));
+  fprintf(stdout, _("  -I include-dir          Add include directory to "
+                   "search path.\n"));
+  fprintf(stdout, _("  -c catalog.po           Load the specified "
+                   "message catalog.\n"));
+  fprintf(stdout, _("  -d output-dir           Specify the output "
+                   "directory.\n"));
+  fprintf(stdout, _("  -l lang[,lang,...]      Specify the output "
+                   "language(s) (locale).\n"));
+  fprintf(stdout, _("  -m                      Use the ModelName value "
+                   "as the filename.\n"));
+  fprintf(stdout, _("  -t                      Test PPDs instead of "
+                   "generating them.\n"));
+  fprintf(stdout, _("  -v                      Be verbose.\n"));
+  fprintf(stdout, _("  -z                      Compress PPD files using "
+                   "GNU zip.\n"));
+  fprintf(stdout, _("  --cr                    End lines with CR (Mac "
+                   "OS 9).\n"));
+  fprintf(stdout, _("  --crlf                  End lines with CR + LF "
+                   "(Windows).\n"));
+  fprintf(stdout, _("  --lf                    End lines with LF "
+                   "(UNIX/Linux/macOS).\n"));
+
+  exit(1);
+}
diff --git a/ppdc/epson.h b/ppdc/epson.h
new file mode 100644 (file)
index 0000000..8a3641e
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * This file contains model number definitions for the CUPS sample
+ * ESC/P driver.
+ *
+ * Copyright 2007 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
+ *
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+ */
+
+#define EPSON_9PIN     0               /* 9-pin dot matrix */
+#define EPSON_24PIN    1               /* 24-pin dot matrix */
+#define EPSON_COLOR    2               /* Epson Stylus Color with ESC . */
+#define EPSON_PHOTO    3               /* Epson Stylus Photo with ESC . */
+#define EPSON_ICOLOR   4               /* Epson Stylus Color with ESC i */
+#define EPSON_IPHOTO   5               /* Epson Stylus Photo with ESC i */
diff --git a/ppdc/font.defs b/ppdc/font.defs
new file mode 100644 (file)
index 0000000..519a8e1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Standard font definitions for the CUPS PPD file compiler.
+ *
+ * Copyright © 2007 by Apple Inc.
+ * Copyright © 1997-2005 by Easy Software Products.
+ *
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
+ */
+
+#font AvantGarde-Book Standard "(1.05)" Standard ROM
+#font AvantGarde-BookOblique Standard "(1.05)" Standard ROM
+#font AvantGarde-Demi Standard "(1.05)" Standard ROM
+#font AvantGarde-DemiOblique Standard "(1.05)" Standard ROM
+#font Bookman-Demi Standard "(1.05)" Standard ROM
+#font Bookman-DemiItalic Standard "(1.05)" Standard ROM
+#font Bookman-Light Standard "(1.05)" Standard ROM
+#font Bookman-LightItalic Standard "(1.05)" Standard ROM
+#font Courier Standard "(1.05)" Standard ROM
+#font Courier-Bold Standard "(1.05)" Standard ROM
+#font Courier-BoldOblique Standard "(1.05)" Standard ROM
+#font Courier-Oblique Standard "(1.05)" Standard ROM
+#font Helvetica Standard "(1.05)" Standard ROM
+#font Helvetica-Bold Standard "(1.05)" Standard ROM
+#font Helvetica-BoldOblique Standard "(1.05)" Standard ROM
+#font Helvetica-Narrow Standard "(1.05)" Standard ROM
+#font Helvetica-Narrow-Bold Standard "(1.05)" Standard ROM
+#font Helvetica-Narrow-BoldOblique Standard "(1.05)" Standard ROM
+#font Helvetica-Narrow-Oblique Standard "(1.05)" Standard ROM
+#font Helvetica-Oblique Standard "(1.05)" Standard ROM
+#font NewCenturySchlbk-Bold Standard "(1.05)" Standard ROM
+#font NewCenturySchlbk-BoldItalic Standard "(1.05)" Standard ROM
+#font NewCenturySchlbk-Italic Standard "(1.05)" Standard ROM
+#font NewCenturySchlbk-Roman Standard "(1.05)" Standard ROM
+#font Palatino-Bold Standard "(1.05)" Standard ROM
+#font Palatino-BoldItalic Standard "(1.05)" Standard ROM
+#font Palatino-Italic Standard "(1.05)" Standard ROM
+#font Palatino-Roman Standard "(1.05)" Standard ROM
+#font Symbol Special "(001.005)" Special ROM
+#font Times-Bold Standard "(1.05)" Standard ROM
+#font Times-BoldItalic Standard "(1.05)" Standard ROM
+#font Times-Italic Standard "(1.05)" Standard ROM
+#font Times-Roman Standard "(1.05)" Standard ROM
+#font ZapfChancery-MediumItalic Standard "(1.05)" Standard ROM
+#font ZapfDingbats Special "(001.005)" Special ROM
diff --git a/ppdc/hp.h b/ppdc/hp.h
new file mode 100644 (file)
index 0000000..f544fad
--- /dev/null
+++ b/ppdc/hp.h
@@ -0,0 +1,13 @@
+/*
+ * This file contains model number definitions for the CUPS sample
+ * HP driver.
+ *
+ * Copyright 2007 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
+ *
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+ */
+
+#define HP_LASERJET    0               /* HP LaserJet */
+#define HP_DESKJET     1               /* HP DeskJet with simple color */
+#define HP_DESKJET2    2               /* HP DeskJet with CRet color */
diff --git a/ppdc/label.h b/ppdc/label.h
new file mode 100644 (file)
index 0000000..89372f2
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * This file contains model number definitions for the CUPS sample
+ * label printer driver.
+ *
+ * Copyright 2007 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
+ *
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+ */
+
+#define DYMO_3x0       0               /* Dymo Labelwriter 300/330/330 Turbo */
+
+#define ZEBRA_EPL_LINE  0x10            /* Zebra EPL line mode printers */
+#define ZEBRA_EPL_PAGE  0x11            /* Zebra EPL page mode printers */
+#define ZEBRA_ZPL       0x12            /* Zebra ZPL-based printers */
+#define ZEBRA_CPCL      0x13            /* Zebra CPCL-based printers */
+
+#define INTELLITECH_PCL        0x20            /* Intellitech PCL-based printers */
diff --git a/ppdc/media.defs b/ppdc/media.defs
new file mode 100644 (file)
index 0000000..179552d
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Adobe standard media size definitions for the CUPS PPD file compiler.
+ *
+ * Copyright © 2007-2016 by Apple Inc.
+ * Copyright © 1997-2005 by Easy Software Products.
+ *
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
+ */
+
+#media "3x5/3 x 5" 216 360
+#media "3.5x5/3.5 x 5" 252 360
+#media "5x7/5 x 7" 360 504
+#media "10x11/10 x 11" 720 792
+#media "10x13/10 x 13" 720 936
+#media "10x14/10 x 14" 720 1008
+#media "12x11/12 x 11" 864 792
+#media "15x11/15 x 11" 1080 792
+#media "7x9/7 x 9" 504 648
+#media "8x10/8 x 10" 576 720
+#media "9x11/9 x 11" 648 792
+#media "9x12/9 x 12" 648 864
+#media "A0/A0" 2384 3370
+#media "A0.Transverse/A0 Long Edge" 3370 2384
+#media "A1/A1" 1684 2384
+#media "A1.Transverse/A1 Long Edge" 2384 1684
+#media "A2/A2" 1191 1684
+#media "A2.Transverse/A2 Long Edge" 1684 1191
+#media "A3/A3" 842 1191
+#media "A3.Transverse/A3 Long Edge" 1191 842
+#media "A3Extra/A3 Oversize" 913 1262
+#media "A3Extra.Transverse/A3 Oversize Long Edge" 913 1262
+#media "A3Rotated/A3 Long Edge" 1191 842
+#media "A4/A4" 595 842
+#media "A4Extra/A4 Oversize" 667 914
+#media "A4Plus/A4 Oversize" 595 936
+#media "A4Rotated/A4 Long Edge" 842 595
+#media "A4Small/A4 Small" 595 842
+#media "A4.Transverse/A4 Long Edge" 842 595
+#media "A5/A5" 420 595
+#media "A5Extra/A5 Oversize" 492 668
+#media "A5Rotated/A5 Long Edge" 595 420
+#media "A5.Transverse/A5 Long Edge" 595 420
+#media "A6/A6" 297 420
+#media "A6Rotated/A6 Long Edge" 420 297
+#media "A7/A7" 210 297
+#media "A8/A8" 148 210
+#media "A9/A9" 105 148
+#media "A10/A10" 73 105
+#media "AnsiA/ANSI A" 612 792
+#media "AnsiB/ANSI B" 792 1224
+#media "AnsiC/ANSI C" 1224 1584
+#media "AnsiD/ANSI D" 1584 2448
+#media "AnsiE/ANSI E" 2448 3168
+#media "ARCHA/Letter Oversize" 648 864
+#media "ARCHA.Transverse/Letter Oversize Long Edge" 864 648
+#media "ARCHB/Tabloid Oversize" 864 1296
+#media "ARCHB.Transverse/Tabloid Oversize Long Edge" 1296 864
+#media "ARCHC/ARCH C" 1296 1728
+#media "ARCHC.Transverse/ARCH C Long Edge" 1728 1296
+#media "ARCHD/ARCH D" 1728 2592
+#media "ARCHD.Transverse/ARCH D Long Edge" 2592 1728
+#media "ARCHE/ARCH E" 2592 3456
+#media "ARCHE.Transverse/ARCH E Long Edge" 3456 2592
+#media "B0/JIS B0" 2920 4127
+#media "B10/JIS B10" 91 127
+#media "B1/JIS B1" 2064 2918
+#media "B1/JIS B1" 2064 2920
+#media "B2/JIS B2" 1460 2064
+#media "B3/JIS B3" 1032 1460
+#media "B4/JIS B4" 729 1032
+#media "B4Rotated/JIS B4 Long Edge" 1032 729
+#media "B5/JIS B5" 516 729
+#media "B5Rotated/JIS B5 Long Edge" 729 516
+#media "B5.Transverse/JIS B5 Long Edge" 516 729
+#media "B6/JIS B6" 363 516
+#media "B6Rotated/JIS B6 Long Edge" 516 363
+#media "B7/JIS B7" 258 363
+#media "B8/JIS B8" 181 258
+#media "B9/JIS B9" 127 181
+#media "C4/Envelope C4" 649 918
+#media "C5/Envelope C5" 459 649
+#media "C6/Envelope C6" 323 459
+#media "DL/Envelope DL" 312 624
+#media "DoublePostcard/Postcard Double" 567 420
+#media "DoublePostcardRotated/Postcard Double Long Edge" 420 567
+#media "Env10/Envelope #10" 297 684
+#media "Env11/Envelope #11" 324 747
+#media "Env12/Envelope #12" 342 792
+#media "Env14/Envelope #14" 360 828
+#media "Env9/Envelope #9" 279 639
+#media "EnvC0/Envelope C0" 2599 3676
+#media "EnvC1/Envelope C1" 1837 2599
+#media "EnvC2/Envelope C2" 1298 1837
+#media "EnvC3/Envelope C3" 918 1296
+#media "EnvC4/Envelope C4" 649 918
+#media "EnvC5/Envelope C5" 459 649
+#media "EnvC65/Envelope C65" 324 648
+#media "EnvC6/Envelope C6" 323 459
+#media "EnvC7/Envelope C7" 230 323
+#media "EnvChou3/Envelope Choukei 3" 340 666
+#media "EnvChou3Rotated/Envelope Choukei 3 Long Edge" 666 340
+#media "EnvChou4/Envelope Choukei 4" 255 581
+#media "EnvChou4Rotated/Envelope Choukei 4 Long Edge" 581 255
+#media "EnvDL/Envelope DL" 312 624
+#media "EnvInvite/Envelope Invite" 624 624
+#media "EnvISOB4/Envelope B4" 708 1001
+#media "EnvISOB5/Envelope B5" 499 709
+#media "EnvISOB6/Envelope B6" 499 354
+#media "EnvItalian/Envelope Italian" 312 652
+#media "EnvKaku2/Envelope Kaku2" 680 941
+#media "EnvKaku2Rotated/Envelope Kaku2 Long Edge" 941 680
+#media "EnvKaku3/Envelope Kaku3" 612 785
+#media "EnvKaku3Rotated/Envelope Kaku3 Long Edge" 785 612
+#media "EnvMonarch/Envelope Monarch" 279 540
+#media "EnvPersonal/Envelope Personal" 261 468
+#media "EnvPRC1/Envelope PRC1" 289 468
+#media "EnvPRC1Rotated/Envelope PRC1 Long Edge" 468 289
+#media "EnvPRC2/Envelope PRC2" 289 499
+#media "EnvPRC2Rotated/Envelope PRC2 Long Edge" 499 289
+#media "EnvPRC3/Envelope PRC3" 354 499
+#media "EnvPRC3Rotated/Envelope PRC3 Long Edge" 499 354
+#media "EnvPRC4/Envelope PRC4" 312 590
+#media "EnvPRC4Rotated/Envelope PRC4 Long Edge" 590 312
+#media "EnvPRC5/Envelope PRC5PRC5" 312 624
+#media "EnvPRC5Rotated/Envelope PRC5 Long Edge" 624 312
+#media "EnvPRC6/Envelope PRC6" 340 652
+#media "EnvPRC6Rotated/Envelope PRC6 Long Edge" 652 340
+#media "EnvPRC7/Envelope PRC7" 454 652
+#media "EnvPRC7Rotated/Envelope PRC7 Long Edge" 652 454
+#media "EnvPRC8/Envelope PRC8" 340 876
+#media "EnvPRC8Rotated/Envelope PRC8 Long Edge" 876 340
+#media "EnvPRC9/Envelope PRC9" 649 918
+#media "EnvPRC9Rotated/Envelope PRC9 Long Edge" 918 649
+#media "EnvPRC10/Envelope PRC10" 918 1298
+#media "EnvPRC10Rotated/Envelope PRC10 Long Edge" 1298 918
+#media "EnvYou4/Envelope You4" 298 666
+#media "EnvYou4Rotated/Envelope You4 Long Edge" 666 298
+#media "Executive/Executive" 522 756
+#media "FanFoldGerman/European Fanfold" 612 864
+#media "FanFoldGermanLegal/European Fanfold Legal" 612 936
+#media "FanFoldUS/US Fanfold" 1071 792
+#media "Folio/Folio" 595 935
+#media "ISOB0/B0" 2835 4008
+#media "ISOB1/B1" 2004 2835
+#media "ISOB2/B2" 1417 2004
+#media "ISOB3/B3" 1001 1417
+#media "ISOB4/B4" 709 1001
+#media "ISOB5/B5" 499 709
+#media "ISOB5Extra/B5 Oversize" 570 782
+#media "ISOB6/B6" 354 499
+#media "ISOB7/B7" 249 354
+#media "ISOB8/B8" 176 249
+#media "ISOB9/B9" 125 176
+#media "ISOB10/B10" 88 125
+#media "Ledger/US Ledger" 1224 792
+#media "Legal/US Legal" 612 1008
+#media "LegalExtra/US Legal Oversize" 684 1080
+#media "Letter/US Letter" 612 792
+#media "Letter.Transverse/US Letter Long Edge" 792 612
+#media "LetterExtra/US Letter Oversize" 684 864
+#media "LetterExtra.Transverse/US Letter Oversize Long Edge" 864 684
+#media "LetterPlus/US Letter Oversize" 612 914
+#media "LetterRotated/US Letter Long Edge" 792 612
+#media "LetterSmall/US Letter Small" 612 792
+#media "Monarch/Envelope Monarch" 279 540
+#media "Note/Note" 612 792
+#media "Postcard/Postcard" 284 419
+#media "PostcardRotated/Postcard Long Edge" 419 284
+#media "PRC16K/PRC16K" 414 610
+#media "PRC16KRotated/PRC16K Long Edge" 610 414
+#media "PRC32K/PRC32K" 275 428
+#media "PRC32KBig/PRC32K Oversize" 275 428
+#media "PRC32KBigRotated/PRC32K Oversize Long Edge" 428 275
+#media "PRC32KRotated/PRC32K Long Edge" 428 275
+#media "Quarto/Quarto" 610 780
+#media "Statement/Statement" 396 612
+#media "SuperA/Super A" 643 1009
+#media "SuperB/Super B" 864 1380
+#media "Tabloid/Tabloid" 792 1224
+#media "TabloidExtra/Tabloid Oversize" 864 1296
+
+/*
+ * Non-standard sizes...
+ */
+
+#media "Photo4x6/Photo" 288 432
+#media "PhotoLabel/Photo Labels" 288 468
+#media "w936h1368/Super B/A3" 936 1368
+#media "w81h252/Address" 81 252
+#media "w101h252/Large Address" 101 252
+#media "w54h144/Return Address" 54 144
+#media "w167h288/Shipping Address" 167 288
+#media "w162h540/Internet Postage 2-Part" 162 540
+#media "w162h504/Internet Postage 3-Part" 162 504
+#media "w41h248/File Folder" 41 248
+#media "w41h144/Hanging Folder" 41 144
+#media "w153h198/3.5\" Disk" 153 198
diff --git a/ppdc/ppdc-array.cxx b/ppdc/ppdc-array.cxx
new file mode 100644 (file)
index 0000000..3a0cab3
--- /dev/null
@@ -0,0 +1,148 @@
+//
+// Array class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2019 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcArray::ppdcArray()' - Create a new array.
+//
+
+ppdcArray::ppdcArray(ppdcArray *a)
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  if (a)
+  {
+    count = a->count;
+    alloc = count;
+
+    if (count)
+    {
+      // Make a copy of the array...
+      data = new ppdcShared *[count];
+
+      memcpy(data, a->data, (size_t)count * sizeof(ppdcShared *));
+
+      for (size_t i = 0; i < count; i ++)
+        data[i]->retain();
+    }
+    else
+      data = 0;
+  }
+  else
+  {
+    count = 0;
+    alloc = 0;
+    data  = 0;
+  }
+
+  current = 0;
+}
+
+
+//
+// 'ppdcArray::~ppdcArray()' - Destroy an array.
+//
+
+ppdcArray::~ppdcArray()
+{
+  PPDC_DELETE;
+
+  for (size_t i = 0; i < count; i ++)
+    data[i]->release();
+
+  if (alloc)
+    delete[] data;
+}
+
+
+//
+// 'ppdcArray::add()' - Add an element to an array.
+//
+
+void
+ppdcArray::add(ppdcShared *d)
+{
+  ppdcShared   **temp;
+
+
+  if (count >= alloc)
+  {
+    alloc += 10;
+    temp  = new ppdcShared *[alloc];
+
+    memcpy(temp, data, (size_t)count * sizeof(ppdcShared *));
+
+    delete[] data;
+    data = temp;
+  }
+
+  data[count++] = d;
+}
+
+
+//
+// 'ppdcArray::first()' - Return the first element in the array.
+//
+
+ppdcShared *
+ppdcArray::first()
+{
+  current = 0;
+
+  if (current >= count)
+    return (0);
+  else
+    return (data[current ++]);
+}
+
+
+//
+// 'ppdcArray::next()' - Return the next element in the array.
+//
+
+ppdcShared *
+ppdcArray::next()
+{
+  if (current >= count)
+    return (0);
+  else
+    return (data[current ++]);
+}
+
+
+//
+// 'ppdcArray::remove()' - Remove an element from the array.
+//
+
+void
+ppdcArray::remove(ppdcShared *d)               // I - Data element
+{
+  size_t       i;                              // Looping var
+
+
+  for (i = 0; i < count; i ++)
+    if (d == data[i])
+      break;
+
+  if (i >= count)
+    return;
+
+  count --;
+  d->release();
+
+  if (i < count)
+    memmove(data + i, data + i + 1, (size_t)(count - i) * sizeof(ppdcShared *));
+}
diff --git a/ppdc/ppdc-attr.cxx b/ppdc/ppdc-attr.cxx
new file mode 100644 (file)
index 0000000..5bd6b21
--- /dev/null
@@ -0,0 +1,50 @@
+//
+// Attribute class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcAttr::ppdcAttr()' - Create an attribute.
+//
+
+ppdcAttr::ppdcAttr(const char *n,      // I - Name
+                   const char *s,      // I - Spec string
+                  const char *t,       // I - Human-readable text
+                  const char *v,       // I - Value
+                  bool       loc)      // I - Localize this attribute?
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  name        = new ppdcString(n);
+  selector    = new ppdcString(s);
+  text        = new ppdcString(t);
+  value       = new ppdcString(v);
+  localizable = loc;
+}
+
+
+//
+// 'ppdcAttr::~ppdcAttr()' - Destroy an attribute.
+//
+
+ppdcAttr::~ppdcAttr()
+{
+  PPDC_DELETE;
+
+  name->release();
+  selector->release();
+  text->release();
+  value->release();
+}
diff --git a/ppdc/ppdc-catalog.cxx b/ppdc/ppdc-catalog.cxx
new file mode 100644 (file)
index 0000000..8283218
--- /dev/null
@@ -0,0 +1,872 @@
+//
+// Shared message catalog class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2017 by Apple Inc.
+// Copyright 2002-2006 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// Character encodings...
+//
+
+typedef enum
+{
+  PPDC_CS_AUTO,
+  PPDC_CS_UTF8,
+  PPDC_CS_UTF16BE,
+  PPDC_CS_UTF16LE
+} ppdc_cs_t;
+
+
+//
+// Local functions...
+//
+
+static int     get_utf8(char *&ptr);
+static int     get_utf16(cups_file_t *fp, ppdc_cs_t &cs);
+static int     put_utf8(int ch, char *&ptr, char *end);
+static int     put_utf16(cups_file_t *fp, int ch);
+
+
+//
+// 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
+//
+
+ppdcCatalog::ppdcCatalog(const char *l,        // I - Locale
+                         const char *f)        // I - Message catalog file
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  locale   = new ppdcString(l);
+  filename = new ppdcString(f);
+  messages = new ppdcArray();
+
+  if (l && strcmp(l, "en"))
+  {
+    // Try loading the base messages for this locale...
+    char       pofile[1024];           // Message catalog file
+    const char  *c;
+
+    /* Determine CUPS datadir (usually /usr/share/cups) */
+    if ((c = getenv("CUPS_DATADIR")) == NULL)
+      c = CUPS_DATADIR;
+
+    snprintf(pofile, sizeof(pofile), "%s/locale/%s/cups_%s.po", c, l, l);
+
+    if (load_messages(pofile) && strchr(l, '_'))
+    {
+      // Try the base locale...
+      char     baseloc[3];             // Base locale...
+
+      strncpy(baseloc, l, sizeof(baseloc) - 1);
+      snprintf(pofile, sizeof(pofile), "%s/locale/%s/cups_%s.po", c,
+               baseloc, baseloc);
+
+      load_messages(pofile);
+    }
+  }
+
+  if (f && *f)
+    load_messages(f);
+}
+
+
+//
+// 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
+//
+
+ppdcCatalog::~ppdcCatalog()
+{
+  PPDC_DELETE;
+
+  locale->release();
+  filename->release();
+  messages->release();
+}
+
+
+//
+// 'ppdcCatalog::add_message()' - Add a new message.
+//
+
+void
+ppdcCatalog::add_message(
+    const char *id,                    // I - Message ID to add
+    const char *string)                        // I - Translation string
+{
+  ppdcMessage  *m;                     // Current message
+  char         text[1024];             // Text to translate
+
+
+  // Range check input...
+  if (!id)
+    return;
+
+  // Verify that we don't already have the message ID...
+  for (m = (ppdcMessage *)messages->first();
+       m;
+       m = (ppdcMessage *)messages->next())
+    if (!strcmp(m->id->value, id))
+    {
+      if (string)
+      {
+        m->string->release();
+       m->string = new ppdcString(string);
+      }
+      return;
+    }
+
+  // Add the message...
+  if (!string)
+  {
+    snprintf(text, sizeof(text), "TRANSLATE %s", id);
+    string = text;
+  }
+
+  messages->add(new ppdcMessage(id, string));
+}
+
+
+//
+// 'ppdcCatalog::find_message()' - Find a message in a catalog...
+//
+
+const char *                           // O - Message text
+ppdcCatalog::find_message(
+    const char *id)                    // I - Message ID
+{
+  ppdcMessage  *m;                     // Current message
+
+
+  if (!*id)
+    return (id);
+
+  for (m = (ppdcMessage *)messages->first();
+       m;
+       m = (ppdcMessage *)messages->next())
+    if (!strcmp(m->id->value, id))
+      return (m->string->value);
+
+  return (id);
+}
+
+
+//
+// 'ppdcCatalog::load_messages()' - Load messages from a .po file.
+//
+
+int                                    // O - 0 on success, -1 on failure
+ppdcCatalog::load_messages(
+    const char *f)                     // I - Message catalog file
+{
+  cups_file_t  *fp;                    // Message file
+  char         line[4096],             // Line buffer
+               *ptr,                   // Pointer into buffer
+               id[4096],               // Translation ID
+               str[4096];              // Translation string
+  int          linenum;                // Line number
+
+
+  // Open the message catalog file...
+  if ((fp = cupsFileOpen(f, "r")) == NULL)
+    return (-1);
+
+  if ((ptr = (char *)strrchr(f, '.')) == NULL)
+    goto unknown_load_format;
+  else if (!strcmp(ptr, ".strings"))
+  {
+   /*
+    * Read messages in macOS ".strings" format, which are either UTF-8/UTF-16
+    * text files of the format:
+    *
+    *     "id" = "str";
+    *
+    * Strings files can also contain C-style comments.
+    */
+
+    ppdc_cs_t  cs = PPDC_CS_AUTO;      // Character set for file
+    int                ch;                     // Current character from file
+    char       *end;                   // End of buffer
+
+
+    id[0]  = '\0';
+    str[0] = '\0';
+    ptr    = NULL;
+    end    = NULL;
+
+    while ((ch = get_utf16(fp, cs)) != 0)
+    {
+      if (ptr)
+      {
+        if (ch == '\\')
+       {
+         if ((ch = get_utf16(fp, cs)) == 0)
+           break;
+
+         if (ch == 'n')
+           ch = '\n';
+         else if (ch == 't')
+           ch = '\t';
+        }
+       else if (ch == '\"')
+       {
+         *ptr = '\0';
+         ptr  = NULL;
+       }
+
+        if (ptr)
+         put_utf8(ch, ptr, end);
+      }
+      else if (ch == '/')
+      {
+        // Start of a comment?
+       if ((ch = get_utf16(fp, cs)) == 0)
+         break;
+
+        if (ch == '*')
+       {
+         // Skip C comment...
+         int lastch = 0;
+
+          while ((ch = get_utf16(fp, cs)) != 0)
+         {
+           if (ch == '/' && lastch == '*')
+             break;
+
+           lastch = ch;
+         }
+       }
+       else if (ch == '/')
+       {
+         // Skip C++ comment...
+         while ((ch = get_utf16(fp, cs)) != 0)
+           if (ch == '\n')
+             break;
+       }
+      }
+      else if (ch == '\"')
+      {
+        // Start quoted string...
+       if (id[0])
+       {
+         ptr = str;
+         end = str + sizeof(str) - 1;
+       }
+       else
+       {
+         ptr = id;
+         end = id + sizeof(id) - 1;
+       }
+      }
+      else if (ch == ';')
+      {
+        // Add string...
+       add_message(id, str);
+       id[0] = '\0';
+      }
+    }
+  }
+  else if (!strcmp(ptr, ".po") || !strcmp(ptr, ".gz"))
+  {
+   /*
+    * Read messages from the catalog file until EOF...
+    *
+    * The format is the GNU gettext .po format, which is fairly simple:
+    *
+    *     msgid "some text"
+    *     msgstr "localized text"
+    *
+    * The ID and localized text can span multiple lines using the form:
+    *
+    *     msgid ""
+    *     "some long text"
+    *     msgstr ""
+    *     "localized text spanning "
+    *     "multiple lines"
+    */
+
+    int        which,                          // In msgid?
+       haveid,                         // Did we get a msgid string?
+       havestr;                        // Did we get a msgstr string?
+
+    linenum = 0;
+    id[0]   = '\0';
+    str[0]  = '\0';
+    haveid  = 0;
+    havestr = 0;
+    which   = 0;
+
+    while (cupsFileGets(fp, line, sizeof(line)))
+    {
+      linenum ++;
+
+      // Skip blank and comment lines...
+      if (line[0] == '#' || !line[0])
+       continue;
+
+      // Strip the trailing quote...
+      if ((ptr = (char *)strrchr(line, '\"')) == NULL)
+      {
+       fprintf(stderr,
+               _("ppdc: Expected quoted string on line %d of %s.\n"),
+               linenum, f);
+       cupsFileClose(fp);
+       return (-1);
+      }
+
+      *ptr = '\0';
+
+      // Find start of value...
+      if ((ptr = strchr(line, '\"')) == NULL)
+      {
+       fprintf(stderr,
+               _("ppdc: Expected quoted string on line %d of %s.\n"),
+               linenum, f);
+       cupsFileClose(fp);
+       return (-1);
+      }
+
+      ptr ++;
+
+      // Unquote the text...
+      char *sptr, *dptr;                       // Source/destination pointers
+
+      for (sptr = ptr, dptr = ptr; *sptr;)
+      {
+       if (*sptr == '\\')
+       {
+         sptr ++;
+         if (isdigit(*sptr))
+         {
+           *dptr = 0;
+
+           while (isdigit(*sptr))
+           {
+             *dptr = *dptr * 8 + *sptr - '0';
+             sptr ++;
+           }
+
+           dptr ++;
+         }
+         else
+         {
+           if (*sptr == 'n')
+             *dptr++ = '\n';
+           else if (*sptr == 'r')
+             *dptr++ = '\r';
+           else if (*sptr == 't')
+             *dptr++ = '\t';
+           else
+             *dptr++ = *sptr;
+
+           sptr ++;
+         }
+       }
+       else
+         *dptr++ = *sptr++;
+      }
+
+      *dptr = '\0';
+
+      // Create or add to a message...
+      if (!strncmp(line, "msgid", 5))
+      {
+       if (haveid && havestr)
+         add_message(id, str);
+
+       strncpy(id, ptr, sizeof(id) - 1);
+       str[0] = '\0';
+       haveid  = 1;
+       havestr = 0;
+       which   = 1;
+      }
+      else if (!strncmp(line, "msgstr", 6))
+      {
+       if (!haveid)
+       {
+         fprintf(stderr,
+                 _("ppdc: Need a msgid line before any "
+                   "translation strings on line %d of %s.\n"),
+                 linenum, f);
+         cupsFileClose(fp);
+         return (-1);
+       }
+
+       strncpy(str, ptr, sizeof(str) - 1);
+       havestr = 1;
+       which   = 2;
+      }
+      else if (line[0] == '\"' && which == 2)
+       strncat(str, ptr, sizeof(str) - 1);
+      else if (line[0] == '\"' && which == 1)
+       strncat(id, ptr, sizeof(id) - 1);
+      else
+      {
+       fprintf(stderr, _("ppdc: Unexpected text on line %d of %s.\n"),
+               linenum, f);
+       cupsFileClose(fp);
+       return (-1);
+      }
+    }
+
+    if (haveid && havestr)
+      add_message(id, str);
+  }
+  else
+    goto unknown_load_format;
+
+ /*
+  * Close the file and return...
+  */
+
+  cupsFileClose(fp);
+
+  return (0);
+
+ /*
+  * Unknown format error...
+  */
+
+  unknown_load_format:
+
+  fprintf(stderr,
+         _("ppdc: Unknown message catalog format for \"%s\".\n"), f);
+  cupsFileClose(fp);
+  return (-1);
+}
+
+
+//
+// 'ppdcCatalog::save_messages()' - Save the messages to a .po file.
+//
+
+int                                    // O - 0 on success, -1 on error
+ppdcCatalog::save_messages(
+    const char *f)                     // I - File to save to
+{
+  cups_file_t  *fp;                    // Message file
+  ppdcMessage  *m;                     // Current message
+  char         *ptr;                   // Pointer into string
+  int          utf16;                  // Output UTF-16 .strings file?
+  int          ch;                     // Current character
+
+
+  // Open the file...
+  if ((ptr = (char *)strrchr(f, '.')) == NULL)
+    return (-1);
+
+  if (!strcmp(ptr, ".gz"))
+    fp = cupsFileOpen(f, "w9");
+  else
+    fp = cupsFileOpen(f, "w");
+
+  if (!fp)
+    return (-1);
+
+  // For .strings files, write a BOM for big-endian output...
+  utf16 = !strcmp(ptr, ".strings");
+
+  if (utf16)
+    put_utf16(fp, 0xfeff);
+
+  // Loop through all of the messages...
+  for (m = (ppdcMessage *)messages->first();
+       m;
+       m = (ppdcMessage *)messages->next())
+  {
+    if (utf16)
+    {
+      put_utf16(fp, '\"');
+
+      ptr = m->id->value;
+      while ((ch = get_utf8(ptr)) != 0)
+       switch (ch)
+       {
+         case '\n' :
+             put_utf16(fp, '\\');
+             put_utf16(fp, 'n');
+             break;
+         case '\\' :
+             put_utf16(fp, '\\');
+             put_utf16(fp, '\\');
+             break;
+         case '\"' :
+             put_utf16(fp, '\\');
+             put_utf16(fp, '\"');
+             break;
+         default :
+             put_utf16(fp, ch);
+             break;
+       }
+
+      put_utf16(fp, '\"');
+      put_utf16(fp, ' ');
+      put_utf16(fp, '=');
+      put_utf16(fp, ' ');
+      put_utf16(fp, '\"');
+
+      ptr = m->string->value;
+      while ((ch = get_utf8(ptr)) != 0)
+       switch (ch)
+       {
+         case '\n' :
+             put_utf16(fp, '\\');
+             put_utf16(fp, 'n');
+             break;
+         case '\\' :
+             put_utf16(fp, '\\');
+             put_utf16(fp, '\\');
+             break;
+         case '\"' :
+             put_utf16(fp, '\\');
+             put_utf16(fp, '\"');
+             break;
+         default :
+             put_utf16(fp, ch);
+             break;
+       }
+
+      put_utf16(fp, '\"');
+      put_utf16(fp, ';');
+      put_utf16(fp, '\n');
+    }
+    else
+    {
+      cupsFilePuts(fp, "msgid \"");
+      for (ptr = m->id->value; *ptr; ptr ++)
+       switch (*ptr)
+       {
+         case '\n' :
+             cupsFilePuts(fp, "\\n");
+             break;
+         case '\\' :
+             cupsFilePuts(fp, "\\\\");
+             break;
+         case '\"' :
+             cupsFilePuts(fp, "\\\"");
+             break;
+         default :
+             cupsFilePutChar(fp, *ptr);
+             break;
+       }
+      cupsFilePuts(fp, "\"\n");
+
+      cupsFilePuts(fp, "msgstr \"");
+      for (ptr = m->string->value; *ptr; ptr ++)
+       switch (*ptr)
+       {
+         case '\n' :
+             cupsFilePuts(fp, "\\n");
+             break;
+         case '\\' :
+             cupsFilePuts(fp, "\\\\");
+             break;
+         case '\"' :
+             cupsFilePuts(fp, "\\\"");
+             break;
+         default :
+             cupsFilePutChar(fp, *ptr);
+             break;
+       }
+      cupsFilePuts(fp, "\"\n");
+
+      cupsFilePutChar(fp, '\n');
+    }
+  }
+
+  cupsFileClose(fp);
+
+  return (0);
+}
+
+
+//
+// 'get_utf8()' - Get a UTF-8 character.
+//
+
+static int                             // O  - Unicode character or 0 on EOF
+get_utf8(char *&ptr)                   // IO - Pointer to character
+{
+  int  ch;                             // Current character
+
+
+  if ((ch = *ptr++ & 255) < 0xc0)
+    return (ch);
+
+  if ((ch & 0xe0) == 0xc0)
+  {
+    // Two-byte UTF-8...
+    if ((*ptr & 0xc0) != 0x80)
+      return (0);
+
+    ch = ((ch & 0x1f) << 6) | (*ptr++ & 0x3f);
+  }
+  else if ((ch & 0xf0) == 0xe0)
+  {
+    // Three-byte UTF-8...
+    if ((*ptr & 0xc0) != 0x80)
+      return (0);
+
+    ch = ((ch & 0x0f) << 6) | (*ptr++ & 0x3f);
+
+    if ((*ptr & 0xc0) != 0x80)
+      return (0);
+
+    ch = (ch << 6) | (*ptr++ & 0x3f);
+  }
+  else if ((ch & 0xf8) == 0xf0)
+  {
+    // Four-byte UTF-8...
+    if ((*ptr & 0xc0) != 0x80)
+      return (0);
+
+    ch = ((ch & 0x07) << 6) | (*ptr++ & 0x3f);
+
+    if ((*ptr & 0xc0) != 0x80)
+      return (0);
+
+    ch = (ch << 6) | (*ptr++ & 0x3f);
+
+    if ((*ptr & 0xc0) != 0x80)
+      return (0);
+
+    ch = (ch << 6) | (*ptr++ & 0x3f);
+  }
+
+  return (ch);
+}
+
+
+//
+// 'get_utf16()' - Get a UTF-16 character...
+//
+
+static int                             // O  - Unicode character or 0 on EOF
+get_utf16(cups_file_t *fp,             // I  - File to read from
+          ppdc_cs_t   &cs)             // IO - Character set of file
+{
+  int          ch;                     // Current character
+  unsigned char        buffer[3];              // Bytes
+
+
+  if (cs == PPDC_CS_AUTO)
+  {
+    // Get byte-order-mark, if present...
+    if (cupsFileRead(fp, (char *)buffer, 2) != 2)
+      return (0);
+
+    if (buffer[0] == 0xfe && buffer[1] == 0xff)
+    {
+      // Big-endian UTF-16...
+      cs = PPDC_CS_UTF16BE;
+
+      if (cupsFileRead(fp, (char *)buffer, 2) != 2)
+       return (0);
+    }
+    else if (buffer[0] == 0xff && buffer[1] == 0xfe)
+    {
+      // Little-endian UTF-16...
+      cs = PPDC_CS_UTF16LE;
+
+      if (cupsFileRead(fp, (char *)buffer, 2) != 2)
+       return (0);
+    }
+    else if (buffer[0] == 0x00 && buffer[1] != 0x00)
+    {
+      // No BOM, assume big-endian UTF-16...
+      cs = PPDC_CS_UTF16BE;
+    }
+    else if (buffer[0] != 0x00 && buffer[1] == 0x00)
+    {
+      // No BOM, assume little-endian UTF-16...
+      cs = PPDC_CS_UTF16LE;
+    }
+    else
+    {
+      // No BOM, assume UTF-8...
+      cs = PPDC_CS_UTF8;
+
+      cupsFileRewind(fp);
+    }
+  }
+  else if (cs != PPDC_CS_UTF8)
+  {
+    if (cupsFileRead(fp, (char *)buffer, 2) != 2)
+      return (0);
+  }
+
+  if (cs == PPDC_CS_UTF8)
+  {
+    // UTF-8 character...
+    if ((ch = cupsFileGetChar(fp)) < 0)
+      return (0);
+
+    if ((ch & 0xe0) == 0xc0)
+    {
+      // Two-byte UTF-8...
+      if (cupsFileRead(fp, (char *)buffer, 1) != 1)
+        return (0);
+
+      if ((buffer[0] & 0xc0) != 0x80)
+        return (0);
+
+      ch = ((ch & 0x1f) << 6) | (buffer[0] & 0x3f);
+    }
+    else if ((ch & 0xf0) == 0xe0)
+    {
+      // Three-byte UTF-8...
+      if (cupsFileRead(fp, (char *)buffer, 2) != 2)
+        return (0);
+
+      if ((buffer[0] & 0xc0) != 0x80 ||
+          (buffer[1] & 0xc0) != 0x80)
+        return (0);
+
+      ch = ((((ch & 0x0f) << 6) | (buffer[0] & 0x3f)) << 6) |
+           (buffer[1] & 0x3f);
+    }
+    else if ((ch & 0xf8) == 0xf0)
+    {
+      // Four-byte UTF-8...
+      if (cupsFileRead(fp, (char *)buffer, 3) != 3)
+        return (0);
+
+      if ((buffer[0] & 0xc0) != 0x80 ||
+          (buffer[1] & 0xc0) != 0x80 ||
+          (buffer[2] & 0xc0) != 0x80)
+        return (0);
+
+      ch = ((((((ch & 0x07) << 6) | (buffer[0] & 0x3f)) << 6) |
+             (buffer[1] & 0x3f)) << 6) | (buffer[2] & 0x3f);
+    }
+  }
+  else
+  {
+    // UTF-16 character...
+    if (cs == PPDC_CS_UTF16BE)
+      ch = (buffer[0] << 8) | buffer[1];
+    else
+      ch = (buffer[1] << 8) | buffer[0];
+
+    if (ch >= 0xd800 && ch <= 0xdbff)
+    {
+      // Handle multi-word encoding...
+      int lch;
+
+      if (cupsFileRead(fp, (char *)buffer, 2) != 2)
+        return (0);
+
+      if (cs == PPDC_CS_UTF16BE)
+       lch = (buffer[0] << 8) | buffer[1];
+      else
+       lch = (buffer[1] << 8) | buffer[0];
+
+      if (lch < 0xdc00 || lch >= 0xdfff)
+        return (0);
+
+      ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+    }
+  }
+
+  return (ch);
+}
+
+
+//
+// 'put_utf8()' - Add a UTF-8 character to a string.
+//
+
+static int                             // O  - 0 on success, -1 on failure
+put_utf8(int  ch,                      // I  - Unicode character
+         char *&ptr,                   // IO - String pointer
+        char *end)                     // I  - End of buffer
+{
+  if (ch < 0x80)
+  {
+    // One-byte ASCII...
+    if (ptr >= end)
+      return (-1);
+
+    *ptr++ = (char)ch;
+  }
+  else if (ch < 0x800)
+  {
+    // Two-byte UTF-8...
+    if ((ptr + 1) >= end)
+      return (-1);
+
+    *ptr++ = (char)(0xc0 | (ch >> 6));
+    *ptr++ = (char)(0x80 | (ch & 0x3f));
+  }
+  else if (ch < 0x10000)
+  {
+    // Three-byte UTF-8...
+    if ((ptr + 2) >= end)
+      return (-1);
+
+    *ptr++ = (char)(0xe0 | (ch >> 12));
+    *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
+    *ptr++ = (char)(0x80 | (ch & 0x3f));
+  }
+  else
+  {
+    // Four-byte UTF-8...
+    if ((ptr + 3) >= end)
+      return (-1);
+
+    *ptr++ = (char)(0xf0 | (ch >> 18));
+    *ptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
+    *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
+    *ptr++ = (char)(0x80 | (ch & 0x3f));
+  }
+
+  return (0);
+}
+
+
+//
+// 'put_utf16()' - Write a UTF-16 character to a file.
+//
+
+static int                             // O - 0 on success, -1 on failure
+put_utf16(cups_file_t *fp,             // I - File to write to
+          int         ch)              // I - Unicode character
+{
+  unsigned char        buffer[4];              // Output buffer
+
+
+  if (ch < 0x10000)
+  {
+    // One-word UTF-16 big-endian...
+    buffer[0] = (unsigned char)(ch >> 8);
+    buffer[1] = (unsigned char)ch;
+
+    if (cupsFileWrite(fp, (char *)buffer, 2) == 2)
+      return (0);
+  }
+  else
+  {
+    // Two-word UTF-16 big-endian...
+    ch -= 0x10000;
+
+    buffer[0] = (unsigned char)(0xd8 | (ch >> 18));
+    buffer[1] = (unsigned char)(ch >> 10);
+    buffer[2] = (unsigned char)(0xdc | ((ch >> 8) & 0x03));
+    buffer[3] = (unsigned char)ch;
+
+    if (cupsFileWrite(fp, (char *)buffer, 4) == 4)
+      return (0);
+  }
+
+  return (-1);
+}
diff --git a/ppdc/ppdc-choice.cxx b/ppdc/ppdc-choice.cxx
new file mode 100644 (file)
index 0000000..cb514d5
--- /dev/null
@@ -0,0 +1,45 @@
+//
+// Option choice class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcChoice::ppdcChoice()' - Create a new option choice.
+//
+
+ppdcChoice::ppdcChoice(const char *n,  // I - Name of choice
+                       const char *t,  // I - Text of choice
+                      const char *c)   // I - Code of choice
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  name = new ppdcString(n);
+  text = new ppdcString(t);
+  code = new ppdcString(c);
+}
+
+
+//
+// 'ppdcChoice::~ppdcChoice()' - Destroy an option choice.
+//
+
+ppdcChoice::~ppdcChoice()
+{
+  PPDC_DELETE;
+
+  name->release();
+  text->release();
+  code->release();
+}
diff --git a/ppdc/ppdc-constraint.cxx b/ppdc/ppdc-constraint.cxx
new file mode 100644 (file)
index 0000000..54e5245
--- /dev/null
@@ -0,0 +1,48 @@
+//
+// Contraint class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcConstraint::ppdcConstraint()' - Create a constraint.
+//
+
+ppdcConstraint::ppdcConstraint(const char *o1, // I - First option
+                               const char *c1, // I - First choice
+                              const char *o2,  // I - Second option
+                              const char *c2)  // I - Second choice
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  option1 = new ppdcString(o1);
+  choice1 = new ppdcString(c1);
+  option2 = new ppdcString(o2);
+  choice2 = new ppdcString(c2);
+}
+
+
+//
+// 'ppdcConstraint::~ppdcConstraint()' - Destroy a constraint.
+//
+
+ppdcConstraint::~ppdcConstraint()
+{
+  PPDC_DELETE;
+
+  option1->release();
+  choice1->release();
+  option2->release();
+  choice2->release();
+}
diff --git a/ppdc/ppdc-driver.cxx b/ppdc/ppdc-driver.cxx
new file mode 100644 (file)
index 0000000..2cc77c7
--- /dev/null
@@ -0,0 +1,1325 @@
+//
+// PPD file compiler definitions for the CUPS PPD Compiler.
+//
+// Copyright © 2007-2019 by Apple Inc.
+// Copyright © 2002-2006 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more
+// information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#include <cupsfilters/ppdgenerator.h>
+
+
+//
+// 'ppdcDriver::ppdcDriver()' - Create a new printer driver.
+//
+
+ppdcDriver::ppdcDriver(ppdcDriver *d)  // I - Printer driver template
+  : ppdcShared()
+{
+  ppdcGroup    *g;                     // Current group
+
+
+  PPDC_NEW;
+
+  if (d)
+  {
+    // Bump the use count of any strings we inherit...
+    if (d->manufacturer)
+      d->manufacturer->retain();
+    if (d->version)
+      d->version->retain();
+    if (d->default_font)
+      d->default_font->retain();
+    if (d->default_size)
+      d->default_size->retain();
+    if (d->custom_size_code)
+      d->custom_size_code->retain();
+
+    // Copy all of the data from the driver template...
+    copyright           = new ppdcArray(d->copyright);
+    manufacturer        = d->manufacturer;
+    model_name          = 0;
+    file_name           = 0;
+    pc_file_name        = 0;
+    type                = d->type;
+    version             = d->version;
+    model_number        = d->model_number;
+    manual_copies       = d->manual_copies;
+    color_device        = d->color_device;
+    throughput          = d->throughput;
+    attrs               = new ppdcArray(d->attrs);
+    constraints         = new ppdcArray(d->constraints);
+    filters             = new ppdcArray(d->filters);
+    fonts               = new ppdcArray(d->fonts);
+    profiles            = new ppdcArray(d->profiles);
+    sizes               = new ppdcArray(d->sizes);
+    default_font        = d->default_font;
+    default_size        = d->default_size;
+    variable_paper_size = d->variable_paper_size;
+    custom_size_code    = d->custom_size_code;
+    left_margin         = d->left_margin;
+    bottom_margin       = d->bottom_margin;
+    right_margin        = d->right_margin;
+    top_margin          = d->top_margin;
+    max_width           = d->max_width;
+    max_length          = d->max_length;
+    min_width           = d->min_width;
+    min_length          = d->min_length;
+
+    // Then copy the groups manually, since we want separate copies
+    // of the groups and options...
+    groups = new ppdcArray();
+
+    for (g = (ppdcGroup *)d->groups->first(); g; g = (ppdcGroup *)d->groups->next())
+      groups->add(new ppdcGroup(g));
+  }
+  else
+  {
+    // Zero all of the data in the driver...
+    copyright           = new ppdcArray();
+    manufacturer        = 0;
+    model_name          = 0;
+    file_name           = 0;
+    pc_file_name        = 0;
+    version             = 0;
+    type                = PPDC_DRIVER_CUSTOM;
+    model_number        = 0;
+    manual_copies       = 0;
+    color_device        = 0;
+    throughput          = 1;
+    attrs               = new ppdcArray();
+    constraints         = new ppdcArray();
+    fonts               = new ppdcArray();
+    filters             = new ppdcArray();
+    groups              = new ppdcArray();
+    profiles            = new ppdcArray();
+    sizes               = new ppdcArray();
+    default_font        = 0;
+    default_size        = 0;
+    variable_paper_size = 0;
+    custom_size_code    = 0;
+    left_margin         = 0;
+    bottom_margin       = 0;
+    right_margin        = 0;
+    top_margin          = 0;
+    max_width           = 0;
+    max_length          = 0;
+    min_width           = 0;
+    min_length          = 0;
+  }
+}
+
+
+//
+// 'ppdcDriver::~ppdcDriver()' - Destroy a printer driver.
+//
+
+ppdcDriver::~ppdcDriver()
+{
+  PPDC_DELETE;
+
+  copyright->release();
+
+  if (manufacturer)
+    manufacturer->release();
+  if (model_name)
+    model_name->release();
+  if (file_name)
+    file_name->release();
+  if (pc_file_name)
+    pc_file_name->release();
+  if (version)
+    version->release();
+  if (default_font)
+    default_font->release();
+  if (default_size)
+    default_size->release();
+  if (custom_size_code)
+    custom_size_code->release();
+
+  attrs->release();
+  constraints->release();
+  filters->release();
+  fonts->release();
+  groups->release();
+  profiles->release();
+  sizes->release();
+}
+
+
+//
+// 'ppdcDriver::find_attr()' - Find an attribute.
+//
+
+ppdcAttr *                             // O - Attribute or NULL
+ppdcDriver::find_attr(const char *k,   // I - Keyword string
+                      const char *s)   // I - Spec string
+{
+  ppdcAttr     *a;                     // Current attribute
+
+
+  for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
+    if (!strcmp(a->name->value, k) &&
+        ((!s && (!a->selector->value || !a->selector->value[0])) ||
+        (s && a->selector->value && !strcmp(a->selector->value, s))))
+      return (a);
+
+  return (NULL);
+}
+
+
+//
+// 'ppdcDriver::find_group()' - Find a group.
+//
+
+ppdcGroup *                            // O - Matching group or NULL
+ppdcDriver::find_group(const char *n)  // I - Group name
+{
+  ppdcGroup    *g;                     // Current group
+
+
+  for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
+    if (!strcasecmp(n, g->name->value))
+      return (g);
+
+  return (0);
+}
+
+
+//
+// 'ppdcDriver::find_option()' - Find an option.
+//
+
+ppdcOption *                           // O - Matching option or NULL
+ppdcDriver::find_option(const char *n) // I - Option name
+{
+  return (find_option_group(n, (ppdcGroup **)0));
+}
+
+
+//
+// 'ppdcDriver::find_option_group()' - Find an option and its group.
+//
+
+ppdcOption *                           // O - Matching option or NULL
+ppdcDriver::find_option_group(
+    const char *n,                     // I - Option name
+    ppdcGroup  **mg)                   // O - Matching group or NULL
+{
+  ppdcGroup    *g;                     // Current group
+  ppdcOption   *o;                     // Current option
+
+
+  for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
+    for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next())
+      if (!strcasecmp(n, o->name->value))
+      {
+        if (mg)
+         *mg = g;
+
+        return (o);
+      }
+
+  if (mg)
+    *mg = (ppdcGroup *)0;
+
+  return (0);
+}
+
+
+//
+// 'ppdcDriver::set_custom_size_code()' - Set the custom page size code.
+//
+
+void
+ppdcDriver::set_custom_size_code(
+    const char *c)                     // I - CustomPageSize code
+{
+  if (custom_size_code)
+    custom_size_code->release();
+
+  custom_size_code = new ppdcString(c);
+}
+
+
+//
+// 'ppdcDriver::set_default_font()' - Set the default font name.
+//
+
+void
+ppdcDriver::set_default_font(
+    ppdcFont *f)                       // I - Font
+{
+  if (default_font)
+    default_font->release();
+
+  if (f)
+  {
+    f->name->retain();
+    default_font = f->name;
+  }
+  else
+    default_font = 0;
+}
+
+
+//
+// 'ppdcDriver::set_default_size()' - Set the default size name.
+//
+
+void
+ppdcDriver::set_default_size(
+    ppdcMediaSize *m)                  // I - Media size
+{
+  if (default_size)
+    default_size->release();
+
+  if (m)
+  {
+    m->name->retain();
+    default_size = m->name;
+  }
+  else
+    default_size = 0;
+}
+
+
+//
+// 'ppdcDriver::set_file_name()' - Set the full filename.
+//
+
+void
+ppdcDriver::set_file_name(const char *f)// I - Filename
+{
+  if (file_name)
+    file_name->release();
+
+  file_name = new ppdcString(f);
+}
+
+
+//
+// 'ppdcDriver::set_manufacturer()' - Set the manufacturer name.
+//
+
+void
+ppdcDriver::set_manufacturer(
+    const char *m)                     // I - Model name
+{
+  if (manufacturer)
+    manufacturer->release();
+
+  manufacturer = new ppdcString(m);
+}
+
+
+//
+// 'ppdcDriver::set_model_name()' - Set the model name.
+//
+
+void
+ppdcDriver::set_model_name(
+    const char *m)                     // I - Model name
+{
+  if (model_name)
+    model_name->release();
+
+  model_name = new ppdcString(m);
+}
+
+
+//
+// 'ppdcDriver::set_pc_file_name()' - Set the PC filename.
+//
+
+void
+ppdcDriver::set_pc_file_name(
+    const char *f)                     // I - Filename
+{
+  if (pc_file_name)
+    pc_file_name->release();
+
+  pc_file_name = new ppdcString(f);
+}
+
+
+//
+// 'ppdcDriver::set_version()' - Set the version string.
+//
+
+void
+ppdcDriver::set_version(const char *v) // I - Version
+{
+  if (version)
+    version->release();
+
+  version = new ppdcString(v);
+}
+
+
+//
+// 'ppdcDriver::write_ppd_file()' - Write a PPD file...
+//
+
+int                                    // O - 0 on success, -1 on failure
+ppdcDriver::write_ppd_file(
+    cups_file_t    *fp,                        // I - PPD file
+    ppdcCatalog    *catalog,           // I - Message catalog
+    ppdcArray      *locales,           // I - Additional languages to add
+    ppdcSource     *src,               // I - Driver source
+    ppdcLineEnding le)                 // I - Line endings to use
+{
+  bool                 delete_cat;     // Delete the catalog when we are done?
+  char                 query[42],      // Query attribute
+                       custom[42];     // Custom attribute
+  ppdcString           *s;             // Copyright string
+  ppdcGroup            *g;             // Current group
+  ppdcOption           *o;             // Current option
+  ppdcChoice           *c;             // Current choice
+  ppdcMediaSize                *m;             // Current media size
+  ppdcProfile          *p;             // Current color profile
+  ppdcFilter           *f;             // Current filter
+  ppdcFont             *fn,            // Current font
+                       *bfn;           // Current base font
+  ppdcConstraint       *cn;            // Current constraint
+  ppdcAttr             *a;             // Current attribute
+  const char           *lf;            // Linefeed character to use
+
+
+  // If we don't have a message catalog, use an empty (English) one...
+  if (!catalog)
+  {
+    catalog    = new ppdcCatalog(NULL);
+    delete_cat = true;
+  }
+  else
+    delete_cat = false;
+
+  // Figure out the end-of-line string...
+  if (le == PPDC_LFONLY)
+    lf = "\n";
+  else if (le == PPDC_CRONLY)
+    lf = "\r";
+  else
+    lf = "\r\n";
+
+  // Write the standard header stuff...
+  cupsFilePrintf(fp, "*PPD-Adobe: \"4.3\"%s", lf);
+  cupsFilePrintf(fp, "*%%%%%%%% PPD file for %s with CUPS.%s",
+                 model_name->value, lf);
+  cupsFilePrintf(fp,
+                 "*%%%%%%%% Created by the PPD Compiler of cups-filters "
+                PACKAGE_VERSION ".%s", lf);
+  for (s = (ppdcString *)copyright->first();
+       s;
+       s = (ppdcString *)copyright->next())
+    cupsFilePrintf(fp, "*%% %s%s", catalog->find_message(s->value), lf);
+  cupsFilePrintf(fp, "*FormatVersion: \"4.3\"%s", lf);
+  cupsFilePrintf(fp, "*FileVersion: \"%s\"%s", version->value, lf);
+
+  a = find_attr("LanguageVersion", NULL);
+  cupsFilePrintf(fp, "*LanguageVersion: %s%s",
+                catalog->find_message(a ? a->value->value : "English"), lf);
+
+  a = find_attr("LanguageEncoding", NULL);
+  cupsFilePrintf(fp, "*LanguageEncoding: %s%s",
+                catalog->find_message(a ? a->value->value : "ISOLatin1"), lf);
+
+  cupsFilePrintf(fp, "*PCFileName: \"%s\"%s", pc_file_name->value, lf);
+
+  for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
+    if (!strcmp(a->name->value, "Product"))
+      break;
+
+  if (a)
+  {
+    for (; a; a = (ppdcAttr *)attrs->next())
+      if (!strcmp(a->name->value, "Product"))
+       cupsFilePrintf(fp, "*Product: \"%s\"%s", a->value->value, lf);
+  }
+  else
+    cupsFilePrintf(fp, "*Product: \"(%s)\"%s", model_name->value, lf);
+
+  cupsFilePrintf(fp, "*Manufacturer: \"%s\"%s",
+                catalog->find_message(manufacturer->value), lf);
+
+  if ((a = find_attr("ModelName", NULL)) != NULL)
+    cupsFilePrintf(fp, "*ModelName: \"%s\"%s",
+                  catalog->find_message(a->value->value), lf);
+  else if (strncasecmp(model_name->value, manufacturer->value,
+                       strlen(manufacturer->value)))
+    cupsFilePrintf(fp, "*ModelName: \"%s %s\"%s",
+                  catalog->find_message(manufacturer->value),
+                  catalog->find_message(model_name->value), lf);
+  else
+    cupsFilePrintf(fp, "*ModelName: \"%s\"%s",
+                  catalog->find_message(model_name->value), lf);
+
+  if ((a = find_attr("ShortNickName", NULL)) != NULL)
+    cupsFilePrintf(fp, "*ShortNickName: \"%s\"%s",
+                  catalog->find_message(a->value->value), lf);
+  else if (strncasecmp(model_name->value, manufacturer->value,
+                       strlen(manufacturer->value)))
+    cupsFilePrintf(fp, "*ShortNickName: \"%s %s\"%s",
+                  catalog->find_message(manufacturer->value),
+                  catalog->find_message(model_name->value), lf);
+  else
+    cupsFilePrintf(fp, "*ShortNickName: \"%s\"%s",
+                  catalog->find_message(model_name->value), lf);
+
+  if ((a = find_attr("NickName", NULL)) != NULL)
+    cupsFilePrintf(fp, "*NickName: \"%s\"%s",
+                  catalog->find_message(a->value->value), lf);
+  else if (strncasecmp(model_name->value, manufacturer->value,
+                       strlen(manufacturer->value)))
+    cupsFilePrintf(fp, "*NickName: \"%s %s, %s\"%s",
+                  catalog->find_message(manufacturer->value),
+                  catalog->find_message(model_name->value), version->value,
+                  lf);
+  else
+    cupsFilePrintf(fp, "*NickName: \"%s, %s\"%s",
+                  catalog->find_message(model_name->value), version->value,
+                  lf);
+
+  for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
+    if (!strcmp(a->name->value, "PSVersion"))
+      break;
+
+  if (a)
+  {
+    for (; a; a = (ppdcAttr *)attrs->next())
+      if (!strcmp(a->name->value, "PSVersion"))
+       cupsFilePrintf(fp, "*PSVersion: \"%s\"%s", a->value->value, lf);
+  }
+  else
+    cupsFilePrintf(fp, "*PSVersion: \"(3010.000) 0\"%s", lf);
+
+  if ((a = find_attr("LanguageLevel", NULL)) != NULL)
+    cupsFilePrintf(fp, "*LanguageLevel: \"%s\"%s", a->value->value, lf);
+  else
+    cupsFilePrintf(fp, "*LanguageLevel: \"3\"%s", lf);
+
+  cupsFilePrintf(fp, "*ColorDevice: %s%s", color_device ? "True" : "False", lf);
+
+  if ((a = find_attr("DefaultColorSpace", NULL)) != NULL)
+    cupsFilePrintf(fp, "*DefaultColorSpace: %s%s", a->value->value, lf);
+  else
+    cupsFilePrintf(fp, "*DefaultColorSpace: %s%s",
+                   color_device ? "RGB" : "Gray", lf);
+
+  if ((a = find_attr("FileSystem", NULL)) != NULL)
+    cupsFilePrintf(fp, "*FileSystem: %s%s", a->value->value, lf);
+  else
+    cupsFilePrintf(fp, "*FileSystem: False%s", lf);
+
+  cupsFilePrintf(fp, "*Throughput: \"%d\"%s", throughput, lf);
+
+  if ((a = find_attr("LandscapeOrientation", NULL)) != NULL)
+    cupsFilePrintf(fp, "*LandscapeOrientation: %s%s", a->value->value, lf);
+  else
+    cupsFilePrintf(fp, "*LandscapeOrientation: Plus90%s", lf);
+
+  if ((a = find_attr("TTRasterizer", NULL)) != NULL)
+    cupsFilePrintf(fp, "*TTRasterizer: %s%s", a->value->value, lf);
+  else if (type != PPDC_DRIVER_PS)
+    cupsFilePrintf(fp, "*TTRasterizer: Type42%s", lf);
+
+  struct lconv *loc = localeconv();
+
+  if (attrs->count)
+  {
+    // Write driver-defined attributes...
+    cupsFilePrintf(fp, "*%% Driver-defined attributes...%s", lf);
+    for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
+    {
+      if (!strcmp(a->name->value, "Product") ||
+          !strcmp(a->name->value, "PSVersion") ||
+          !strcmp(a->name->value, "LanguageLevel") ||
+          !strcmp(a->name->value, "DefaultColorSpace") ||
+          !strcmp(a->name->value, "FileSystem") ||
+          !strcmp(a->name->value, "LandscapeOrientation") ||
+          !strcmp(a->name->value, "TTRasterizer") ||
+          !strcmp(a->name->value, "LanguageVersion") ||
+          !strcmp(a->name->value, "LanguageEncoding") ||
+          !strcmp(a->name->value, "ModelName") ||
+          !strcmp(a->name->value, "NickName") ||
+          !strcmp(a->name->value, "ShortNickName") ||
+         !strcmp(a->name->value, "cupsVersion"))
+       continue;
+
+      if (a->name->value[0] == '?' &&
+          (find_option(a->name->value + 1) ||
+          !strcmp(a->name->value, "?ImageableArea") ||
+          !strcmp(a->name->value, "?PageRegion") ||
+          !strcmp(a->name->value, "?PageSize") ||
+          !strcmp(a->name->value, "?PaperDimension")))
+        continue;
+
+      if (!strncmp(a->name->value, "Custom", 6) &&
+          find_option(a->name->value + 6))
+       continue;
+
+      if (!strncmp(a->name->value, "ParamCustom", 11) &&
+          find_option(a->name->value + 11))
+       continue;
+
+      if (!a->selector->value || !a->selector->value[0])
+       cupsFilePrintf(fp, "*%s", a->name->value);
+      else if (!a->text->value || !a->text->value[0])
+       cupsFilePrintf(fp, "*%s %s", a->name->value, a->selector->value);
+      else
+       cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value,
+                      a->text->value);
+
+      if (strcmp(a->value->value, "False") &&
+          strcmp(a->value->value, "True") &&
+         strcmp(a->name->value, "1284Modes") &&
+         strcmp(a->name->value, "InkName") &&
+         strcmp(a->name->value, "PageStackOrder") &&
+         strncmp(a->name->value, "ParamCustom", 11) &&
+         strcmp(a->name->value, "Protocols") &&
+         strcmp(a->name->value, "ReferencePunch") &&
+         strncmp(a->name->value, "Default", 7))
+      {
+       cupsFilePrintf(fp, ": \"%s\"%s", a->value->value, lf);
+
+       if (strchr(a->value->value, '\n') || strchr(a->value->value, '\r'))
+          cupsFilePrintf(fp, "*End%s", lf);
+      }
+      else
+       cupsFilePrintf(fp, ": %s%s", a->value->value, lf);
+    }
+  }
+
+  if (type != PPDC_DRIVER_PS || filters->count)
+  {
+    if ((a = find_attr("cupsVersion", NULL)) != NULL)
+      cupsFilePrintf(fp, "*cupsVersion: %s%s", a->value->value, lf);
+    else
+      cupsFilePrintf(fp, "*cupsVersion: %d.%d%s", CUPS_VERSION_MAJOR,
+                    CUPS_VERSION_MINOR, lf);
+    cupsFilePrintf(fp, "*cupsModelNumber: %d%s", model_number, lf);
+    cupsFilePrintf(fp, "*cupsManualCopies: %s%s",
+                   manual_copies ? "True" : "False", lf);
+
+    if (filters->count)
+    {
+      for (f = (ppdcFilter *)filters->first();
+           f;
+          f = (ppdcFilter *)filters->next())
+       cupsFilePrintf(fp, "*cupsFilter: \"%s %d %s\"%s", f->mime_type->value,
+                      f->cost, f->program->value, lf);
+    }
+    else
+    {
+      switch (type)
+      {
+        case PPDC_DRIVER_LABEL :
+           cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
+                            "rastertolabel\"%s", lf);
+           break;
+
+        case PPDC_DRIVER_EPSON :
+           cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
+                            "rastertoepson\"%s", lf);
+           break;
+
+        case PPDC_DRIVER_ESCP :
+           cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-command 50 "
+                            "commandtoescpx\"%s", lf);
+           cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
+                            "rastertoescpx\"%s", lf);
+           break;
+
+        case PPDC_DRIVER_HP :
+           cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
+                            "rastertohp\"%s", lf);
+           break;
+
+        case PPDC_DRIVER_PCL :
+           cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-command 50 "
+                            "commandtopclx\"%s", lf);
+           cupsFilePrintf(fp, "*cupsFilter: \"application/vnd.cups-raster 50 "
+                            "rastertopclx\"%s", lf);
+           break;
+
+       default :
+           break;
+      }
+    }
+
+    for (p = (ppdcProfile *)profiles->first();
+         p;
+        p = (ppdcProfile *)profiles->next())
+    {
+      char density[255], gamma[255], profile[9][255];
+
+      cfStrFormatd(density, density + sizeof(density), p->density, loc);
+      cfStrFormatd(gamma, gamma + sizeof(gamma), p->gamma, loc);
+
+      for (int i = 0; i < 9; i ++)
+       cfStrFormatd(profile[i], profile[i] + sizeof(profile[0]),
+                       p->profile[i], loc);
+
+      cupsFilePrintf(fp,
+                     "*cupsColorProfile %s/%s: \"%s %s %s %s %s %s %s %s %s %s "
+                    "%s\"%s", p->resolution->value, p->media_type->value,
+                    density, gamma, profile[0], profile[1], profile[2],
+                    profile[3], profile[4], profile[5], profile[6], profile[7],
+                    profile[8], lf);
+    }
+  }
+
+  if (locales)
+  {
+    // Add localizations for additional languages...
+    ppdcString *locale;                // Locale name
+    ppdcCatalog        *locatalog;             // Message catalog for locale
+
+
+    // Write the list of languages...
+    cupsFilePrintf(fp, "*cupsLanguages: \"en");
+
+    for (locale = (ppdcString *)locales->first();
+         locale;
+        locale = (ppdcString *)locales->next())
+    {
+      // Skip (US) English...
+      if (!strcmp(locale->value, "en") || !strcmp(locale->value, "en_US"))
+        continue;
+
+      // See if we have a po file for this language...
+      if (!src->find_po(locale->value))
+      {
+        // No, see if we can use the base file?
+        locatalog = new ppdcCatalog(locale->value);
+
+       if (locatalog->messages->count == 0)
+       {
+         // No, skip this one...
+          fprintf(stderr,
+                 _("ppdc: No message catalog provided for locale "
+                   "%s.\n"), locale->value);
+          delete locatalog;
+          continue;
+       }
+
+        // Add the base file to the list...
+       src->po_files->add(locatalog);
+      }
+
+      cupsFilePrintf(fp, " %s", locale->value);
+    }
+
+    cupsFilePrintf(fp, "\"%s", lf);
+  }
+
+  for (cn = (ppdcConstraint *)constraints->first();
+       cn;
+       cn = (ppdcConstraint *)constraints->next())
+  {
+    // First constrain 1 against 2...
+    if (!strncmp(cn->option1->value, "*Custom", 7) ||
+        !strncmp(cn->option2->value, "*Custom", 7))
+      cupsFilePuts(fp, "*NonUIConstraints: ");
+    else
+      cupsFilePuts(fp, "*UIConstraints: ");
+
+    if (cn->option1->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePuts(fp, cn->option1->value);
+
+    if (cn->choice1->value)
+      cupsFilePrintf(fp, " %s", cn->choice1->value);
+
+    cupsFilePutChar(fp, ' ');
+
+    if (cn->option2->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePuts(fp, cn->option2->value);
+
+    if (cn->choice2->value)
+      cupsFilePrintf(fp, " %s", cn->choice2->value);
+
+    cupsFilePuts(fp, lf);
+
+    // Then constrain 2 against 1...
+    if (!strncmp(cn->option1->value, "*Custom", 7) ||
+        !strncmp(cn->option2->value, "*Custom", 7))
+      cupsFilePuts(fp, "*NonUIConstraints: ");
+    else
+      cupsFilePuts(fp, "*UIConstraints: ");
+
+    if (cn->option2->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePuts(fp, cn->option2->value);
+
+    if (cn->choice2->value)
+      cupsFilePrintf(fp, " %s", cn->choice2->value);
+
+    cupsFilePutChar(fp, ' ');
+
+    if (cn->option1->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePuts(fp, cn->option1->value);
+
+    if (cn->choice1->value)
+      cupsFilePrintf(fp, " %s", cn->choice1->value);
+
+    cupsFilePuts(fp, lf);
+  }
+
+  // PageSize option...
+  cupsFilePrintf(fp, "*OpenUI *PageSize/Media Size: PickOne%s", lf);
+  cupsFilePrintf(fp, "*OrderDependency: 10 AnySetup *PageSize%s", lf);
+  cupsFilePrintf(fp, "*DefaultPageSize: %s%s",
+                 default_size ? default_size->value : "Letter", lf);
+
+  for (m = (ppdcMediaSize *)sizes->first();
+       m;
+       m = (ppdcMediaSize *)sizes->next())
+    if (m->size_code->value)
+    {
+      cupsFilePrintf(fp, "*PageSize %s/%s: \"%s\"%s",
+                    m->name->value, catalog->find_message(m->text->value),
+                    m->size_code->value, lf);
+
+      if (strchr(m->size_code->value, '\n') ||
+          strchr(m->size_code->value, '\r'))
+        cupsFilePrintf(fp, "*End%s", lf);
+    }
+    else
+      cupsFilePrintf(fp,
+                     "*PageSize %s/%s: \"<</PageSize[%.0f %.0f]"
+                    "/ImagingBBox null>>setpagedevice\"%s",
+                    m->name->value, catalog->find_message(m->text->value),
+                    m->width, m->length, lf);
+
+  if ((a = find_attr("?PageSize", NULL)) != NULL)
+  {
+    cupsFilePrintf(fp, "*?PageSize: \"%s\"%s", a->value->value, lf);
+
+    if (strchr(a->value->value, '\n') ||
+        strchr(a->value->value, '\r'))
+      cupsFilePrintf(fp, "*End%s", lf);
+  }
+
+  cupsFilePrintf(fp, "*CloseUI: *PageSize%s", lf);
+
+  // PageRegion option...
+  cupsFilePrintf(fp, "*OpenUI *PageRegion/Media Size: PickOne%s", lf);
+  cupsFilePrintf(fp, "*OrderDependency: 10 AnySetup *PageRegion%s", lf);
+  cupsFilePrintf(fp, "*DefaultPageRegion: %s%s",
+                 default_size ? default_size->value : "Letter", lf);
+
+  for (m = (ppdcMediaSize *)sizes->first();
+       m;
+       m = (ppdcMediaSize *)sizes->next())
+    if (m->region_code->value)
+    {
+      cupsFilePrintf(fp, "*PageRegion %s/%s: \"%s\"%s",
+                    m->name->value, catalog->find_message(m->text->value),
+                    m->region_code->value, lf);
+
+      if (strchr(m->region_code->value, '\n') ||
+          strchr(m->region_code->value, '\r'))
+        cupsFilePrintf(fp, "*End%s", lf);
+    }
+    else
+      cupsFilePrintf(fp,
+                     "*PageRegion %s/%s: \"<</PageSize[%.0f %.0f]"
+                    "/ImagingBBox null>>setpagedevice\"%s",
+                    m->name->value, catalog->find_message(m->text->value),
+                    m->width, m->length, lf);
+
+  if ((a = find_attr("?PageRegion", NULL)) != NULL)
+  {
+    cupsFilePrintf(fp, "*?PageRegion: \"%s\"%s", a->value->value, lf);
+
+    if (strchr(a->value->value, '\n') ||
+        strchr(a->value->value, '\r'))
+      cupsFilePrintf(fp, "*End%s", lf);
+  }
+
+  cupsFilePrintf(fp, "*CloseUI: *PageRegion%s", lf);
+
+  // ImageableArea info...
+  cupsFilePrintf(fp, "*DefaultImageableArea: %s%s",
+                 default_size ? default_size->value : "Letter", lf);
+
+  char left[255], right[255], bottom[255], top[255];
+
+  for (m = (ppdcMediaSize *)sizes->first();
+       m;
+       m = (ppdcMediaSize *)sizes->next())
+  {
+    cfStrFormatd(left, left + sizeof(left), m->left, loc);
+    cfStrFormatd(bottom, bottom + sizeof(bottom), m->bottom, loc);
+    cfStrFormatd(right, right + sizeof(right), m->width - m->right, loc);
+    cfStrFormatd(top, top + sizeof(top), m->length - m->top, loc);
+
+    cupsFilePrintf(fp, "*ImageableArea %s/%s: \"%s %s %s %s\"%s",
+                   m->name->value, catalog->find_message(m->text->value),
+                  left, bottom, right, top, lf);
+  }
+
+  if ((a = find_attr("?ImageableArea", NULL)) != NULL)
+  {
+    cupsFilePrintf(fp, "*?ImageableArea: \"%s\"%s", a->value->value, lf);
+
+    if (strchr(a->value->value, '\n') ||
+        strchr(a->value->value, '\r'))
+      cupsFilePrintf(fp, "*End%s", lf);
+  }
+
+  // PaperDimension info...
+  cupsFilePrintf(fp, "*DefaultPaperDimension: %s%s",
+                 default_size ? default_size->value : "Letter", lf);
+
+  char width[255], length[255];
+
+  for (m = (ppdcMediaSize *)sizes->first();
+       m;
+       m = (ppdcMediaSize *)sizes->next())
+  {
+    cfStrFormatd(width, width + sizeof(width), m->width, loc);
+    cfStrFormatd(length, length + sizeof(length), m->length, loc);
+
+    cupsFilePrintf(fp, "*PaperDimension %s/%s: \"%s %s\"%s",
+                   m->name->value, catalog->find_message(m->text->value),
+                  width, length, lf);
+  }
+
+  if ((a = find_attr("?PaperDimension", NULL)) != NULL)
+  {
+    cupsFilePrintf(fp, "*?PaperDimension: \"%s\"%s", a->value->value, lf);
+
+    if (strchr(a->value->value, '\n') ||
+        strchr(a->value->value, '\r'))
+      cupsFilePrintf(fp, "*End%s", lf);
+  }
+
+  // Custom size support...
+  if (variable_paper_size)
+  {
+    cfStrFormatd(width, width + sizeof(width), max_width, loc);
+    cfStrFormatd(length, length + sizeof(length), max_length, loc);
+
+    cfStrFormatd(left, left + sizeof(left), left_margin, loc);
+    cfStrFormatd(bottom, bottom + sizeof(bottom), bottom_margin, loc);
+    cfStrFormatd(right, right + sizeof(right), right_margin, loc);
+    cfStrFormatd(top, top + sizeof(top), top_margin, loc);
+
+    cupsFilePrintf(fp, "*MaxMediaWidth: \"%s\"%s", width, lf);
+    cupsFilePrintf(fp, "*MaxMediaHeight: \"%s\"%s", length, lf);
+    cupsFilePrintf(fp, "*HWMargins: %s %s %s %s%s", left, bottom, right, top,
+                   lf);
+
+    if (custom_size_code && custom_size_code->value)
+    {
+      cupsFilePrintf(fp, "*CustomPageSize True: \"%s\"%s",
+                     custom_size_code->value, lf);
+
+      if (strchr(custom_size_code->value, '\n') ||
+          strchr(custom_size_code->value, '\r'))
+        cupsFilePrintf(fp, "*End%s", lf);
+    }
+    else
+      cupsFilePrintf(fp,
+                    "*CustomPageSize True: \"pop pop pop <</PageSize[5 -2 roll]"
+                    "/ImagingBBox null>>setpagedevice\"%s", lf);
+
+    if ((a = find_attr("ParamCustomPageSize", "Width")) != NULL)
+      cupsFilePrintf(fp, "*ParamCustomPageSize Width: %s%s", a->value->value,
+                    lf);
+    else
+    {
+      char width0[255];
+
+      cfStrFormatd(width0, width0 + sizeof(width0), min_width, loc);
+      cfStrFormatd(width, width + sizeof(width), max_width, loc);
+
+      cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %s %s%s",
+                     width0, width, lf);
+    }
+
+    if ((a = find_attr("ParamCustomPageSize", "Height")) != NULL)
+      cupsFilePrintf(fp, "*ParamCustomPageSize Height: %s%s", a->value->value,
+                    lf);
+    else
+    {
+      char length0[255];
+
+      cfStrFormatd(length0, length0 + sizeof(length0), min_length, loc);
+      cfStrFormatd(length, length + sizeof(length), max_length, loc);
+
+      cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %s %s%s",
+                     length0, length, lf);
+    }
+
+    if ((a = find_attr("ParamCustomPageSize", "WidthOffset")) != NULL)
+      cupsFilePrintf(fp, "*ParamCustomPageSize WidthOffset: %s%s",
+                     a->value->value, lf);
+    else
+      cupsFilePrintf(fp, "*ParamCustomPageSize WidthOffset: 3 points 0 0%s", lf);
+
+    if ((a = find_attr("ParamCustomPageSize", "HeightOffset")) != NULL)
+      cupsFilePrintf(fp, "*ParamCustomPageSize HeightOffset: %s%s",
+                     a->value->value, lf);
+    else
+      cupsFilePrintf(fp, "*ParamCustomPageSize HeightOffset: 4 points 0 0%s", lf);
+
+    if ((a = find_attr("ParamCustomPageSize", "Orientation")) != NULL)
+      cupsFilePrintf(fp, "*ParamCustomPageSize Orientation: %s%s",
+                     a->value->value, lf);
+    else
+      cupsFilePrintf(fp, "*ParamCustomPageSize Orientation: 5 int 0 0%s", lf);
+  }
+
+  // All other options...
+  for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
+  {
+    if (!g->options->count)
+      continue;
+
+    if (strcasecmp(g->name->value, "General"))
+      cupsFilePrintf(fp, "*OpenGroup: %s/%s%s", g->name->value,
+                     catalog->find_message(g->text->value), lf);
+
+    for (o = (ppdcOption *)g->options->first();
+         o;
+        o = (ppdcOption *)g->options->next())
+    {
+      if (!o->choices->count)
+        continue;
+
+      if (o->section == PPDC_SECTION_JCL)
+      {
+       if (!o->text->value)
+         cupsFilePrintf(fp, "*JCLOpenUI *%s/%s: ", o->name->value,
+                        catalog->find_message(o->name->value));
+       else
+         cupsFilePrintf(fp, "*JCLOpenUI *%s/%s: ", o->name->value,
+                        catalog->find_message(o->text->value));
+      }
+      else if (!o->text->value)
+       cupsFilePrintf(fp, "*OpenUI *%s/%s: ", o->name->value,
+                      catalog->find_message(o->name->value));
+      else
+       cupsFilePrintf(fp, "*OpenUI *%s/%s: ", o->name->value,
+                      catalog->find_message(o->text->value));
+
+      switch (o->type)
+      {
+        case PPDC_BOOLEAN :
+           cupsFilePrintf(fp, "Boolean%s", lf);
+           break;
+        default :
+           cupsFilePrintf(fp, "PickOne%s", lf);
+           break;
+        case PPDC_PICKMANY :
+           cupsFilePrintf(fp, "PickMany%s", lf);
+           break;
+      }
+
+      char order[255];
+      cfStrFormatd(order, order + sizeof(order), o->order, loc);
+
+      cupsFilePrintf(fp, "*OrderDependency: %s ", order);
+      switch (o->section)
+      {
+        default :
+           cupsFilePrintf(fp, "AnySetup");
+           break;
+        case PPDC_SECTION_DOCUMENT :
+           cupsFilePrintf(fp, "DocumentSetup");
+           break;
+        case PPDC_SECTION_EXIT :
+           cupsFilePrintf(fp, "ExitServer");
+           break;
+        case PPDC_SECTION_JCL :
+           cupsFilePrintf(fp, "JCLSetup");
+           break;
+        case PPDC_SECTION_PAGE :
+           cupsFilePrintf(fp, "PageSetup");
+           break;
+        case PPDC_SECTION_PROLOG :
+           cupsFilePrintf(fp, "Prolog");
+           break;
+      }
+
+      cupsFilePrintf(fp, " *%s%s", o->name->value, lf);
+
+      if (o->defchoice)
+      {
+        // Use the programmer-supplied default...
+        cupsFilePrintf(fp, "*Default%s: %s%s", o->name->value,
+                      o->defchoice->value, lf);
+      }
+      else
+      {
+        // Make the first choice the default...
+        c = (ppdcChoice *)o->choices->first();
+        cupsFilePrintf(fp, "*Default%s: %s%s", o->name->value, c->name->value,
+                      lf);
+      }
+
+      for (c = (ppdcChoice *)o->choices->first();
+           c;
+          c = (ppdcChoice *)o->choices->next())
+      {
+        // Write this choice...
+       if (!c->text->value)
+          cupsFilePrintf(fp, "*%s %s/%s: \"%s\"%s", o->name->value,
+                         c->name->value, catalog->find_message(c->name->value),
+                        c->code->value, lf);
+        else
+          cupsFilePrintf(fp, "*%s %s/%s: \"%s\"%s", o->name->value,
+                        c->name->value, catalog->find_message(c->text->value),
+                        c->code->value, lf);
+
+       // Multi-line commands need a *End line to terminate them.
+        if (strchr(c->code->value, '\n') ||
+           strchr(c->code->value, '\r'))
+         cupsFilePrintf(fp, "*End%s", lf);
+      }
+
+      snprintf(query, sizeof(query), "?%s", o->name->value);
+
+      if ((a = find_attr(query, NULL)) != NULL)
+      {
+       cupsFilePrintf(fp, "*%s: \"%s\"%s", query, a->value->value, lf);
+
+       if (strchr(a->value->value, '\n') ||
+            strchr(a->value->value, '\r'))
+         cupsFilePrintf(fp, "*End%s", lf);
+      }
+
+      if (o->section == PPDC_SECTION_JCL)
+       cupsFilePrintf(fp, "*JCLCloseUI: *%s%s", o->name->value, lf);
+      else
+       cupsFilePrintf(fp, "*CloseUI: *%s%s", o->name->value, lf);
+
+      snprintf(custom, sizeof(custom), "Custom%s", o->name->value);
+      if ((a = find_attr(custom, "True")) != NULL)
+      {
+        // Output custom option information...
+        cupsFilePrintf(fp, "*%s True: \"%s\"%s", custom, a->value->value, lf);
+       if (strchr(a->value->value, '\n') || strchr(a->value->value, '\r'))
+         cupsFilePrintf(fp, "*End%s", lf);
+
+        snprintf(custom, sizeof(custom), "ParamCustom%s", o->name->value);
+       for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
+       {
+         if (strcmp(a->name->value, custom))
+           continue;
+
+         if (!a->selector->value || !a->selector->value[0])
+           cupsFilePrintf(fp, "*%s", a->name->value);
+         else if (!a->text->value || !a->text->value[0])
+           cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value,
+                          catalog->find_message(a->selector->value));
+         else
+           cupsFilePrintf(fp, "*%s %s/%s", a->name->value, a->selector->value,
+                          catalog->find_message(a->text->value));
+
+          cupsFilePrintf(fp, ": %s%s", a->value->value, lf);
+       }
+      }
+    }
+
+    if (strcasecmp(g->name->value, "General"))
+      cupsFilePrintf(fp, "*CloseGroup: %s%s", g->name->value, lf);
+  }
+
+  if (locales)
+  {
+    // Add localizations for additional languages...
+    ppdcString *locale;                // Locale name
+    ppdcCatalog        *locatalog;             // Message catalog for locale
+
+
+    // Write the translation strings for each language...
+    for (locale = (ppdcString *)locales->first();
+         locale;
+        locale = (ppdcString *)locales->next())
+    {
+      // Skip (US) English...
+      if (!strcmp(locale->value, "en") || !strcmp(locale->value, "en_US"))
+        continue;
+
+      // Skip missing languages...
+      if ((locatalog = src->find_po(locale->value)) == NULL)
+        continue;
+
+      // Do the core stuff first...
+      cupsFilePrintf(fp, "*%s.Translation Manufacturer/%s: \"\"%s",
+                     locale->value,
+                    locatalog->find_message(manufacturer->value), lf);
+
+      if ((a = find_attr("ModelName", NULL)) != NULL)
+       cupsFilePrintf(fp, "*%s.Translation ModelName/%s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(a->value->value), lf);
+      else if (strncasecmp(model_name->value, manufacturer->value,
+                          strlen(manufacturer->value)))
+       cupsFilePrintf(fp, "*%s.Translation ModelName/%s %s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(manufacturer->value),
+                      locatalog->find_message(model_name->value), lf);
+      else
+       cupsFilePrintf(fp, "*%s.Translation ModelName/%s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(model_name->value), lf);
+
+      if ((a = find_attr("ShortNickName", NULL)) != NULL)
+       cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(a->value->value), lf);
+      else if (strncasecmp(model_name->value, manufacturer->value,
+                          strlen(manufacturer->value)))
+       cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s %s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(manufacturer->value),
+                      locatalog->find_message(model_name->value), lf);
+      else
+       cupsFilePrintf(fp, "*%s.Translation ShortNickName/%s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(model_name->value), lf);
+
+      if ((a = find_attr("NickName", NULL)) != NULL)
+       cupsFilePrintf(fp, "*%s.Translation NickName/%s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(a->value->value), lf);
+      else if (strncasecmp(model_name->value, manufacturer->value,
+                          strlen(manufacturer->value)))
+       cupsFilePrintf(fp, "*%s.Translation NickName/%s %s, %s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(manufacturer->value),
+                      locatalog->find_message(model_name->value),
+                      version->value, lf);
+      else
+       cupsFilePrintf(fp, "*%s.Translation NickName/%s, %s: \"\"%s",
+                       locale->value,
+                      locatalog->find_message(model_name->value),
+                      version->value, lf);
+
+      // Then the page sizes...
+      cupsFilePrintf(fp, "*%s.Translation PageSize/%s: \"\"%s", locale->value,
+                     locatalog->find_message("Media Size"), lf);
+
+      for (m = (ppdcMediaSize *)sizes->first();
+          m;
+          m = (ppdcMediaSize *)sizes->next())
+      {
+        cupsFilePrintf(fp, "*%s.PageSize %s/%s: \"\"%s", locale->value,
+                      m->name->value, locatalog->find_message(m->text->value),
+                      lf);
+      }
+
+      // Next the groups and options...
+      for (g = (ppdcGroup *)groups->first(); g; g = (ppdcGroup *)groups->next())
+      {
+       if (!g->options->count)
+         continue;
+
+       if (strcasecmp(g->name->value, "General"))
+         cupsFilePrintf(fp, "*%s.Translation %s/%s: \"\"%s", locale->value,
+                        g->name->value,
+                        locatalog->find_message(g->text->value), lf);
+
+       for (o = (ppdcOption *)g->options->first();
+             o;
+            o = (ppdcOption *)g->options->next())
+       {
+         if (!o->choices->count)
+            continue;
+
+          cupsFilePrintf(fp, "*%s.Translation %s/%s: \"\"%s", locale->value,
+                        o->name->value,
+                        locatalog->find_message(o->text->value ?
+                                                o->text->value :
+                                                o->name->value), lf);
+
+         for (c = (ppdcChoice *)o->choices->first();
+               c;
+              c = (ppdcChoice *)o->choices->next())
+         {
+            // Write this choice...
+            cupsFilePrintf(fp, "*%s.%s %s/%s: \"\"%s", locale->value,
+                          o->name->value, c->name->value,
+                          locatalog->find_message(c->text->value ?
+                                                  c->text->value :
+                                                  c->name->value), lf);
+         }
+       }
+      }
+
+      // Finally the localizable attributes...
+      for (a = (ppdcAttr *)attrs->first(); a; a = (ppdcAttr *)attrs->next())
+      {
+        if (!a->localizable &&
+           (!a->text || !a->text->value || !a->text->value[0]) &&
+           strcmp(a->name->value, "APCustomColorMatchingName") &&
+           strcmp(a->name->value, "APPrinterPreset") &&
+           strcmp(a->name->value, "cupsICCProfile") &&
+           strcmp(a->name->value, "cupsIPPReason") &&
+           strcmp(a->name->value, "cupsMarkerName") &&
+           strncmp(a->name->value, "Custom", 6) &&
+           strncmp(a->name->value, "ParamCustom", 11))
+         continue;
+
+        cupsFilePrintf(fp, "*%s.%s %s/%s: \"%s\"%s", locale->value,
+                      a->name->value, a->selector->value,
+                      locatalog->find_message(a->text && a->text->value ?
+                                              a->text->value : a->name->value),
+                      ((a->localizable && a->value->value[0]) ||
+                       !strcmp(a->name->value, "cupsIPPReason")) ?
+                          locatalog->find_message(a->value->value) : "",
+                      lf);
+      }
+    }
+  }
+
+  if (default_font && default_font->value)
+    cupsFilePrintf(fp, "*DefaultFont: %s%s", default_font->value, lf);
+  else
+    cupsFilePrintf(fp, "*DefaultFont: Courier%s", lf);
+
+  for (fn = (ppdcFont *)fonts->first(); fn; fn = (ppdcFont *)fonts->next())
+    if (!strcmp(fn->name->value, "*"))
+    {
+      for (bfn = (ppdcFont *)src->base_fonts->first();
+          bfn;
+          bfn = (ppdcFont *)src->base_fonts->next())
+       cupsFilePrintf(fp, "*Font %s: %s \"%s\" %s %s%s",
+                      bfn->name->value, bfn->encoding->value,
+                      bfn->version->value, bfn->charset->value,
+                      bfn->status == PPDC_FONT_ROM ? "ROM" : "Disk", lf);
+    }
+    else
+      cupsFilePrintf(fp, "*Font %s: %s \"%s\" %s %s%s",
+                    fn->name->value, fn->encoding->value, fn->version->value,
+                    fn->charset->value,
+                    fn->status == PPDC_FONT_ROM ? "ROM" : "Disk", lf);
+
+  cupsFilePrintf(fp, "*%% End of %s, %05d bytes.%s", pc_file_name->value,
+                (int)((size_t)cupsFileTell(fp) + 25 + strlen(pc_file_name->value)),
+                lf);
+
+  if (delete_cat)
+    catalog->release();
+
+  return (0);
+}
diff --git a/ppdc/ppdc-file.cxx b/ppdc/ppdc-file.cxx
new file mode 100644 (file)
index 0000000..484d9f1
--- /dev/null
@@ -0,0 +1,92 @@
+//
+// File class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2010 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcFile::ppdcFile()' - Create (open) a file.
+//
+
+ppdcFile::ppdcFile(const char  *f,             // I - File to open
+                   cups_file_t *ffp)           // I - File pointer to use
+{
+  if (ffp)
+  {
+    fp = ffp;
+    cupsFileRewind(fp);
+  }
+  else
+    fp = cupsFileOpen(f, "r");
+
+  close_on_delete = !ffp;
+  filename        = f;
+  line            = 1;
+
+  if (!fp)
+    fprintf(stderr, _("ppdc: Unable to open %s: %s\n"), f,
+           strerror(errno));
+}
+
+
+//
+// 'ppdcFile::~ppdcFile()' - Delete (close) a file.
+//
+
+ppdcFile::~ppdcFile()
+{
+  if (close_on_delete && fp)
+    cupsFileClose(fp);
+}
+
+
+//
+// 'ppdcFile::get()' - Get a character from a file.
+//
+
+int
+ppdcFile::get()
+{
+  int  ch;                                     // Character from file
+
+
+  // Return EOF if there is no open file...
+  if (!fp)
+    return (EOF);
+
+  // Get the character...
+  ch = cupsFileGetChar(fp);
+
+  // Update the line number as needed...
+  if (ch == '\n')
+    line ++;
+
+  // Return the character...
+  return (ch);
+}
+
+
+//
+// 'ppdcFile::peek()' - Look at the next character from a file.
+//
+
+int                                    // O - Next character in file
+ppdcFile::peek()
+{
+  // Return immediaely if there is no open file...
+  if (!fp)
+    return (EOF);
+
+  // Otherwise return the next character without advancing...
+  return (cupsFilePeekChar(fp));
+}
diff --git a/ppdc/ppdc-filter.cxx b/ppdc/ppdc-filter.cxx
new file mode 100644 (file)
index 0000000..e85d121
--- /dev/null
@@ -0,0 +1,44 @@
+//
+// Filter class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcFilter::ppdcFilter()' - Create a filter.
+//
+
+ppdcFilter::ppdcFilter(const char *t,  // I - MIME type
+                      const char *p,   // I - Filter program
+                      int        c)    // I - Relative cost
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  mime_type = new ppdcString(t);
+  program   = new ppdcString(p);
+  cost      = c;
+}
+
+
+//
+// 'ppdcFilter::~ppdcFilter()' - Destroy a filter.
+//
+
+ppdcFilter::~ppdcFilter()
+{
+  PPDC_DELETE;
+
+  mime_type->release();
+  program->release();
+}
diff --git a/ppdc/ppdc-font.cxx b/ppdc/ppdc-font.cxx
new file mode 100644 (file)
index 0000000..8a9f65a
--- /dev/null
@@ -0,0 +1,50 @@
+//
+// Shared font class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcFont::ppdcFont()' - Create a shared font.
+//
+
+ppdcFont::ppdcFont(const char     *n,          // I - Name of font
+                   const char     *e,          // I - Font encoding
+                  const char     *v,           // I - Font version
+                  const char     *c,           // I - Font charset
+                  ppdcFontStatus s)            // I - Font status
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  name     = new ppdcString(n);
+  encoding = new ppdcString(e);
+  version  = new ppdcString(v);
+  charset  = new ppdcString(c);
+  status   = s;
+}
+
+
+//
+// 'ppdcFont::~ppdcFont()' - Destroy a shared font.
+//
+
+ppdcFont::~ppdcFont()
+{
+  PPDC_DELETE;
+
+  name->release();
+  encoding->release();
+  version->release();
+  charset->release();
+}
diff --git a/ppdc/ppdc-group.cxx b/ppdc/ppdc-group.cxx
new file mode 100644 (file)
index 0000000..dc94284
--- /dev/null
@@ -0,0 +1,86 @@
+//
+// Group class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2011 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcGroup::ppdcGroup()' - Create a new group.
+//
+
+ppdcGroup::ppdcGroup(const char *n,    // I - Name of group
+                     const char *t)    // I - Text of group
+{
+  PPDC_NEWVAL(n);
+
+  name    = new ppdcString(n);
+  text    = new ppdcString(t);
+  options = new ppdcArray();
+}
+
+
+//
+// 'ppdcGroup::ppdcGroup()' - Copy a new group.
+//
+
+ppdcGroup::ppdcGroup(ppdcGroup *g)     // I - Group template
+{
+  PPDC_NEWVAL(g->name->value);
+
+  g->name->retain();
+  g->text->retain();
+
+  name = g->name;
+  text = g->text;
+
+  options = new ppdcArray();
+  for (ppdcOption *o = (ppdcOption *)g->options->first();
+       o;
+       o = (ppdcOption *)g->options->next())
+    options->add(new ppdcOption(o));
+}
+
+
+//
+// 'ppdcGroup::~ppdcGroup()' - Destroy a group.
+//
+
+ppdcGroup::~ppdcGroup()
+{
+  PPDC_DELETEVAL(name ? name->value : NULL);
+
+  name->release();
+  text->release();
+  options->release();
+
+  name = text = 0;
+  options = 0;
+}
+
+
+//
+// 'ppdcGroup::find_option()' - Find an option in a group.
+//
+
+ppdcOption *
+ppdcGroup::find_option(const char *n)  // I - Name of option
+{
+  ppdcOption   *o;                     // Current option
+
+
+  for (o = (ppdcOption *)options->first(); o; o = (ppdcOption *)options->next())
+    if (!strcasecmp(n, o->name->value))
+      return (o);
+
+  return (0);
+}
diff --git a/ppdc/ppdc-import.cxx b/ppdc/ppdc-import.cxx
new file mode 100644 (file)
index 0000000..0f04e42
--- /dev/null
@@ -0,0 +1,332 @@
+//
+// PPD file import methods for the CUPS PPD Compiler.
+//
+// Copyright 2007-2011 by Apple Inc.
+// Copyright 2002-2006 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#include <ppd/ppd.h>
+
+
+//
+// 'ppdcSource::import_ppd()' - Import a PPD file.
+//
+
+int                                    // O - 1 on success, 0 on failure
+ppdcSource::import_ppd(const char *f)  // I - Filename
+{
+  int          i, j, k;                // Looping vars
+  cups_file_t  *fp;                    // File
+  char         line[256],              // Comment line
+               *ptr;                   // Pointer into line
+  int          cost;                   // Cost for filter
+  ppd_file_t   *ppd;                   // PPD file data
+  ppd_group_t  *group;                 // PPD group
+  ppd_option_t *option;                // PPD option
+  ppd_choice_t *choice;                // PPD choice
+  ppd_attr_t   *attr;                  // PPD attribute
+  ppd_const_t  *constraint;            // PPD UI constraint
+  ppd_const_t  *constraint2;           // Temp PPD UI constraint
+  ppd_size_t   *size;                  // PPD page size
+  ppdcDriver   *driver;                // Driver
+  ppdcFilter   *filter;                // Current filter
+  ppdcFont     *font;                  // Font
+  ppdcGroup    *cgroup;                // UI group
+  ppdcOption   *coption;               // UI option
+  ppdcChoice   *cchoice;               // UI choice
+  ppdcConstraint *cconstraint;         // UI constraint
+  ppdcMediaSize        *csize;                 // Media size
+
+
+  // Try opening the PPD file...
+  if ((ppd = ppdOpenFile(f)) == NULL)
+    return (0);
+
+  // All PPD files need a PCFileName attribute...
+  if (!ppd->pcfilename)
+  {
+    ppdClose(ppd);
+    return (0);
+  }
+
+  // See if the driver has already been imported...
+  if (find_driver(ppd->pcfilename))
+  {
+    ppdClose(ppd);
+    return (1);
+  }
+
+  // Create a new PPD file...
+  if ((fp = cupsFileOpen(f, "r")) == NULL)
+  {
+    ppdClose(ppd);
+    return (0);
+  }
+
+  driver       = new ppdcDriver();
+  driver->type = PPDC_DRIVER_PS;
+
+  drivers->add(driver);
+
+  // Read the initial comments from the PPD file and use them as the
+  // copyright/license text...
+  cupsFileGets(fp, line, sizeof(line));
+                                     // Skip *PPD-Adobe-M.m
+
+  while (cupsFileGets(fp, line, sizeof(line)))
+    if (strncmp(line, "*%", 2))
+      break;
+    else if (strncmp(line, "*%%%% ", 6))
+    {
+      for (ptr = line + 2; isspace(*ptr); ptr ++);
+
+      driver->add_copyright(ptr);
+    }
+
+  cupsFileClose(fp);
+
+  // Then add the stuff from the PPD file...
+  if (ppd->modelname && ppd->manufacturer &&
+      !strncasecmp(ppd->modelname, ppd->manufacturer,
+                  strlen(ppd->manufacturer)))
+  {
+    ptr = ppd->modelname + strlen(ppd->manufacturer);
+
+    while (isspace(*ptr))
+      ptr ++;
+  }
+  else
+    ptr = ppd->modelname;
+
+  if (ppd->nickname)
+    driver->add_attr(new ppdcAttr("NickName", NULL, NULL, ppd->nickname));
+
+  if (ppd->shortnickname)
+    driver->add_attr(new ppdcAttr("ShortNickName", NULL, NULL,
+                                 ppd->shortnickname));
+
+  driver->manufacturer        = new ppdcString(ppd->manufacturer);
+  driver->model_name          = new ppdcString(ptr);
+  driver->pc_file_name        = new ppdcString(ppd->pcfilename);
+  attr = ppdFindAttr(ppd, "FileVersion", NULL);
+  driver->version             = new ppdcString(attr ? attr->value : NULL);
+  driver->model_number        = ppd->model_number;
+  driver->manual_copies       = ppd->manual_copies;
+  driver->color_device        = ppd->color_device;
+  driver->throughput          = ppd->throughput;
+  driver->variable_paper_size = ppd->variable_sizes;
+  driver->max_width           = ppd->custom_max[0];
+  driver->max_length          = ppd->custom_max[1];
+  driver->min_width           = ppd->custom_min[0];
+  driver->min_length          = ppd->custom_min[1];
+  driver->left_margin         = ppd->custom_margins[0];
+  driver->bottom_margin       = ppd->custom_margins[1];
+  driver->right_margin        = ppd->custom_margins[2];
+  driver->top_margin          = ppd->custom_margins[3];
+
+  for (i = 0; i < ppd->num_filters; i ++)
+  {
+    strncpy(line, ppd->filters[i], sizeof(line) - 1);
+
+    for (ptr = line; *ptr; ptr ++)
+      if (isspace(*ptr & 255))
+       break;
+    *ptr++ = '\0';
+
+    cost = strtol(ptr, &ptr, 10);
+
+    while (isspace(*ptr & 255))
+      ptr ++;
+
+    filter = new ppdcFilter(line, ptr, cost);
+    driver->add_filter(filter);
+  }
+
+  attr = ppdFindAttr(ppd, "DefaultFont", NULL);
+  driver->default_font  = new ppdcString(attr ? attr->value : NULL);
+
+  // Collect media sizes...
+  ppd_option_t *region_option,         // PageRegion option
+                     *size_option;             // PageSize option
+  ppd_choice_t *region_choice,         // PageRegion choice
+                     *size_choice;             // PageSize choice
+
+  region_option = ppdFindOption(ppd, "PageRegion");
+  size_option   = ppdFindOption(ppd, "PageSize");
+
+  for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+  {
+    // Don't do custom size here...
+    if (!strcasecmp(size->name, "Custom"))
+      continue;
+
+    // Get the code for the PageSize and PageRegion options...
+    region_choice = ppdFindChoice(region_option, size->name);
+    size_choice   = ppdFindChoice(size_option, size->name);
+
+    // Create a new media size record and add it to the driver...
+    csize = new ppdcMediaSize(size->name, size_choice->text, size->width,
+                             size->length, size->left, size->bottom,
+                             size->width - size->right,
+                             size->length - size->top,
+                             size_choice->code, region_choice->code);
+
+     driver->add_size(csize);
+
+     if (!strcasecmp(size_option->defchoice, size->name))
+       driver->set_default_size(csize);
+  }
+
+  // Now all of the options...
+  for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+  {
+    cgroup = new ppdcGroup(group->name, group->text);
+    driver->add_group(cgroup);
+
+    for (j = group->num_options, option = group->options; j > 0; j --, option ++)
+    {
+      if (!strcmp(option->keyword, "PageSize") || !strcmp(option->keyword, "PageRegion"))
+       continue;
+
+      coption = new ppdcOption((ppdcOptType)option->ui, option->keyword,
+                              option->text, (ppdcOptSection)option->section,
+                              option->order);
+      cgroup->add_option(coption);
+
+      for (k = option->num_choices, choice = option->choices; k > 0; k --, choice ++)
+      {
+       if (!strcmp(choice->choice, "Custom"))
+         continue;
+
+       cchoice = new ppdcChoice(choice->choice, choice->text, choice->code);
+       coption->add_choice(cchoice);
+
+       if (!strcasecmp(option->defchoice, choice->choice))
+         coption->set_defchoice(cchoice);
+      }
+    }
+  }
+
+  // Now the constraints...
+  for (i = ppd->num_consts, constraint = ppd->consts;
+       i > 0;
+       i --, constraint ++)
+  {
+    // Look for mirrored constraints...
+    for (j = i - 1, constraint2 = constraint + 1;
+        j > 0;
+        j --, constraint2 ++)
+      if (!strcmp(constraint->option1, constraint2->option2) &&
+         !strcmp(constraint->choice1, constraint2->choice2) &&
+         !strcmp(constraint->option2, constraint2->option1) &&
+         !strcmp(constraint->choice2, constraint2->choice1))
+       break;
+
+    if (j)
+      continue;
+
+    cconstraint = new ppdcConstraint(constraint->option2, constraint->choice2,
+                                    constraint->option1, constraint->choice1);
+    driver->add_constraint(cconstraint);
+  }
+
+  for (i = 0; i < ppd->num_attrs; i ++)
+  {
+    attr = ppd->attrs[i];
+
+    if (!strcmp(attr->name, "Font"))
+    {
+      // Font...
+      char             encoding[256],  // Encoding string
+                     version[256],     // Version string
+                     charset[256],     // Charset string
+                     status[256];      // Status string
+      ppdcFontStatus   fstatus;        // Status enumeration
+
+
+      if (sscanf(attr->value, "%s%*[^\"]\"%[^\"]\"%s%s", encoding, version,
+                charset, status) != 4)
+      {
+       fprintf(stderr, _("ppdc: Bad font attribute: %s\n"),
+               attr->value);
+       continue;
+      }
+
+      if (!strcmp(status, "ROM"))
+       fstatus = PPDC_FONT_ROM;
+      else
+       fstatus = PPDC_FONT_DISK;
+
+      font = new ppdcFont(attr->spec, encoding, version, charset, fstatus);
+
+      driver->add_font(font);
+    }
+    else if (!strcmp(attr->name, "CustomPageSize"))
+    {
+      driver->set_custom_size_code(attr->value);
+    }
+    else if ((strncmp(attr->name, "Default", 7) ||
+             !strcmp(attr->name, "DefaultColorSpace")) &&
+            strcmp(attr->name, "ColorDevice") &&
+            strcmp(attr->name, "Manufacturer") &&
+            strcmp(attr->name, "ModelName") &&
+            strcmp(attr->name, "MaxMediaHeight") &&
+            strcmp(attr->name, "MaxMediaWidth") &&
+            strcmp(attr->name, "NickName") &&
+            strcmp(attr->name, "ParamCustomPageSize") &&
+            strcmp(attr->name, "ShortNickName") &&
+            strcmp(attr->name, "Throughput") &&
+            strcmp(attr->name, "PCFileName") &&
+            strcmp(attr->name, "FileVersion") &&
+            strcmp(attr->name, "FormatVersion") &&
+            strcmp(attr->name, "HWMargins") &&
+            strcmp(attr->name, "VariablePaperSize") &&
+            strcmp(attr->name, "LanguageEncoding") &&
+            strcmp(attr->name, "LanguageVersion") &&
+            strcmp(attr->name, "cupsFilter") &&
+            strcmp(attr->name, "cupsFlipDuplex") &&
+            strcmp(attr->name, "cupsLanguages") &&
+            strcmp(attr->name, "cupsManualCopies") &&
+            strcmp(attr->name, "cupsModelNumber") &&
+            strcmp(attr->name, "cupsVersion"))
+    {
+      if ((ptr = strchr(attr->name, '.')) != NULL &&
+         ((ptr - attr->name) == 2 || (ptr - attr->name) == 5))
+      {
+       // Might be a localization attribute; test further...
+       if (isalpha(attr->name[0] & 255) &&
+           isalpha(attr->name[1] & 255) &&
+           (attr->name[2] == '.' ||
+            (attr->name[2] == '_' && isalpha(attr->name[3] & 255) &&
+             isalpha(attr->name[4] & 255))))
+         continue;
+      }
+
+      // Attribute...
+      driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text,
+                                   attr->value));
+    }
+    else if (!strncmp(attr->name, "Default", 7) &&
+            !ppdFindOption(ppd, attr->name + 7) &&
+            strcmp(attr->name, "DefaultFont") &&
+            strcmp(attr->name, "DefaultImageableArea") &&
+            strcmp(attr->name, "DefaultPaperDimension") &&
+            strcmp(attr->name, "DefaultFont"))
+    {
+      // Default attribute...
+      driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text,
+                                   attr->value));
+    }
+  }
+
+  ppdClose(ppd);
+
+  return (1);
+}
diff --git a/ppdc/ppdc-mediasize.cxx b/ppdc/ppdc-mediasize.cxx
new file mode 100644 (file)
index 0000000..a93d7e7
--- /dev/null
@@ -0,0 +1,69 @@
+//
+// Shared media size class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcMediaSize::ppdcMediaSize()' - Create a new media size.
+//
+
+ppdcMediaSize::ppdcMediaSize(const char *n,    // I - Name of media size
+                             const char *t,    // I - Text of media size
+                            float      w,      // I - Width in points
+                            float      l,      // I - Length in points
+                             float      lm,    // I - Left margin in points
+                            float      bm,     // I - Bottom margin in points
+                            float      rm,     // I - Right margin in points
+                            float      tm,     // I - Top margin in points
+                            const char *sc,    // I - PageSize code, if any
+                            const char *rc)    // I - PageRegion code, if any
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  name        = new ppdcString(n);
+  text        = new ppdcString(t);
+  width       = w;
+  length      = l;
+  left        = lm;
+  bottom      = bm;
+  right       = rm;
+  top         = tm;
+  size_code   = new ppdcString(sc);
+  region_code = new ppdcString(rc);
+
+  if (left < 0.0f)
+    left = 0.0f;
+  if (bottom < 0.0f)
+    bottom = 0.0f;
+  if (right < 0.0f)
+    right = 0.0f;
+  if (top < 0.0f)
+    top = 0.0f;
+}
+
+
+//
+// 'ppdcMediaSize::~ppdcMediaSize()' - Destroy a media size.
+//
+
+ppdcMediaSize::~ppdcMediaSize()
+{
+  PPDC_DELETE;
+
+  name->release();
+  text->release();
+  size_code->release();
+  region_code->release();
+}
diff --git a/ppdc/ppdc-message.cxx b/ppdc/ppdc-message.cxx
new file mode 100644 (file)
index 0000000..8d6ec7e
--- /dev/null
@@ -0,0 +1,42 @@
+//
+// Shared message class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcMessage::ppdcMessage()' - Create a shared message.
+//
+
+ppdcMessage::ppdcMessage(const char *i,        // I - ID
+                         const char *s)        // I - Text
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  id     = new ppdcString(i);
+  string = new ppdcString(s);
+}
+
+
+//
+// 'ppdcMessage::~ppdcMessage()' - Destroy a shared message.
+//
+
+ppdcMessage::~ppdcMessage()
+{
+  PPDC_DELETE;
+
+  id->release();
+  string->release();
+}
diff --git a/ppdc/ppdc-option.cxx b/ppdc/ppdc-option.cxx
new file mode 100644 (file)
index 0000000..eca6304
--- /dev/null
@@ -0,0 +1,111 @@
+//
+// Option class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2011 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcOption::ppdcOption()' - Create a new option.
+//
+
+ppdcOption::ppdcOption(ppdcOptType    ot,      // I - Option type
+                       const char     *n,      // I - Option name
+                      const char     *t,       // I - Option text
+                      ppdcOptSection s,        // I - Section
+                       float          o)       // I - Ordering number
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  type      = ot;
+  name      = new ppdcString(n);
+  text      = new ppdcString(t);
+  section   = s;
+  order     = o;
+  choices   = new ppdcArray();
+  defchoice = 0;
+}
+
+
+//
+// 'ppdcOption::ppdcOption()' - Copy a new option.
+//
+
+ppdcOption::ppdcOption(ppdcOption *o)          // I - Template option
+{
+  PPDC_NEW;
+
+  o->name->retain();
+  o->text->retain();
+  if (o->defchoice)
+    o->defchoice->retain();
+
+  type      = o->type;
+  name      = o->name;
+  text      = o->text;
+  section   = o->section;
+  order     = o->order;
+  choices   = new ppdcArray(o->choices);
+  defchoice = o->defchoice;
+}
+
+
+//
+// 'ppdcOption::~ppdcOption()' - Destroy an option.
+//
+
+ppdcOption::~ppdcOption()
+{
+  PPDC_DELETE;
+
+  name->release();
+  text->release();
+  if (defchoice)
+    defchoice->release();
+  choices->release();
+}
+
+
+//
+// 'ppdcOption::find_choice()' - Find an option choice.
+//
+
+ppdcChoice *                                   // O - Choice or NULL
+ppdcOption::find_choice(const char *n)         // I - Name of choice
+{
+  ppdcChoice   *c;                             // Current choice
+
+
+  for (c = (ppdcChoice *)choices->first(); c; c = (ppdcChoice *)choices->next())
+    if (!strcasecmp(n, c->name->value))
+      return (c);
+
+  return (0);
+}
+
+
+//
+// 'ppdcOption::set_defchoice()' - Set the default choice.
+//
+
+void
+ppdcOption::set_defchoice(ppdcChoice *c)       // I - Choice
+{
+  if (defchoice)
+    defchoice->release();
+
+  if (c->name)
+    c->name->retain();
+
+  defchoice = c->name;
+}
diff --git a/ppdc/ppdc-private.h b/ppdc/ppdc-private.h
new file mode 100644 (file)
index 0000000..f964db9
--- /dev/null
@@ -0,0 +1,45 @@
+//
+// Private definitions for the CUPS PPD Compiler.
+//
+// Copyright 2009-2010 by Apple Inc.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+#ifndef _PPDC_PRIVATE_H_
+#  define _PPDC_PRIVATE_H_
+
+//
+// Include necessary headers...
+//
+
+#  include "ppdc.h"
+#  include <string.h>
+#  include <ctype.h>
+#  include <stdio.h>
+#  include <errno.h>
+#  include <config.h>
+
+//
+// Macros...
+//
+
+#  ifdef PPDC_DEBUG
+#    define PPDC_NEW           DEBUG_printf(("%s: %p new", class_name(), this))
+#    define PPDC_NEWVAL(s)     DEBUG_printf(("%s(\"%s\"): %p new", class_name(), s, this))
+#    define PPDC_DELETE                DEBUG_printf(("%s: %p delete", class_name(), this))
+#    define PPDC_DELETEVAL(s)  DEBUG_printf(("%s(\"%s\"): %p delete", class_name(), s, this))
+#  else
+#    define PPDC_NEW
+#    define PPDC_NEWVAL(s)
+#    define PPDC_DELETE
+#    define PPDC_DELETEVAL(s)
+#  endif /* PPDC_DEBUG */
+
+/*
+ * Macro for localized text...
+ */
+
+#  define _(x) x
+
+#endif // !_PPDC_PRIVATE_H_
diff --git a/ppdc/ppdc-profile.cxx b/ppdc/ppdc-profile.cxx
new file mode 100644 (file)
index 0000000..3fbf968
--- /dev/null
@@ -0,0 +1,49 @@
+//
+// Color profile class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcProfile::ppdcProfile()' - Create a color profile.
+//
+
+ppdcProfile::ppdcProfile(const char  *r,       // I - Resolution name
+                         const char  *m,       // I - Media type name
+                        float       d,         // I - Density
+                        float       g,         // I - Gamma
+                        const float *p)        // I - 3x3 transform matrix
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  resolution = new ppdcString(r);
+  media_type = new ppdcString(m);
+  density    = d;
+  gamma      = g;
+
+  memcpy(profile, p, sizeof(profile));
+}
+
+
+//
+// 'ppdcProfile::~ppdcProfile()' - Destroy a color profile.
+//
+
+ppdcProfile::~ppdcProfile()
+{
+  PPDC_DELETE;
+
+  resolution->release();
+  media_type->release();
+}
diff --git a/ppdc/ppdc-shared.cxx b/ppdc/ppdc-shared.cxx
new file mode 100644 (file)
index 0000000..1b0152d
--- /dev/null
@@ -0,0 +1,66 @@
+//
+// Shared data class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcShared::ppdcShared()' - Create shared data.
+//
+
+ppdcShared::ppdcShared()
+{
+  use = 1;
+}
+
+
+//
+// 'ppdcShared::~ppdcShared()' - Destroy shared data.
+//
+
+ppdcShared::~ppdcShared()
+{
+}
+
+
+//
+// 'ppdcShared::release()' - Decrement the use count and delete as needed.
+//
+
+void
+ppdcShared::release(void)
+{
+  use --;
+
+#ifdef DEBUG
+  if (use < 0)
+  {
+    fprintf(stderr, "ERROR: Over-release of %s: %p\n", class_name(), this);
+    abort();
+  }
+#endif /* DEBUG */
+
+  if (use == 0)
+    delete this;
+}
+
+
+//
+// 'ppdcShared::retain()' - Increment the use count for this data.
+//
+
+void
+ppdcShared::retain()
+{
+  use ++;
+}
diff --git a/ppdc/ppdc-source.cxx b/ppdc/ppdc-source.cxx
new file mode 100644 (file)
index 0000000..500ce21
--- /dev/null
@@ -0,0 +1,3813 @@
+//
+// Source class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2018 by Apple Inc.
+// Copyright 2002-2007 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more
+// information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#include <limits.h>
+#include <math.h>
+#include <unistd.h>
+#include <cups/raster.h>
+#include "epson.h"
+#include "hp.h"
+#include "label.h"
+#ifndef _WIN32
+#  include <sys/utsname.h>
+#endif // !_WIN32
+
+
+//
+// Class globals...
+//
+
+ppdcArray      *ppdcSource::includes = 0;
+const char     *ppdcSource::driver_types[] =
+               {
+                 "custom",
+                 "ps",
+                 "escp",
+                 "pcl",
+                 "label",
+                 "epson",
+                 "hp"
+               };
+
+
+//
+// 'ppdcSource::ppdcSource()' - Load a driver source file.
+//
+
+ppdcSource::ppdcSource(const char  *f, // I - File to read
+                       cups_file_t *ffp)// I - File pointer to use
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  filename      = new ppdcString(f);
+  base_fonts    = new ppdcArray();
+  drivers       = new ppdcArray();
+  po_files      = new ppdcArray();
+  sizes         = new ppdcArray();
+  vars          = new ppdcArray();
+  cond_state    = PPDC_COND_NORMAL;
+  cond_current  = cond_stack;
+  cond_stack[0] = PPDC_COND_NORMAL;
+
+  // Add standard #define variables...
+#define MAKE_STRING(x) #x
+
+  vars->add(new ppdcVariable("CUPS_VERSION", MAKE_STRING(CUPS_VERSION)));
+  vars->add(new ppdcVariable("CUPS_VERSION_MAJOR", MAKE_STRING(CUPS_VERSION_MAJOR)));
+  vars->add(new ppdcVariable("CUPS_VERSION_MINOR", MAKE_STRING(CUPS_VERSION_MINOR)));
+  vars->add(new ppdcVariable("CUPS_VERSION_PATCH", MAKE_STRING(CUPS_VERSION_PATCH)));
+
+#ifdef _WIN32
+  vars->add(new ppdcVariable("PLATFORM_NAME", "Windows"));
+  vars->add(new ppdcVariable("PLATFORM_ARCH", "X86"));
+
+#else
+  struct utsname name;                 // uname information
+
+  if (!uname(&name))
+  {
+    vars->add(new ppdcVariable("PLATFORM_NAME", name.sysname));
+    vars->add(new ppdcVariable("PLATFORM_ARCH", name.machine));
+  }
+  else
+  {
+    vars->add(new ppdcVariable("PLATFORM_NAME", "unknown"));
+    vars->add(new ppdcVariable("PLATFORM_ARCH", "unknown"));
+  }
+#endif // _WIN32
+
+  if (f)
+    read_file(f, ffp);
+}
+
+
+//
+// 'ppdcSource::~ppdcSource()' - Free a driver source file.
+//
+
+ppdcSource::~ppdcSource()
+{
+  PPDC_DELETE;
+
+  filename->release();
+  base_fonts->release();
+  drivers->release();
+  po_files->release();
+  sizes->release();
+  vars->release();
+}
+
+
+//
+// 'ppdcSource::add_include()' - Add an include directory.
+//
+
+void
+ppdcSource::add_include(const char *d) // I - Include directory
+{
+  if (!d)
+    return;
+
+  if (!includes)
+    includes = new ppdcArray();
+
+  includes->add(new ppdcString(d));
+}
+
+
+//
+// 'ppdcSource::find_driver()' - Find a driver.
+//
+
+ppdcDriver *                           // O - Driver
+ppdcSource::find_driver(const char *f) // I - Driver file name
+{
+  ppdcDriver   *d;                     // Current driver
+
+
+  for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next())
+    if (!strcasecmp(f, d->pc_file_name->value))
+      return (d);
+
+  return (NULL);
+}
+
+
+//
+// 'ppdcSource::find_include()' - Find an include file.
+//
+
+char *                                 // O - Found path or NULL
+ppdcSource::find_include(
+    const char *f,                     // I - Include filename
+    const char *base,                  // I - Current directory
+    char       *n,                     // I - Path buffer
+    int        nlen)                   // I - Path buffer length
+{
+  ppdcString   *dir;                   // Include directory
+  char         temp[1024],             // Temporary path
+               *ptr;                   // Pointer to end of path
+  const char   *c;
+
+
+  // Range check input...
+  if (!f || !*f || !n || nlen < 2)
+    return (0);
+
+  // Check the first character to see if we have <name> or "name"...
+  if (*f == '<')
+  {
+    // Remove the surrounding <> from the name...
+    strncpy(temp, f + 1, sizeof(temp) - 1);
+    ptr = temp + strlen(temp) - 1;
+
+    if (*ptr != '>')
+    {
+      fprintf(stderr,
+             _("ppdc: Invalid #include/#po filename \"%s\".\n"), n);
+      return (0);
+    }
+
+    *ptr = '\0';
+    f    = temp;
+  }
+  else
+  {
+    // Check for the local file relative to the current directory...
+    if (base && *base && f[0] != '/')
+      snprintf(n, (size_t)nlen, "%s/%s", base, f);
+    else
+      strncpy(n, f, (size_t)nlen);
+
+    if (!access(n, 0))
+      return (n);
+    else if (*f == '/')
+    {
+      // Absolute path that doesn't exist...
+      return (0);
+    }
+  }
+
+  // Search the include directories, if any...
+  if (includes)
+  {
+    for (dir = (ppdcString *)includes->first(); dir; dir = (ppdcString *)includes->next())
+    {
+      snprintf(n, (size_t)nlen, "%s/%s", dir->value, f);
+      if (!access(n, 0))
+        return (n);
+    }
+  }
+
+  // Search our own include directory (Usually /usr/share/ppdc)
+  if ((c = getenv("PPDC_DATADIR")) == NULL)
+    c = PPDC_DATADIR;
+
+  snprintf(n, (size_t)nlen, "%s/%s", c, f);
+  if (!access(n, 0))
+    return (n);
+  
+  snprintf(n, (size_t)nlen, "%s/po/%s", c, f);
+  if (!access(n, 0))
+    return (n);
+
+  // Search the CUPS include directories...
+  if ((c = getenv("CUPS_DATADIR")) == NULL)
+    c = CUPS_DATADIR;
+
+  snprintf(n, (size_t)nlen, "%s/ppdc/%s", c, f);
+  if (!access(n, 0))
+    return (n);
+
+  snprintf(n, (size_t)nlen, "%s/po/%s", c, f);
+  if (!access(n, 0))
+    return (n);
+
+  return (0);
+}
+
+
+//
+// 'ppdcSource::find_po()' - Find a message catalog for the given locale.
+//
+
+ppdcCatalog *                          // O - Message catalog or NULL
+ppdcSource::find_po(const char *l)     // I - Locale name
+{
+  ppdcCatalog  *cat;                   // Current message catalog
+
+
+  for (cat = (ppdcCatalog *)po_files->first();
+       cat;
+       cat = (ppdcCatalog *)po_files->next())
+    if (!strcasecmp(l, cat->locale->value))
+      return (cat);
+
+  return (NULL);
+}
+
+
+//
+// 'ppdcSource::find_size()' - Find a media size.
+//
+
+ppdcMediaSize *                                // O - Size
+ppdcSource::find_size(const char *s)   // I - Size name
+{
+  ppdcMediaSize        *m;                     // Current media size
+
+
+  for (m = (ppdcMediaSize *)sizes->first(); m; m = (ppdcMediaSize *)sizes->next())
+    if (!strcasecmp(s, m->name->value))
+      return (m);
+
+  return (NULL);
+}
+
+
+//
+// 'ppdcSource::find_variable()' - Find a variable.
+//
+
+ppdcVariable *                         // O - Variable
+ppdcSource::find_variable(const char *n)// I - Variable name
+{
+  ppdcVariable *v;                     // Current variable
+
+
+  for (v = (ppdcVariable *)vars->first(); v; v = (ppdcVariable *)vars->next())
+    if (!strcasecmp(n, v->name->value))
+      return (v);
+
+  return (NULL);
+}
+
+
+//
+// 'ppdcSource::get_attr()' - Get an attribute.
+//
+
+ppdcAttr *                             // O - Attribute
+ppdcSource::get_attr(ppdcFile *fp,     // I - File to read
+                     bool     loc)     // I - Localize this attribute?
+{
+  char name[1024],                     // Name string
+       selector[1024],                 // Selector string
+       *text,                          // Text string
+       value[1024];                    // Value string
+
+
+  // Get the attribute parameters:
+  //
+  // Attribute name selector value
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected name after %s on line %d of %s.\n"),
+           loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
+    return (0);
+  }
+
+  if (!get_token(fp, selector, sizeof(selector)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected selector after %s on line %d of %s.\n"),
+           loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
+    return (0);
+  }
+
+  if ((text = strchr(selector, '/')) != NULL)
+    *text++ = '\0';
+
+  if (!get_token(fp, value, sizeof(value)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected value after %s on line %d of %s.\n"),
+           loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
+    return (0);
+  }
+
+  return (new ppdcAttr(name, selector, text, value, loc));
+}
+
+
+//
+// 'ppdcSource::get_boolean()' - Get a boolean value.
+//
+
+int                                    // O - Boolean value
+ppdcSource::get_boolean(ppdcFile *fp)  // I - File to read
+{
+  char buffer[256];                    // String buffer
+
+
+  if (!get_token(fp, buffer, sizeof(buffer)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected boolean value on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (-1);
+  }
+
+  if (!strcasecmp(buffer, "on") ||
+      !strcasecmp(buffer, "yes") ||
+      !strcasecmp(buffer, "true"))
+    return (1);
+  else if (!strcasecmp(buffer, "off") ||
+          !strcasecmp(buffer, "no") ||
+          !strcasecmp(buffer, "false"))
+    return (0);
+  else
+  {
+    fprintf(stderr,
+           _("ppdc: Bad boolean value (%s) on line %d of %s.\n"),
+           buffer, fp->line, fp->filename);
+    return (-1);
+  }
+}
+
+
+//
+// 'ppdcSource::get_choice()' - Get a choice.
+//
+
+ppdcChoice *                           // O - Choice data
+ppdcSource::get_choice(ppdcFile *fp)   // I - File to read
+{
+  char name[1024],                     // Name
+       *text,                          // Text
+       code[10240];                    // Code
+
+
+  // Read a choice from the file:
+  //
+  // Choice name/text code
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected choice name/text on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  if (!get_token(fp, code, sizeof(code)))
+  {
+    fprintf(stderr, _("ppdc: Expected choice code on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  // Return the new choice
+  return (new ppdcChoice(name, text, code));
+}
+
+
+//
+// 'ppdcSource::get_color_model()' - Get an old-style color model option.
+//
+
+ppdcChoice *                           // O - Choice data
+ppdcSource::get_color_model(ppdcFile *fp)
+                                       // I - File to read
+{
+  char         name[1024],             // Option name
+               *text,                  // Text option
+               temp[256];              // Temporary string
+  int          color_space,            // Colorspace
+               color_order,            // Color order
+               compression;            // Compression mode
+
+
+  // Get the ColorModel parameters:
+  //
+  // ColorModel name/text colorspace colororder compression
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected name/text combination for ColorModel on "
+             "line %d of %s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected colorspace for ColorModel on line %d of "
+             "%s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((color_space = get_color_space(temp)) < 0)
+    color_space = get_integer(temp);
+
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected color order for ColorModel on line %d of "
+             "%s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((color_order = get_color_order(temp)) < 0)
+    color_order = get_integer(temp);
+
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected compression for ColorModel on line %d of "
+             "%s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  compression = get_integer(temp);
+
+  snprintf(temp, sizeof(temp),
+           "<</cupsColorSpace %d/cupsColorOrder %d/cupsCompression %d>>"
+          "setpagedevice",
+           color_space, color_order, compression);
+
+  return (new ppdcChoice(name, text, temp));
+}
+
+
+//
+// 'ppdcSource::get_color_order()' - Get an old-style color order value.
+//
+
+int                                    // O - Color order value
+ppdcSource::get_color_order(
+    const char *co)                    // I - Color order string
+{
+  if (!strcasecmp(co, "chunked") ||
+      !strcasecmp(co, "chunky"))
+    return (CUPS_ORDER_CHUNKED);
+  else if (!strcasecmp(co, "banded"))
+    return (CUPS_ORDER_BANDED);
+  else if (!strcasecmp(co, "planar"))
+    return (CUPS_ORDER_PLANAR);
+  else
+    return (-1);
+}
+
+
+//
+// 'ppdcSource::get_color_profile()' - Get a color profile definition.
+//
+
+ppdcProfile *                          // O - Color profile
+ppdcSource::get_color_profile(
+    ppdcFile *fp)                      // I - File to read
+{
+  char         resolution[1024],       // Resolution/media type
+               *media_type;            // Media type
+  int          i;                      // Looping var
+  float                g,                      // Gamma value
+               d,                      // Density value
+               m[9];                   // Transform matrix
+
+
+  // Get the ColorProfile parameters:
+  //
+  // ColorProfile resolution/mediatype gamma density m00 m01 m02 ... m22
+  if (!get_token(fp, resolution, sizeof(resolution)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected resolution/mediatype following "
+             "ColorProfile on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((media_type = strchr(resolution, '/')) != NULL)
+    *media_type++ = '\0';
+  else
+    media_type = resolution;
+
+  g = get_float(fp);
+  d = get_float(fp);
+  for (i = 0; i < 9; i ++)
+    m[i] = get_float(fp);
+
+  return (new ppdcProfile(resolution, media_type, d, g, m));
+}
+
+
+//
+// 'ppdcSource::get_color_space()' - Get an old-style colorspace value.
+//
+
+int                                    // O - Colorspace value
+ppdcSource::get_color_space(
+    const char *cs)                    // I - Colorspace string
+{
+  if (!strcasecmp(cs, "w"))
+    return (CUPS_CSPACE_W);
+  else if (!strcasecmp(cs, "rgb"))
+    return (CUPS_CSPACE_RGB);
+  else if (!strcasecmp(cs, "rgba"))
+    return (CUPS_CSPACE_RGBA);
+  else if (!strcasecmp(cs, "k"))
+    return (CUPS_CSPACE_K);
+  else if (!strcasecmp(cs, "cmy"))
+    return (CUPS_CSPACE_CMY);
+  else if (!strcasecmp(cs, "ymc"))
+    return (CUPS_CSPACE_YMC);
+  else if (!strcasecmp(cs, "cmyk"))
+    return (CUPS_CSPACE_CMYK);
+  else if (!strcasecmp(cs, "ymck"))
+    return (CUPS_CSPACE_YMCK);
+  else if (!strcasecmp(cs, "kcmy"))
+    return (CUPS_CSPACE_KCMY);
+  else if (!strcasecmp(cs, "kcmycm"))
+    return (CUPS_CSPACE_KCMYcm);
+  else if (!strcasecmp(cs, "gmck"))
+    return (CUPS_CSPACE_GMCK);
+  else if (!strcasecmp(cs, "gmcs"))
+    return (CUPS_CSPACE_GMCS);
+  else if (!strcasecmp(cs, "white"))
+    return (CUPS_CSPACE_WHITE);
+  else if (!strcasecmp(cs, "gold"))
+    return (CUPS_CSPACE_GOLD);
+  else if (!strcasecmp(cs, "silver"))
+    return (CUPS_CSPACE_SILVER);
+  else if (!strcasecmp(cs, "CIEXYZ"))
+    return (CUPS_CSPACE_CIEXYZ);
+  else if (!strcasecmp(cs, "CIELab"))
+    return (CUPS_CSPACE_CIELab);
+  else if (!strcasecmp(cs, "RGBW"))
+    return (CUPS_CSPACE_RGBW);
+  else if (!strcasecmp(cs, "ICC1"))
+    return (CUPS_CSPACE_ICC1);
+  else if (!strcasecmp(cs, "ICC2"))
+    return (CUPS_CSPACE_ICC2);
+  else if (!strcasecmp(cs, "ICC3"))
+    return (CUPS_CSPACE_ICC3);
+  else if (!strcasecmp(cs, "ICC4"))
+    return (CUPS_CSPACE_ICC4);
+  else if (!strcasecmp(cs, "ICC5"))
+    return (CUPS_CSPACE_ICC5);
+  else if (!strcasecmp(cs, "ICC6"))
+    return (CUPS_CSPACE_ICC6);
+  else if (!strcasecmp(cs, "ICC7"))
+    return (CUPS_CSPACE_ICC7);
+  else if (!strcasecmp(cs, "ICC8"))
+    return (CUPS_CSPACE_ICC8);
+  else if (!strcasecmp(cs, "ICC9"))
+    return (CUPS_CSPACE_ICC9);
+  else if (!strcasecmp(cs, "ICCA"))
+    return (CUPS_CSPACE_ICCA);
+  else if (!strcasecmp(cs, "ICCB"))
+    return (CUPS_CSPACE_ICCB);
+  else if (!strcasecmp(cs, "ICCC"))
+    return (CUPS_CSPACE_ICCC);
+  else if (!strcasecmp(cs, "ICCD"))
+    return (CUPS_CSPACE_ICCD);
+  else if (!strcasecmp(cs, "ICCE"))
+    return (CUPS_CSPACE_ICCE);
+  else if (!strcasecmp(cs, "ICCF"))
+    return (CUPS_CSPACE_ICCF);
+  else
+    return (-1);
+}
+
+
+//
+// 'ppdcSource::get_constraint()' - Get a constraint.
+//
+
+ppdcConstraint *                       // O - Constraint
+ppdcSource::get_constraint(ppdcFile *fp)// I - File to read
+{
+  char         temp[1024],             // One string to rule them all
+               *ptr,                   // Pointer into string
+               *option1,               // Constraint option 1
+               *choice1,               // Constraint choice 1
+               *option2,               // Constraint option 2
+               *choice2;               // Constraint choice 2
+
+
+  // Read the UIConstaints parameter in one of the following forms:
+  //
+  // UIConstraints "*Option1 *Option2"
+  // UIConstraints "*Option1 Choice1 *Option2"
+  // UIConstraints "*Option1 *Option2 Choice2"
+  // UIConstraints "*Option1 Choice1 *Option2 Choice2"
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected constraints string for UIConstraints on "
+             "line %d of %s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  for (ptr = temp; isspace(*ptr); ptr ++);
+
+  if (*ptr != '*')
+  {
+    fprintf(stderr,
+           _("ppdc: Option constraint must *name on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  option1 = ptr;
+
+  for (; *ptr && !isspace(*ptr); ptr ++);
+  for (; isspace(*ptr); *ptr++ = '\0');
+
+  if (*ptr != '*')
+  {
+    choice1 = ptr;
+
+    for (; *ptr && !isspace(*ptr); ptr ++);
+    for (; isspace(*ptr); *ptr++ = '\0');
+  }
+  else
+    choice1 = NULL;
+
+  if (*ptr != '*')
+  {
+    fprintf(stderr,
+           _("ppdc: Expected two option names on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  option2 = ptr;
+
+  for (; *ptr && !isspace(*ptr); ptr ++);
+  for (; isspace(*ptr); *ptr++ = '\0');
+
+  if (*ptr)
+    choice2 = ptr;
+  else
+    choice2 = NULL;
+
+  return (new ppdcConstraint(option1, choice1, option2, choice2));
+}
+
+
+//
+// 'ppdcSource::get_custom_size()' - Get a custom media size definition from a file.
+//
+
+ppdcMediaSize *                                // O - Media size
+ppdcSource::get_custom_size(ppdcFile *fp)
+                                       // I - File to read
+{
+  char         name[1024],             // Name
+               *text,                  // Text
+               size_code[10240],       // PageSize code
+               region_code[10240];     // PageRegion
+  float                width,                  // Width
+               length,                 // Length
+               left,                   // Left margin
+               bottom,                 // Bottom margin
+               right,                  // Right margin
+               top;                    // Top margin
+
+
+  // Get the name, text, width, length, margins, and code:
+  //
+  // CustomMedia name/text width length left bottom right top size-code region-code
+  if (!get_token(fp, name, sizeof(name)))
+    return (NULL);
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  if ((width = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  if ((length = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  if ((left = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  if ((bottom = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  if ((right = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  if ((top = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  if (!get_token(fp, size_code, sizeof(size_code)))
+    return (NULL);
+
+  if (!get_token(fp, region_code, sizeof(region_code)))
+    return (NULL);
+
+  // Return the new media size...
+  return (new ppdcMediaSize(name, text, width, length, left, bottom,
+                            right, top, size_code, region_code));
+}
+
+
+//
+// 'ppdcSource::get_duplex()' - Get a duplex option.
+//
+
+void
+ppdcSource::get_duplex(ppdcFile   *fp, // I - File to read from
+                       ppdcDriver *d)  // I - Current driver
+{
+  char         temp[256];              // Duplex keyword
+  ppdcAttr     *attr;                  // cupsFlipDuplex attribute
+  ppdcGroup    *g;                     // Current group
+  ppdcOption   *o;                     // Duplex option
+
+
+  // Duplex {boolean|none|normal|flip}
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected duplex type after Duplex on line %d of "
+             "%s.\n"), fp->line, fp->filename);
+    return;
+  }
+
+  if (cond_state)
+    return;
+
+  if (!strcasecmp(temp, "none") || !strcasecmp(temp, "false") ||
+      !strcasecmp(temp, "no") || !strcasecmp(temp, "off"))
+  {
+    g = d->find_group("General");
+    if ((o = g->find_option("Duplex")) != NULL)
+      g->options->remove(o);
+
+    for (attr = (ppdcAttr *)d->attrs->first();
+         attr;
+        attr = (ppdcAttr *)d->attrs->next())
+      if (!strcmp(attr->name->value, "cupsFlipDuplex"))
+      {
+        d->attrs->remove(attr);
+       break;
+      }
+  }
+  else if (!strcasecmp(temp, "normal") || !strcasecmp(temp, "true") ||
+          !strcasecmp(temp, "yes") || !strcasecmp(temp, "on") ||
+          !strcasecmp(temp, "flip") || !strcasecmp(temp, "rotated") ||
+          !strcasecmp(temp, "manualtumble"))
+  {
+    g = d->find_group("General");
+    o = g->find_option("Duplex");
+
+    if (!o)
+    {
+      o = new ppdcOption(PPDC_PICKONE, "Duplex", "2-Sided Printing",
+                        !strcasecmp(temp, "flip") ? PPDC_SECTION_PAGE :
+                                                    PPDC_SECTION_ANY, 10.0f);
+      o->add_choice(new ppdcChoice("None", "Off (1-Sided)",
+                                  "<</Duplex false>>setpagedevice"));
+      o->add_choice(new ppdcChoice("DuplexNoTumble", "Long-Edge (Portrait)",
+                                   "<</Duplex true/Tumble false>>setpagedevice"));
+      o->add_choice(new ppdcChoice("DuplexTumble", "Short-Edge (Landscape)",
+                                   "<</Duplex true/Tumble true>>setpagedevice"));
+
+      g->add_option(o);
+    }
+
+    for (attr = (ppdcAttr *)d->attrs->first();
+         attr;
+        attr = (ppdcAttr *)d->attrs->next())
+      if (!strcmp(attr->name->value, "cupsFlipDuplex"))
+      {
+        if (strcasecmp(temp, "flip"))
+          d->attrs->remove(attr);
+       break;
+      }
+
+    if (!strcasecmp(temp, "flip") && !attr)
+      d->add_attr(new ppdcAttr("cupsFlipDuplex", NULL, NULL, "true"));
+
+    for (attr = (ppdcAttr *)d->attrs->first();
+         attr;
+        attr = (ppdcAttr *)d->attrs->next())
+      if (!strcmp(attr->name->value, "cupsBackSide"))
+      {
+        d->attrs->remove(attr);
+       break;
+      }
+
+    if (!strcasecmp(temp, "flip"))
+      d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Flipped"));
+    else if (!strcasecmp(temp, "rotated"))
+      d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Rotated"));
+    else if (!strcasecmp(temp, "manualtumble"))
+      d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "ManualTumble"));
+    else
+      d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Normal"));
+  }
+  else
+    fprintf(stderr,
+           _("ppdc: Unknown duplex type \"%s\" on line %d of %s.\n"),
+           temp, fp->line, fp->filename);
+}
+
+
+//
+// 'ppdcSource::get_filter()' - Get a filter.
+//
+
+ppdcFilter *                           // O - Filter
+ppdcSource::get_filter(ppdcFile *fp)   // I - File to read
+{
+  char type[1024],                     // MIME type
+       program[1024],                  // Filter program
+       *ptr;                           // Pointer into MIME type
+  int  cost;                           // Relative cost
+
+
+  // Read filter parameters in one of the following formats:
+  //
+  // Filter "type cost program"
+  // Filter type cost program
+
+  if (!get_token(fp, type, sizeof(type)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected a filter definition on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((ptr = strchr(type, ' ')) != NULL)
+  {
+    // Old-style filter definition in one string...
+    *ptr++ = '\0';
+    cost = strtol(ptr, &ptr, 10);
+
+    while (isspace(*ptr))
+      ptr ++;
+
+    strncpy(program, ptr, sizeof(program) - 1);
+  }
+  else
+  {
+    cost = get_integer(fp);
+
+    if (!get_token(fp, program, sizeof(program)))
+    {
+      fprintf(stderr,
+             _("ppdc: Expected a program name on line %d of %s.\n"),
+             fp->line, fp->filename);
+      return (NULL);
+    }
+  }
+
+  if (!type[0])
+  {
+    fprintf(stderr,
+           _("ppdc: Invalid empty MIME type for filter on line %d of "
+             "%s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if (cost < 0 || cost > 200)
+  {
+    fprintf(stderr,
+           _("ppdc: Invalid cost for filter on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if (!program[0])
+  {
+    fprintf(stderr,
+           _("ppdc: Invalid empty program name for filter on line %d "
+             "of %s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  return (new ppdcFilter(type, program, cost));
+}
+
+
+//
+// 'ppdcSource::get_float()' - Get a single floating-point number.
+//
+
+float                                  // O - Number
+ppdcSource::get_float(ppdcFile *fp)    // I - File to read
+{
+  char temp[256],                      // String buffer
+       *ptr;                           // Pointer into buffer
+  float        val;                            // Floating point value
+
+
+  // Get the number from the file and range-check...
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr, _("ppdc: Expected real number on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (-1.0f);
+  }
+
+  val = (float)strtod(temp, &ptr);
+
+  if (*ptr)
+  {
+    fprintf(stderr,
+           _("ppdc: Unknown trailing characters in real number \"%s\" "
+             "on line %d of %s.\n"), temp, fp->line, fp->filename);
+    return (-1.0f);
+  }
+  else
+    return (val);
+}
+
+
+//
+// 'ppdcSource::get_font()' - Get a font definition.
+//
+
+ppdcFont *                             // O - Font data
+ppdcSource::get_font(ppdcFile *fp)     // I - File to read
+{
+  char                 name[256],      // Font name
+                       encoding[256],  // Font encoding
+                       version[256],   // Font version
+                       charset[256],   // Font charset
+                       temp[256];      // Font status string
+  ppdcFontStatus       status;         // Font status enumeration
+
+
+  // Read font parameters as follows:
+  //
+  // Font *
+  // Font name encoding version charset status
+  // %font name encoding version charset status
+  //
+  // "Name" is the PostScript font name.
+  //
+  // "Encoding" is the default encoding of the font: Standard, ISOLatin1,
+  // Special, Expert, ExpertSubset, etc.
+  //
+  // "Version" is the version number string.
+  //
+  // "Charset" specifies the characters that are included in the font:
+  // Standard, Special, Expert, Adobe-Identity, etc.
+  //
+  // "Status" is the keyword ROM or Disk.
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected name after Font on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (0);
+  }
+
+  if (!strcmp(name, "*"))
+  {
+    // Include all base fonts...
+    encoding[0] = '\0';
+    version[0]  = '\0';
+    charset[0]  = '\0';
+    status      = PPDC_FONT_ROM;
+  }
+  else
+  {
+    // Load a full font definition...
+    if (!get_token(fp, encoding, sizeof(encoding)))
+    {
+      fprintf(stderr,
+             _("ppdc: Expected encoding after Font on line %d of "
+               "%s.\n"), fp->line, fp->filename);
+      return (0);
+    }
+
+    if (!get_token(fp, version, sizeof(version)))
+    {
+      fprintf(stderr,
+             _("ppdc: Expected version after Font on line %d of "
+               "%s.\n"), fp->line, fp->filename);
+      return (0);
+    }
+
+    if (!get_token(fp, charset, sizeof(charset)))
+    {
+      fprintf(stderr,
+             _("ppdc: Expected charset after Font on line %d of "
+               "%s.\n"), fp->line, fp->filename);
+      return (0);
+    }
+
+    if (!get_token(fp, temp, sizeof(temp)))
+    {
+      fprintf(stderr,
+             _("ppdc: Expected status after Font on line %d of %s.\n"),
+             fp->line, fp->filename);
+      return (0);
+    }
+
+    if (!strcasecmp(temp, "ROM"))
+      status = PPDC_FONT_ROM;
+    else if (!strcasecmp(temp, "Disk"))
+      status = PPDC_FONT_DISK;
+    else
+    {
+      fprintf(stderr,
+             _("ppdc: Bad status keyword %s on line %d of %s.\n"),
+             temp, fp->line, fp->filename);
+      return (0);
+    }
+  }
+
+//  printf("Font %s %s %s %s %s\n", name, encoding, version, charset, temp);
+
+  return (new ppdcFont(name, encoding, version, charset, status));
+}
+
+
+//
+// 'ppdcSource::get_generic()' - Get a generic old-style option.
+//
+
+ppdcChoice *                           // O - Choice data
+ppdcSource::get_generic(ppdcFile   *fp,        // I - File to read
+                        const char *keyword,
+                                       // I - Keyword name
+                        const char *tattr,
+                                       // I - Text attribute
+                       const char *nattr)
+                                       // I - Numeric attribute
+{
+  char         name[1024],             // Name
+               *text,                  // Text
+               command[2048];          // Command string
+  int          val;                    // Numeric value
+
+
+  // Read one of the following parameters:
+  //
+  // Foo name/text
+  // Foo integer name/text
+  if (nattr)
+    val = get_integer(fp);
+  else
+    val = 0;
+
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected name/text after %s on line %d of %s.\n"),
+           keyword, fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  if (nattr)
+  {
+    if (tattr)
+      snprintf(command, sizeof(command),
+               "<</%s(%s)/%s %d>>setpagedevice",
+               tattr, name, nattr, val);
+    else
+      snprintf(command, sizeof(command),
+               "<</%s %d>>setpagedevice",
+               nattr, val);
+  }
+  else
+    snprintf(command, sizeof(command),
+             "<</%s(%s)>>setpagedevice",
+             tattr, name);
+
+  return (new ppdcChoice(name, text, command));
+}
+
+
+//
+// 'ppdcSource::get_group()' - Get an option group.
+//
+
+ppdcGroup *                            // O - Group
+ppdcSource::get_group(ppdcFile   *fp,  // I - File to read
+                      ppdcDriver *d)   // I - Printer driver
+{
+  char         name[1024],             // UI name
+               *text;                  // UI text
+  ppdcGroup    *g;                     // Group
+
+
+  // Read the Group parameters:
+  //
+  // Group name/text
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected group name/text on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  // See if the group already exists...
+  if ((g = d->find_group(name)) == NULL)
+  {
+    // Nope, add a new one...
+    g = new ppdcGroup(name, text);
+  }
+
+  return (g);
+}
+
+
+//
+// 'ppdcSource::get_installable()' - Get an installable option.
+//
+
+ppdcOption *                           // O - Option
+ppdcSource::get_installable(ppdcFile *fp)
+                                       // I - File to read
+{
+  char         name[1024],             // Name for installable option
+               *text;                  // Text for installable option
+  ppdcOption   *o;                     // Option
+
+
+  // Read the parameter for an installable option:
+  //
+  // Installable name/text
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected name/text after Installable on line %d "
+             "of %s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  // Create the option...
+  o = new ppdcOption(PPDC_BOOLEAN, name, text, PPDC_SECTION_ANY, 10.0f);
+
+  // Add the false and true choices...
+  o->add_choice(new ppdcChoice("False", "Not Installed", ""));
+  o->add_choice(new ppdcChoice("True", "Installed", ""));
+
+  return (o);
+}
+
+
+//
+// 'ppdcSource::get_integer()' - Get an integer value from a string.
+//
+
+#define PPDC_XX        -1                      // Bad
+#define PPDC_EQ        0                       // ==
+#define PPDC_NE        1                       // !=
+#define PPDC_LT        2                       // <
+#define PPDC_LE        3                       // <=
+#define PPDC_GT        4                       // >
+#define PPDC_GE        5                       // >=
+
+int                                    // O - Integer value
+ppdcSource::get_integer(const char *v) // I - Value string
+{
+  long         val;                    // Value
+  long         temp,                   // Temporary value
+               temp2;                  // Second temporary value
+  char         *newv,                  // New value string pointer
+               ch;                     // Temporary character
+  ppdcVariable *var;                   // #define variable
+  int          compop;                 // Comparison operator
+
+
+  // Parse the value string...
+  if (!v)
+    return (-1);
+
+  if (isdigit(*v & 255) || *v == '-' || *v == '+')
+  {
+    // Return a simple integer value
+    val = strtol(v, (char **)&v, 0);
+    if (*v || val == LONG_MIN)
+      return (-1);
+    else
+      return ((int)val);
+  }
+  else if (*v == '(')
+  {
+    // Evaluate and expression in any of the following formats:
+    //
+    // (number number ... number)   Bitwise OR of all numbers
+    // (NAME == value)              1 if equal, 0 otherwise
+    // (NAME != value)              1 if not equal, 0 otherwise
+    // (NAME < value)               1 if less than, 0 otherwise
+    // (NAME <= value)              1 if less than or equal, 0 otherwise
+    // (NAME > value)               1 if greater than, 0 otherwise
+    // (NAME >= value)              1 if greater than or equal, 0 otherwise
+
+    v ++;
+    val = 0;
+
+    while (*v && *v != ')')
+    {
+      // Skip leading whitespace...
+      while (*v && isspace(*v & 255))
+        v ++;
+
+      if (!*v || *v == ')')
+        break;
+
+      if (isdigit(*v & 255) || *v == '-' || *v == '+')
+      {
+        // Bitwise OR a number...
+       temp = strtol(v, &newv, 0);
+
+       if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') ||
+           temp == LONG_MIN)
+         return (-1);
+      }
+      else
+      {
+        // NAME logicop value
+       for (newv = (char *)v + 1;
+            *newv && (isalnum(*newv & 255) || *newv == '_');
+            newv ++)
+         /* do nothing */;
+
+        ch    = *newv;
+       *newv = '\0';
+
+        if ((var = find_variable(v)) != NULL)
+       {
+         if (!var->value || !var->value->value || !var->value->value[0])
+           temp = 0;
+         else if (isdigit(var->value->value[0] & 255) ||
+                  var->value->value[0] == '-' ||
+                  var->value->value[0] == '+')
+            temp = strtol(var->value->value, NULL, 0);
+         else
+           temp = 1;
+       }
+       else
+         temp = 0;
+
+        *newv = ch;
+       while (isspace(*newv & 255))
+         newv ++;
+
+        if (!strncmp(newv, "==", 2))
+       {
+         compop = PPDC_EQ;
+         newv += 2;
+       }
+        else if (!strncmp(newv, "!=", 2))
+        {
+         compop = PPDC_NE;
+         newv += 2;
+       }
+        else if (!strncmp(newv, "<=", 2))
+        {
+         compop = PPDC_LE;
+         newv += 2;
+       }
+       else if (*newv == '<')
+        {
+         compop = PPDC_LT;
+         newv ++;
+       }
+        else if (!strncmp(newv, ">=", 2))
+        {
+         compop = PPDC_GE;
+         newv += 2;
+       }
+       else if (*newv == '>')
+       {
+         compop = PPDC_GT;
+         newv ++;
+       }
+       else
+         compop = PPDC_XX;
+
+        if (compop != PPDC_XX)
+       {
+         while (isspace(*newv & 255))
+           newv ++;
+
+          if (*newv == ')' || !*newv)
+           return (-1);
+
+         if (isdigit(*newv & 255) || *newv == '-' || *newv == '+')
+         {
+           // Get the second number...
+           temp2 = strtol(newv, &newv, 0);
+           if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') ||
+               temp == LONG_MIN)
+             return (-1);
+          }
+         else
+         {
+           // Lookup the second name...
+           for (v = newv, newv ++;
+                *newv && (isalnum(*newv & 255) || *newv == '_');
+                newv ++);
+
+           ch    = *newv;
+           *newv = '\0';
+
+           if ((var = find_variable(v)) != NULL)
+           {
+             if (!var->value || !var->value->value || !var->value->value[0])
+               temp2 = 0;
+             else if (isdigit(var->value->value[0] & 255) ||
+                      var->value->value[0] == '-' ||
+                      var->value->value[0] == '+')
+               temp2 = strtol(var->value->value, NULL, 0);
+             else
+               temp2 = 1;
+           }
+           else
+             temp2 = 0;
+
+           *newv = ch;
+          }
+
+         // Do the comparison...
+         switch (compop)
+         {
+           case PPDC_EQ :
+               temp = temp == temp2;
+               break;
+           case PPDC_NE :
+               temp = temp != temp2;
+               break;
+           case PPDC_LT :
+               temp = temp < temp2;
+               break;
+           case PPDC_LE :
+               temp = temp <= temp2;
+               break;
+           case PPDC_GT :
+               temp = temp > temp2;
+               break;
+           case PPDC_GE :
+               temp = temp >= temp2;
+               break;
+         }
+       }
+      }
+
+      val |= temp;
+      v   = newv;
+    }
+
+    if (*v == ')' && !v[1])
+      return ((int)val);
+    else
+      return (-1);
+  }
+  else if ((var = find_variable(v)) != NULL)
+  {
+    // NAME by itself returns 1 if the #define variable is not blank and
+    // not "0"...
+    return (var->value->value && var->value->value[0] &&
+            strcmp(var->value->value, "0"));
+  }
+  else
+  {
+    // Anything else is an error...
+    return (-1);
+  }
+}
+
+
+//
+// 'ppdcSource::get_integer()' - Get an integer value from a file.
+//
+
+int                                    // O - Integer value
+ppdcSource::get_integer(ppdcFile *fp)  // I - File to read
+{
+  char temp[1024];                     // String buffer
+
+
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr, _("ppdc: Expected integer on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (-1);
+  }
+  else
+    return (get_integer(temp));
+}
+
+
+//
+// 'ppdcSource::get_measurement()' - Get a measurement value.
+//
+
+float                                  // O - Measurement value in points
+ppdcSource::get_measurement(ppdcFile *fp)
+                                       // I - File to read
+{
+  char buffer[256],                    // Number buffer
+       *ptr;                           // Pointer into buffer
+  float        val;                            // Measurement value
+
+
+  // Grab a token from the file...
+  if (!get_token(fp, buffer, sizeof(buffer)))
+    return (-1.0f);
+
+  // Get the floating point value of "s" and skip all digits and decimal points.
+  val = (float)strtod(buffer, &ptr);
+
+  // Check for a trailing unit specifier...
+  if (!strcasecmp(ptr, "mm"))
+    val *= 72.0f / 25.4f;
+  else if (!strcasecmp(ptr, "cm"))
+    val *= 72.0f / 2.54f;
+  else if (!strcasecmp(ptr, "m"))
+    val *= 72.0f / 0.0254f;
+  else if (!strcasecmp(ptr, "in"))
+    val *= 72.0f;
+  else if (!strcasecmp(ptr, "ft"))
+    val *= 72.0f * 12.0f;
+  else if (strcasecmp(ptr, "pt") && *ptr)
+    return (-1.0f);
+
+  return (val);
+}
+
+
+//
+// 'ppdcSource::get_option()' - Get an option definition.
+//
+
+ppdcOption *                           // O - Option
+ppdcSource::get_option(ppdcFile   *fp, // I - File to read
+                       ppdcDriver *d,  // I - Printer driver
+                      ppdcGroup  *g)   // I - Current group
+{
+  char         name[1024],             // UI name
+               *text,                  // UI text
+               type[256];              // UI type string
+  ppdcOptType  ot;                     // Option type value
+  ppdcOptSection section;              // Option section
+  float                order;                  // Option order
+  ppdcOption   *o;                     // Option
+  ppdcGroup    *mg;                    // Matching group, if any
+
+
+  // Read the Option parameters:
+  //
+  // Option name/text type section order
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected option name/text on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  if (!get_token(fp, type, sizeof(type)))
+  {
+    fprintf(stderr, _("ppdc: Expected option type on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if (!strcasecmp(type, "boolean"))
+    ot = PPDC_BOOLEAN;
+  else if (!strcasecmp(type, "pickone"))
+    ot = PPDC_PICKONE;
+  else if (!strcasecmp(type, "pickmany"))
+    ot = PPDC_PICKMANY;
+  else
+  {
+    fprintf(stderr,
+           _("ppdc: Invalid option type \"%s\" on line %d of %s.\n"),
+           type, fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if (!get_token(fp, type, sizeof(type)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected option section on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if (!strcasecmp(type, "AnySetup"))
+    section = PPDC_SECTION_ANY;
+  else if (!strcasecmp(type, "DocumentSetup"))
+    section = PPDC_SECTION_DOCUMENT;
+  else if (!strcasecmp(type, "ExitServer"))
+    section = PPDC_SECTION_EXIT;
+  else if (!strcasecmp(type, "JCLSetup"))
+    section = PPDC_SECTION_JCL;
+  else if (!strcasecmp(type, "PageSetup"))
+    section = PPDC_SECTION_PAGE;
+  else if (!strcasecmp(type, "Prolog"))
+    section = PPDC_SECTION_PROLOG;
+  else
+  {
+    fprintf(stderr,
+           _("ppdc: Invalid option section \"%s\" on line %d of "
+             "%s.\n"), type, fp->line, fp->filename);
+    return (NULL);
+  }
+
+  order = get_float(fp);
+
+  // See if the option already exists...
+  if ((o = d->find_option_group(name, &mg)) == NULL)
+  {
+    // Nope, add a new one...
+    o = new ppdcOption(ot, name, text, section, order);
+  }
+  else if (o->type != ot)
+  {
+    fprintf(stderr,
+           _("ppdc: Option %s redefined with a different type on line "
+             "%d of %s.\n"), name, fp->line, fp->filename);
+    return (NULL);
+  }
+  else if (g != mg)
+  {
+    fprintf(stderr,
+           _("ppdc: Option %s defined in two different groups on line "
+             "%d of %s.\n"), name, fp->line, fp->filename);
+    return (NULL);
+  }
+
+  return (o);
+}
+
+
+//
+// 'ppdcSource::get_po()' - Get a message catalog.
+//
+
+ppdcCatalog *                          // O - Message catalog
+ppdcSource::get_po(ppdcFile *fp)       // I - File to read
+{
+  char         locale[32],             // Locale name
+               poname[1024],           // Message catalog filename
+               basedir[1024],          // Base directory
+               *baseptr,               // Pointer into directory
+               pofilename[1024];       // Full filename of message catalog
+  ppdcCatalog  *cat;                   // Message catalog
+
+
+  // Read the #po parameters:
+  //
+  // #po locale "filename.po"
+  if (!get_token(fp, locale, sizeof(locale)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected locale after #po on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if (!get_token(fp, poname, sizeof(poname)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected filename after #po %s on line %d of "
+             "%s.\n"), locale, fp->line, fp->filename);
+    return (NULL);
+  }
+
+  // See if the locale is already loaded...
+  if (find_po(locale))
+  {
+    fprintf(stderr,
+           _("ppdc: Duplicate #po for locale %s on line %d of %s.\n"),
+           locale, fp->line, fp->filename);
+    return (NULL);
+  }
+
+  // Figure out the current directory...
+  strncpy(basedir, fp->filename, sizeof(basedir) - 1);
+
+  if ((baseptr = strrchr(basedir, '/')) != NULL)
+    *baseptr = '\0';
+  else
+    strncpy(basedir, ".", sizeof(basedir));
+
+  // Find the po file...
+  pofilename[0] = '\0';
+
+  if (!poname[0] ||
+      find_include(poname, basedir, pofilename, sizeof(pofilename)))
+  {
+    // Found it, so load it...
+    cat = new ppdcCatalog(locale, pofilename);
+
+    // Reset the filename to the name supplied by the user...
+    cat->filename->release();
+    cat->filename = new ppdcString(poname);
+
+    // Return the catalog...
+    return (cat);
+  }
+  else
+  {
+    fprintf(stderr,
+           _("ppdc: Unable to find #po file %s on line %d of %s.\n"),
+           poname, fp->line, fp->filename);
+    return (NULL);
+  }
+}
+
+
+//
+// 'ppdcSource::get_resolution()' - Get an old-style resolution option.
+//
+
+ppdcChoice *                           // O - Choice data
+ppdcSource::get_resolution(ppdcFile *fp)// I - File to read
+{
+  char         name[1024],             // Name
+               *text,                  // Text
+               temp[256],              // Temporary string
+               command[256],           // Command string
+               *commptr;               // Pointer into command
+  int          xdpi, ydpi,             // X + Y resolution
+               color_order,            // Color order
+               color_space,            // Colorspace
+               compression,            // Compression mode
+               depth,                  // Bits per color
+               row_count,              // Row count
+               row_feed,               // Row feed
+               row_step;               // Row step/interval
+
+
+  // Read the resolution parameters:
+  //
+  // Resolution colorspace bits row-count row-feed row-step name/text
+  if (!get_token(fp, temp, sizeof(temp)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected override field after Resolution on line "
+             "%d of %s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  color_order = get_color_order(temp);
+  color_space = get_color_space(temp);
+  compression = get_integer(temp);
+
+  depth       = get_integer(fp);
+  row_count   = get_integer(fp);
+  row_feed    = get_integer(fp);
+  row_step    = get_integer(fp);
+
+  if (!get_token(fp, name, sizeof(name)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected name/text after Resolution on line %d of "
+             "%s.\n"), fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  switch (sscanf(name, "%dx%d", &xdpi, &ydpi))
+  {
+    case 1 :
+        ydpi = xdpi;
+        break;
+    case 2 :
+        break;
+    default :
+        fprintf(stderr,
+               _("ppdc: Bad resolution name \"%s\" on line %d of "
+                 "%s.\n"), name, fp->line, fp->filename);
+        break;
+}
+
+  // Create the necessary PS commands...
+  snprintf(command, sizeof(command),
+           "<</HWResolution[%d %d]/cupsBitsPerColor %d/cupsRowCount %d"
+           "/cupsRowFeed %d/cupsRowStep %d",
+          xdpi, ydpi, depth, row_count, row_feed, row_step);
+  commptr = command + strlen(command);
+
+  if (color_order >= 0)
+  {
+    snprintf(commptr, sizeof(command) - (size_t)(commptr - command),
+             "/cupsColorOrder %d", color_order);
+    commptr += strlen(commptr);
+  }
+
+  if (color_space >= 0)
+  {
+    snprintf(commptr, sizeof(command) - (size_t)(commptr - command),
+             "/cupsColorSpace %d", color_space);
+    commptr += strlen(commptr);
+  }
+
+  if (compression >= 0)
+  {
+    snprintf(commptr, sizeof(command) - (size_t)(commptr - command),
+             "/cupsCompression %d", compression);
+    commptr += strlen(commptr);
+  }
+
+  snprintf(commptr, sizeof(command) - (size_t)(commptr - command), ">>setpagedevice");
+
+  // Return the new choice...
+  return (new ppdcChoice(name, text, command));
+}
+
+
+//
+// 'ppdcSource::get_simple_profile()' - Get a simple color profile definition.
+//
+
+ppdcProfile *                          // O - Color profile
+ppdcSource::get_simple_profile(ppdcFile *fp)
+                                       // I - File to read
+{
+  char         resolution[1024],       // Resolution/media type
+               *media_type;            // Media type
+  float                m[9];                   // Transform matrix
+  float                kd, rd, g;              // Densities and gamma
+  float                red, green, blue;       // RGB adjustments
+  float                yellow;                 // Yellow density
+  float                color;                  // Color density values
+
+
+  // Get the SimpleColorProfile parameters:
+  //
+  // SimpleColorProfile resolution/mediatype black-density yellow-density
+  //     red-density gamma red-adjust green-adjust blue-adjust
+  if (!get_token(fp, resolution, sizeof(resolution)))
+  {
+    fprintf(stderr,
+           _("ppdc: Expected resolution/mediatype following "
+             "SimpleColorProfile on line %d of %s.\n"),
+           fp->line, fp->filename);
+    return (NULL);
+  }
+
+  if ((media_type = strchr(resolution, '/')) != NULL)
+    *media_type++ = '\0';
+  else
+    media_type = resolution;
+
+  // Collect the profile parameters...
+  kd     = get_float(fp);
+  yellow = get_float(fp);
+  rd     = get_float(fp);
+  g      = get_float(fp);
+  red    = get_float(fp);
+  green  = get_float(fp);
+  blue   = get_float(fp);
+
+  // Build the color profile...
+  color = 0.5f * rd / kd - kd;
+  m[0]  = 1.0f;                                // C
+  m[1]  = color + blue;                        // C + M (blue)
+  m[2]  = color - green;               // C + Y (green)
+  m[3]  = color - blue;                        // M + C (blue)
+  m[4]  = 1.0f;                                // M
+  m[5]  = color + red;                 // M + Y (red)
+  m[6]  = yellow * (color + green);    // Y + C (green)
+  m[7]  = yellow * (color - red);      // Y + M (red)
+  m[8]  = yellow;                      // Y
+
+  if (m[1] > 0.0f)
+  {
+    m[3] -= m[1];
+    m[1] = 0.0f;
+  }
+  else if (m[3] > 0.0f)
+  {
+    m[1] -= m[3];
+    m[3] = 0.0f;
+  }
+
+  if (m[2] > 0.0f)
+  {
+    m[6] -= m[2];
+    m[2] = 0.0f;
+  }
+  else if (m[6] > 0.0f)
+  {
+    m[2] -= m[6];
+    m[6] = 0.0f;
+  }
+
+  if (m[5] > 0.0f)
+  {
+    m[7] -= m[5];
+    m[5] = 0.0f;
+  }
+  else if (m[7] > 0.0f)
+  {
+    m[5] -= m[7];
+    m[7] = 0.0f;
+  }
+
+  // Return the new profile...
+  return (new ppdcProfile(resolution, media_type, kd, g, m));
+}
+
+
+//
+// 'ppdcSource::get_size()' - Get a media size definition from a file.
+//
+
+ppdcMediaSize *                                // O - Media size
+ppdcSource::get_size(ppdcFile *fp)     // I - File to read
+{
+  char         name[1024],             // Name
+               *text;                  // Text
+  float                width,                  // Width
+               length;                 // Length
+
+
+  // Get the name, text, width, and length:
+  //
+  // #media name/text width length
+  if (!get_token(fp, name, sizeof(name)))
+    return (NULL);
+
+  if ((text = strchr(name, '/')) != NULL)
+    *text++ = '\0';
+  else
+    text = name;
+
+  if ((width = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  if ((length = get_measurement(fp)) < 0.0f)
+    return (NULL);
+
+  // Return the new media size...
+  return (new ppdcMediaSize(name, text, width, length, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+
+//
+// 'ppdcSource::get_token()' - Get a token from a file.
+//
+
+char *                                 // O - Token string or NULL
+ppdcSource::get_token(ppdcFile *fp,    // I - File to read
+                      char     *buffer,        // I - Buffer
+                     int      buflen)  // I - Length of buffer
+{
+  char         *bufptr,                // Pointer into string buffer
+               *bufend;                // End of string buffer
+  int          ch,                     // Character from file
+               nextch,                 // Next char in file
+               quote,                  // Quote character used...
+               empty,                  // Empty input?
+               startline;              // Start line for quote
+  char         name[256],              // Name string
+               *nameptr;               // Name pointer
+  ppdcVariable *var;                   // Variable pointer
+
+
+  // Mark the beginning and end of the buffer...
+  bufptr = buffer;
+  bufend = buffer + buflen - 1;
+
+  // Loop intil we've read a token...
+  quote     = 0;
+  startline = 0;
+  empty     = 1;
+
+  while ((ch = fp->get()) != EOF)
+  {
+    if (isspace(ch) && !quote)
+    {
+      if (empty)
+        continue;
+      else
+        break;
+    }
+    else if (ch == '$')
+    {
+      // Variable substitution
+      empty = 0;
+
+      for (nameptr = name; (ch = fp->peek()) != EOF;)
+      {
+        if (!isalnum(ch) && ch != '_')
+         break;
+       else if (nameptr < (name + sizeof(name) - 1))
+         *nameptr++ = (char)fp->get();
+      }
+
+      if (nameptr == name)
+      {
+        // Just substitute this character...
+       if (ch == '$')
+       {
+         // $$ = $
+         if (bufptr < bufend)
+           *bufptr++ = (char)fp->get();
+       }
+       else
+       {
+         // $ch = $ch
+          fprintf(stderr,
+                 _("ppdc: Bad variable substitution ($%c) on line %d "
+                   "of %s.\n"), ch, fp->line, fp->filename);
+
+         if (bufptr < bufend)
+           *bufptr++ = '$';
+       }
+      }
+      else
+      {
+        // Substitute the variable value...
+       *nameptr = '\0';
+       var = find_variable(name);
+       if (var)
+       {
+         strncpy(bufptr, var->value->value, (size_t)(bufend - bufptr + 1));
+         bufptr += strlen(bufptr);
+       }
+       else
+       {
+         if (!(cond_state & PPDC_COND_SKIP))
+           fprintf(stderr,
+                   _("ppdc: Undefined variable (%s) on line %d of "
+                     "%s.\n"), name, fp->line, fp->filename);
+
+         snprintf(bufptr, (size_t)(bufend - bufptr + 1), "$%s", name);
+         bufptr += strlen(bufptr);
+       }
+      }
+    }
+    else if (ch == '/' && !quote)
+    {
+      // Possibly a comment...
+      nextch = fp->peek();
+
+      if (nextch == '*')
+      {
+        // C comment...
+       fp->get();
+       ch = fp->get();
+       while ((nextch = fp->get()) != EOF)
+       {
+         if (ch == '*' && nextch == '/')
+           break;
+
+         ch = nextch;
+       }
+
+        if (nextch == EOF)
+          break;
+      }
+      else if (nextch == '/')
+      {
+        // C++ comment...
+        while ((nextch = fp->get()) != EOF)
+          if (nextch == '\n')
+           break;
+
+        if (nextch == EOF)
+          break;
+      }
+      else
+      {
+        // Not a comment...
+        empty = 0;
+
+       if (bufptr < bufend)
+         *bufptr++ = (char)ch;
+      }
+    }
+    else if (ch == '\'' || ch == '\"')
+    {
+      empty = 0;
+
+      if (quote == ch)
+      {
+        // Ending the current quoted string...
+        quote = 0;
+      }
+      else if (quote)
+      {
+        // Insert the opposing quote char...
+       if (bufptr < bufend)
+          *bufptr++ = (char)ch;
+      }
+      else
+      {
+        // Start a new quoted string...
+        startline = fp->line;
+        quote     = ch;
+      }
+    }
+    else if ((ch == '(' || ch == '<') && !quote)
+    {
+      empty     = 0;
+      quote     = ch;
+      startline = fp->line;
+
+      if (bufptr < bufend)
+       *bufptr++ = (char)ch;
+    }
+    else if ((ch == ')' && quote == '(') || (ch == '>' && quote == '<'))
+    {
+      quote = 0;
+
+      if (bufptr < bufend)
+       *bufptr++ = (char)ch;
+    }
+    else if (ch == '\\')
+    {
+      empty = 0;
+
+      if ((ch = fp->get()) == EOF)
+        break;
+
+      if (bufptr < bufend)
+        *bufptr++ = (char)ch;
+    }
+    else if (bufptr < bufend)
+    {
+      empty = 0;
+
+      *bufptr++ = (char)ch;
+
+      if ((ch == '{' || ch == '}') && !quote)
+        break;
+    }
+  }
+
+  if (quote)
+  {
+    fprintf(stderr,
+           _("ppdc: Unterminated string starting with %c on line %d "
+             "of %s.\n"), quote, startline, fp->filename);
+    return (NULL);
+  }
+
+  if (empty)
+    return (NULL);
+  else
+  {
+    *bufptr = '\0';
+    return (buffer);
+  }
+}
+
+
+//
+// 'ppdcSource::get_variable()' - Get a variable definition.
+//
+
+ppdcVariable *                         // O - Variable
+ppdcSource::get_variable(ppdcFile *fp) // I - File to read
+{
+  char         name[1024],             // Name
+               value[1024];            // Value
+
+
+  // Get the name and value:
+  //
+  // #define name value
+  if (!get_token(fp, name, sizeof(name)))
+    return (NULL);
+
+  if (!get_token(fp, value, sizeof(value)))
+    return (NULL);
+
+  // Set the variable...
+  return (set_variable(name, value));
+}
+
+
+//
+// 'ppdcSource::quotef()' - Write a formatted, quoted string...
+//
+
+int                                    // O - Number bytes on success, -1 on failure
+ppdcSource::quotef(cups_file_t *fp,    // I - File to write to
+                   const char  *format,        // I - Printf-style format string
+                  ...)                 // I - Additional args as needed
+{
+  va_list      ap;                     // Pointer to additional arguments
+  int          bytes;                  // Bytes written
+  char         sign,                   // Sign of format width
+               size,                   // Size character (h, l, L)
+               type;                   // Format type character
+  const char   *bufformat;             // Start of format
+  int          width,                  // Width of field
+               prec;                   // Number of characters of precision
+  char         tformat[100];           // Temporary format string for fprintf()
+  char         *s;                     // Pointer to string
+  int          slen;                   // Length of string
+  int          i;                      // Looping var
+
+
+  // Range check input...
+  if (!fp || !format)
+    return (-1);
+
+  // Loop through the format string, formatting as needed...
+  va_start(ap, format);
+
+  bytes = 0;
+
+  while (*format)
+  {
+    if (*format == '%')
+    {
+      bufformat = format;
+      format ++;
+
+      if (*format == '%')
+      {
+        cupsFilePutChar(fp, *format++);
+       bytes ++;
+       continue;
+      }
+      else if (strchr(" -+#\'", *format))
+        sign = *format++;
+      else
+        sign = 0;
+
+      width = 0;
+      while (isdigit(*format))
+        width = width * 10 + *format++ - '0';
+
+      if (*format == '.')
+      {
+        format ++;
+       prec = 0;
+
+       while (isdigit(*format))
+          prec = prec * 10 + *format++ - '0';
+      }
+      else
+        prec = -1;
+
+      if (*format == 'l' && format[1] == 'l')
+      {
+        size = 'L';
+       format += 2;
+      }
+      else if (*format == 'h' || *format == 'l' || *format == 'L')
+        size = *format++;
+      else
+        size = '\0';
+
+      if (!*format)
+        break;
+
+      type = *format++;
+
+      switch (type)
+      {
+       case 'E' : // Floating point formats
+       case 'G' :
+       case 'e' :
+       case 'f' :
+       case 'g' :
+           if ((format - bufformat + 1) > (int)sizeof(tformat))
+             break;
+
+           memcpy(tformat, bufformat, (size_t)(format - bufformat));
+           tformat[format - bufformat] = '\0';
+
+           bytes += cupsFilePrintf(fp, tformat, va_arg(ap, double));
+           break;
+
+        case 'B' : // Integer formats
+       case 'X' :
+       case 'b' :
+        case 'd' :
+       case 'i' :
+       case 'o' :
+       case 'u' :
+       case 'x' :
+           if ((format - bufformat + 1) > (int)sizeof(tformat))
+             break;
+
+           memcpy(tformat, bufformat, (size_t)(format - bufformat));
+           tformat[format - bufformat] = '\0';
+
+#  ifdef HAVE_LONG_LONG
+            if (size == 'L')
+             bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long long));
+           else
+#  endif /* HAVE_LONG_LONG */
+            if (size == 'l')
+             bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long));
+           else
+             bytes += cupsFilePrintf(fp, tformat, va_arg(ap, int));
+           break;
+
+       case 'p' : // Pointer value
+           if ((format - bufformat + 1) > (int)sizeof(tformat))
+             break;
+
+           memcpy(tformat, bufformat, (size_t)(format - bufformat));
+           tformat[format - bufformat] = '\0';
+
+           bytes += cupsFilePrintf(fp, tformat, va_arg(ap, void *));
+           break;
+
+        case 'c' : // Character or character array
+           if (width <= 1)
+           {
+             bytes ++;
+             cupsFilePutChar(fp, va_arg(ap, int));
+           }
+           else
+           {
+             cupsFileWrite(fp, va_arg(ap, char *), (size_t)width);
+             bytes += width;
+           }
+           break;
+
+       case 's' : // String
+           if ((s = va_arg(ap, char *)) == NULL)
+             s = (char *)"(nil)";
+
+           slen = (int)strlen(s);
+           if (slen > width && prec != width)
+             width = slen;
+
+            if (slen > width)
+             slen = width;
+
+            if (sign != '-')
+           {
+             for (i = width - slen; i > 0; i --, bytes ++)
+               cupsFilePutChar(fp, ' ');
+           }
+
+            for (i = slen; i > 0; i --, s ++, bytes ++)
+           {
+             if (*s == '\\' || *s == '\"')
+             {
+               cupsFilePutChar(fp, '\\');
+               bytes ++;
+             }
+
+             cupsFilePutChar(fp, *s);
+           }
+
+            if (sign == '-')
+           {
+             for (i = width - slen; i > 0; i --, bytes ++)
+               cupsFilePutChar(fp, ' ');
+           }
+           break;
+      }
+    }
+    else
+    {
+      cupsFilePutChar(fp, *format++);
+      bytes ++;
+    }
+  }
+
+  va_end(ap);
+
+  // Return the number of characters written.
+  return (bytes);
+}
+
+
+//
+// 'ppdcSource::read_file()' - Read a driver source file.
+//
+
+void
+ppdcSource::read_file(const char  *f,  // I - File to read
+                      cups_file_t *ffp)        // I - File pointer to use
+{
+  ppdcFile *fp = new ppdcFile(f, ffp);
+  scan_file(fp);
+  delete fp;
+
+  if (cond_current != cond_stack)
+    fprintf(stderr, _("ppdc: Missing #endif at end of \"%s\".\n"), f);
+}
+
+
+//
+// 'ppdcSource::scan_file()' - Scan a driver source file.
+//
+
+void
+ppdcSource::scan_file(ppdcFile   *fp,  // I - File to read
+                      ppdcDriver *td,  // I - Driver template
+                     bool       inc)   // I - Including?
+{
+  ppdcDriver   *d;                     // Current driver
+  ppdcGroup    *g,                     // Current group
+               *mg,                    // Matching group
+               *general,               // General options group
+               *install;               // Installable options group
+  ppdcOption   *o;                     // Current option
+  ppdcChoice   *c;                     // Current choice
+  char         temp[256],              // Token from file...
+               *ptr;                   // Pointer into token
+  int          isdefault;              // Default option?
+
+
+  // Initialize things as needed...
+  if (inc && td)
+  {
+    d = td;
+    d->retain();
+  }
+  else
+    d = new ppdcDriver(td);
+
+  if ((general = d->find_group("General")) == NULL)
+  {
+    general = new ppdcGroup("General", NULL);
+    d->add_group(general);
+  }
+
+  if ((install = d->find_group("InstallableOptions")) == NULL)
+  {
+    install = new ppdcGroup("InstallableOptions", "Installable Options");
+    d->add_group(install);
+  }
+
+  // Loop until EOF or }
+  o = 0;
+  g = general;
+
+  while (get_token(fp, temp, sizeof(temp)))
+  {
+    if (temp[0] == '*')
+    {
+      // Mark the next choice as the default
+      isdefault = 1;
+
+      for (ptr = temp; ptr[1]; ptr ++)
+        *ptr = ptr[1];
+
+      *ptr = '\0';
+    }
+    else
+    {
+      // Don't mark the next choice as the default
+      isdefault = 0;
+    }
+
+    if (!strcasecmp(temp, "}"))
+    {
+      // Close this one out...
+      break;
+    }
+    else if (!strcasecmp(temp, "{"))
+    {
+      // Open a new child...
+      scan_file(fp, d);
+    }
+    else if (!strcasecmp(temp, "#if"))
+    {
+      if ((cond_current - cond_stack) >= 100)
+      {
+        fprintf(stderr,
+               _("ppdc: Too many nested #if's on line %d of %s.\n"),
+               fp->line, fp->filename);
+       break;
+      }
+
+      cond_current ++;
+      if (get_integer(fp) > 0)
+        *cond_current = PPDC_COND_SATISFIED;
+      else
+      {
+        *cond_current = PPDC_COND_SKIP;
+       cond_state    |= PPDC_COND_SKIP;
+      }
+    }
+    else if (!strcasecmp(temp, "#elif"))
+    {
+      if (cond_current == cond_stack)
+      {
+        fprintf(stderr, _("ppdc: Missing #if on line %d of %s.\n"),
+               fp->line, fp->filename);
+        break;
+      }
+
+      if (*cond_current & PPDC_COND_SATISFIED)
+      {
+        get_integer(fp);
+       *cond_current |= PPDC_COND_SKIP;
+      }
+      else if (get_integer(fp) > 0)
+      {
+        *cond_current |= PPDC_COND_SATISFIED;
+       *cond_current &= ~PPDC_COND_SKIP;
+      }
+      else
+        *cond_current |= PPDC_COND_SKIP;
+
+      // Update the current state
+      int *cond_temp = cond_current;   // Temporary stack pointer
+
+      cond_state = PPDC_COND_NORMAL;
+      while (cond_temp > cond_stack)
+        if (*cond_temp & PPDC_COND_SKIP)
+       {
+         cond_state = PPDC_COND_SKIP;
+         break;
+       }
+       else
+         cond_temp --;
+    }
+    else if (!strcasecmp(temp, "#else"))
+    {
+      if (cond_current == cond_stack)
+      {
+        fprintf(stderr, _("ppdc: Missing #if on line %d of %s.\n"),
+               fp->line, fp->filename);
+        break;
+      }
+
+      if (*cond_current & PPDC_COND_SATISFIED)
+       *cond_current |= PPDC_COND_SKIP;
+      else
+      {
+        *cond_current |= PPDC_COND_SATISFIED;
+       *cond_current &= ~PPDC_COND_SKIP;
+      }
+
+      // Update the current state
+      int *cond_temp = cond_current;   // Temporary stack pointer
+
+      cond_state = PPDC_COND_NORMAL;
+      while (cond_temp > cond_stack)
+        if (*cond_temp & PPDC_COND_SKIP)
+       {
+         cond_state = PPDC_COND_SKIP;
+         break;
+       }
+       else
+         cond_temp --;
+    }
+    else if (!strcasecmp(temp, "#endif"))
+    {
+      if (cond_current == cond_stack)
+      {
+        fprintf(stderr, _("ppdc: Missing #if on line %d of %s.\n"),
+               fp->line, fp->filename);
+        break;
+      }
+
+      cond_current --;
+
+      // Update the current state
+      int *cond_temp = cond_current;   // Temporary stack pointer
+
+      cond_state = PPDC_COND_NORMAL;
+      while (cond_temp > cond_stack)
+        if (*cond_temp & PPDC_COND_SKIP)
+       {
+         cond_state = PPDC_COND_SKIP;
+         break;
+       }
+       else
+         cond_temp --;
+    }
+    else if (!strcasecmp(temp, "#define"))
+    {
+      // Get the variable...
+      get_variable(fp);
+    }
+    else if (!strcasecmp(temp, "#include"))
+    {
+      // #include filename
+      char     basedir[1024],          // Base directory
+               *baseptr,               // Pointer into directory
+               inctemp[1024],          // Initial filename
+               incname[1024];          // Include filename
+      ppdcFile *incfile;               // Include file
+      int      *old_current = cond_current;
+                                       // Previous current stack
+
+
+      // Get the include name...
+      if (!get_token(fp, inctemp, sizeof(inctemp)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected include filename on line %d of "
+                 "%s.\n"), fp->line, fp->filename);
+        break;
+      }
+
+      if (cond_state)
+        continue;
+
+      // Figure out the current directory...
+      strncpy(basedir, fp->filename, sizeof(basedir) - 1);
+
+      if ((baseptr = strrchr(basedir, '/')) != NULL)
+       *baseptr = '\0';
+      else
+       strncpy(basedir, ".", sizeof(basedir));
+
+      // Find the include file...
+      if (find_include(inctemp, basedir, incname, sizeof(incname)))
+      {
+       // Open the include file, scan it, and then close it...
+       incfile = new ppdcFile(incname);
+       scan_file(incfile, d, true);
+       delete incfile;
+
+       if (cond_current != old_current)
+         fprintf(stderr, _("ppdc: Missing #endif at end of \"%s\".\n"),
+                 incname);
+      }
+      else
+      {
+       // Can't find it!
+       fprintf(stderr,
+               _("ppdc: Unable to find include file \"%s\" on line %d "
+                 "of %s.\n"), inctemp, fp->line, fp->filename);
+       break;
+      }
+    }
+    else if (!strcasecmp(temp, "#media"))
+    {
+      ppdcMediaSize    *m;             // Media size
+
+
+      // Get a media size...
+      m = get_size(fp);
+      if (m)
+      {
+        if (cond_state)
+         m->release();
+       else
+          sizes->add(m);
+      }
+    }
+    else if (!strcasecmp(temp, "#po"))
+    {
+      ppdcCatalog      *cat;           // Message catalog
+
+
+      // Get a message catalog...
+      cat = get_po(fp);
+      if (cat)
+      {
+        if (cond_state)
+         cat->release();
+       else
+         po_files->add(cat);
+      }
+    }
+    else if (!strcasecmp(temp, "Attribute") ||
+             !strcasecmp(temp, "LocAttribute"))
+    {
+      ppdcAttr *a;                     // Attribute
+
+
+      // Get an attribute...
+      a = get_attr(fp, !strcasecmp(temp, "LocAttribute"));
+      if (a)
+      {
+        if (cond_state)
+         a->release();
+       else
+          d->add_attr(a);
+      }
+    }
+    else if (!strcasecmp(temp, "Choice"))
+    {
+      // Get a choice...
+      c = get_choice(fp);
+      if (!c)
+        break;
+
+      if (cond_state)
+      {
+        c->release();
+        continue;
+      }
+
+      // Add it to the current option...
+      if (!o)
+      {
+        c->release();
+        fprintf(stderr,
+               _("ppdc: Choice found on line %d of %s with no "
+                 "Option.\n"), fp->line, fp->filename);
+        break;
+      }
+
+      o->add_choice(c);
+
+      if (isdefault)
+        o->set_defchoice(c);
+    }
+    else if (!strcasecmp(temp, "ColorDevice"))
+    {
+      // ColorDevice boolean
+      if (cond_state)
+        get_boolean(fp);
+      else
+        d->color_device = get_boolean(fp);
+    }
+    else if (!strcasecmp(temp, "ColorModel"))
+    {
+      // Get the color model
+      c = get_color_model(fp);
+      if (!c)
+        continue;
+
+      if (cond_state)
+      {
+        c->release();
+        continue;
+      }
+
+      // Add the choice to the ColorModel option...
+      if ((o = d->find_option("ColorModel")) == NULL)
+      {
+       // Create the ColorModel option...
+       o = new ppdcOption(PPDC_PICKONE, "ColorModel", "Color Mode", PPDC_SECTION_ANY, 10.0f);
+       g = general;
+       g->add_option(o);
+      }
+
+      o->add_choice(c);
+
+      if (isdefault)
+       o->set_defchoice(c);
+
+      o = NULL;
+    }
+    else if (!strcasecmp(temp, "ColorProfile"))
+    {
+      ppdcProfile      *p;             // Color profile
+
+
+      // Get the color profile...
+      p = get_color_profile(fp);
+
+      if (p)
+      {
+        if (cond_state)
+         p->release();
+       else
+          d->profiles->add(p);
+      }
+    }
+    else if (!strcasecmp(temp, "Copyright"))
+    {
+      // Copyright string
+      char     copytemp[8192],         // Copyright string
+               *copyptr,               // Pointer into string
+               *copyend;               // Pointer to end of string
+
+
+      // Get the copyright string...
+      if (!get_token(fp, copytemp, sizeof(temp)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected string after Copyright on line %d "
+                 "of %s.\n"), fp->line, fp->filename);
+       break;
+      }
+
+      if (cond_state)
+        continue;
+
+      // Break it up into individual lines...
+      for (copyptr = copytemp; copyptr; copyptr = copyend)
+      {
+        if ((copyend = strchr(copyptr, '\n')) != NULL)
+         *copyend++ = '\0';
+
+        d->copyright->add(new ppdcString(copyptr));
+      }
+    }
+    else if (!strcasecmp(temp, "CustomMedia"))
+    {
+      ppdcMediaSize    *m;             // Media size
+
+
+      // Get a custom media size...
+      m = get_custom_size(fp);
+
+      if (cond_state)
+      {
+        m->release();
+        continue;
+      }
+
+      if (m)
+        d->sizes->add(m);
+
+      if (isdefault)
+        d->set_default_size(m);
+    }
+    else if (!strcasecmp(temp, "Cutter"))
+    {
+      // Cutter boolean
+      int      have_cutter;            // Have a paper cutter?
+
+
+      have_cutter = get_boolean(fp);
+      if (have_cutter <= 0 || cond_state)
+        continue;
+
+      if (!d->find_option("CutMedia"))
+      {
+        o = new ppdcOption(PPDC_BOOLEAN, "CutMedia", "Cut Media", PPDC_SECTION_ANY, 10.0f);
+
+       g = general;
+       g->add_option(o);
+
+       c = new ppdcChoice("False", NULL, "<</CutMedia 0>>setpagedevice");
+       o->add_choice(c);
+       o->set_defchoice(c);
+
+       c = new ppdcChoice("True", NULL, "<</CutMedia 4>>setpagedevice");
+       o->add_choice(c);
+        o = NULL;
+      }
+    }
+    else if (!strcasecmp(temp, "Darkness"))
+    {
+      // Get the darkness choice...
+      c = get_generic(fp, "Darkness", NULL, "cupsCompression");
+      if (!c)
+        continue;
+
+      if (cond_state)
+      {
+        c->release();
+        continue;
+      }
+
+      // Add the choice to the cupsDarkness option...
+      if ((o = d->find_option_group("cupsDarkness", &mg)) == NULL)
+      {
+       // Create the cupsDarkness option...
+       o = new ppdcOption(PPDC_PICKONE, "cupsDarkness", "Darkness", PPDC_SECTION_ANY, 10.0f);
+       g = general;
+       g->add_option(o);
+      }
+      else if (mg != general)
+      {
+       fprintf(stderr,
+               _("ppdc: Option %s defined in two different groups on "
+                 "line %d of %s.\n"), "cupsDarkness", fp->line,
+               fp->filename);
+       c->release();
+       continue;
+      }
+
+      o->add_choice(c);
+
+      if (isdefault)
+       o->set_defchoice(c);
+
+      o = NULL;
+    }
+    else if (!strcasecmp(temp, "DriverType"))
+    {
+      int      i;                      // Looping var
+
+
+      // DriverType keyword
+      if (!get_token(fp, temp, sizeof(temp)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected driver type keyword following "
+                 "DriverType on line %d of %s.\n"),
+               fp->line, fp->filename);
+        continue;
+      }
+
+      if (cond_state)
+        continue;
+
+      for (i = 0; i < (int)(sizeof(driver_types) / sizeof(driver_types[0])); i ++)
+        if (!strcasecmp(temp, driver_types[i]))
+         break;
+
+      if (i < (int)(sizeof(driver_types) / sizeof(driver_types[0])))
+        d->type = (ppdcDrvType)i;
+      else if (!strcasecmp(temp, "dymo"))
+        d->type = PPDC_DRIVER_LABEL;
+      else
+        fprintf(stderr,
+               _("ppdc: Unknown driver type %s on line %d of %s.\n"),
+               temp, fp->line, fp->filename);
+    }
+    else if (!strcasecmp(temp, "Duplex"))
+      get_duplex(fp, d);
+    else if (!strcasecmp(temp, "Filter"))
+    {
+      ppdcFilter       *f;             // Filter
+
+
+      // Get the filter value...
+      f = get_filter(fp);
+      if (f)
+      {
+        if (cond_state)
+         f->release();
+       else
+          d->filters->add(f);
+      }
+    }
+    else if (!strcasecmp(temp, "Finishing"))
+    {
+      // Get the finishing choice...
+      c = get_generic(fp, "Finishing", "OutputType", NULL);
+      if (!c)
+        continue;
+
+      if (cond_state)
+      {
+        c->release();
+        continue;
+      }
+
+      // Add the choice to the cupsFinishing option...
+      if ((o = d->find_option_group("cupsFinishing", &mg)) == NULL)
+      {
+       // Create the cupsFinishing option...
+       o = new ppdcOption(PPDC_PICKONE, "cupsFinishing", "Finishing", PPDC_SECTION_ANY, 10.0f);
+       g = general;
+       g->add_option(o);
+      }
+      else if (mg != general)
+      {
+       fprintf(stderr,
+               _("ppdc: Option %s defined in two different groups on "
+                 "line %d of %s.\n"), "cupsFinishing", fp->line,
+               fp->filename);
+       c->release();
+       continue;
+      }
+
+      o->add_choice(c);
+
+      if (isdefault)
+       o->set_defchoice(c);
+
+      o = NULL;
+    }
+    else if (!strcasecmp(temp, "Font") ||
+             !strcasecmp(temp, "#font"))
+    {
+      ppdcFont *f;                     // Font
+
+
+      // Get a font...
+      f = get_font(fp);
+      if (f)
+      {
+        if (cond_state)
+         f->release();
+       else
+       {
+         if (!strcasecmp(temp, "#font"))
+           base_fonts->add(f);
+         else
+           d->add_font(f);
+
+         if (isdefault)
+           d->set_default_font(f);
+       }
+      }
+    }
+    else if (!strcasecmp(temp, "Group"))
+    {
+      // Get a group...
+      ppdcGroup *tempg = get_group(fp, d);
+
+      if (!tempg)
+        break;
+
+      if (cond_state)
+      {
+        if (!d->find_group(tempg->name->value))
+          tempg->release();
+      }
+      else
+      {
+       if (!d->find_group(tempg->name->value))
+         d->add_group(tempg);
+
+        g = tempg;
+      }
+    }
+    else if (!strcasecmp(temp, "HWMargins"))
+    {
+      // HWMargins left bottom right top
+      d->left_margin   = get_measurement(fp);
+      d->bottom_margin = get_measurement(fp);
+      d->right_margin  = get_measurement(fp);
+      d->top_margin    = get_measurement(fp);
+    }
+    else if (!strcasecmp(temp, "InputSlot"))
+    {
+      // Get the input slot choice...
+      c = get_generic(fp, "InputSlot", NULL, "MediaPosition");
+      if (!c)
+        continue;
+
+      if (cond_state)
+      {
+        c->release();
+        continue;
+      }
+
+      // Add the choice to the InputSlot option...
+
+      if ((o = d->find_option_group("InputSlot", &mg)) == NULL)
+      {
+       // Create the InputSlot option...
+       o = new ppdcOption(PPDC_PICKONE, "InputSlot", "Media Source",
+                          PPDC_SECTION_ANY, 10.0f);
+       g = general;
+       g->add_option(o);
+      }
+      else if (mg != general)
+      {
+       fprintf(stderr,
+               _("ppdc: Option %s defined in two different groups on "
+                 "line %d of %s.\n"), "InputSlot", fp->line,
+               fp->filename);
+       c->release();
+       continue;
+      }
+
+      o->add_choice(c);
+
+      if (isdefault)
+       o->set_defchoice(c);
+
+      o = NULL;
+    }
+    else if (!strcasecmp(temp, "Installable"))
+    {
+      // Get the installable option...
+      o = get_installable(fp);
+
+      // Add it as needed...
+      if (o)
+      {
+        if (cond_state)
+         o->release();
+       else
+          install->add_option(o);
+
+        o = NULL;
+      }
+    }
+    else if (!strcasecmp(temp, "ManualCopies"))
+    {
+      // ManualCopies boolean
+      if (cond_state)
+        get_boolean(fp);
+      else
+        d->manual_copies = get_boolean(fp);
+    }
+    else if (!strcasecmp(temp, "Manufacturer"))
+    {
+      // Manufacturer name
+      char     name[256];              // Model name string
+
+
+      if (!get_token(fp, name, sizeof(name)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected name after Manufacturer on line %d "
+                 "of %s.\n"), fp->line, fp->filename);
+       break;
+      }
+
+      if (!cond_state)
+        d->set_manufacturer(name);
+    }
+    else if (!strcasecmp(temp, "MaxSize"))
+    {
+      // MaxSize width length
+      if (cond_state)
+      {
+        get_measurement(fp);
+       get_measurement(fp);
+      }
+      else
+      {
+       d->max_width  = get_measurement(fp);
+       d->max_length = get_measurement(fp);
+      }
+    }
+    else if (!strcasecmp(temp, "MediaSize"))
+    {
+      // MediaSize keyword
+      char             name[41];       // Media size name
+      ppdcMediaSize    *m,             // Matching media size...
+                       *dm;            // Driver media size...
+
+
+      if (get_token(fp, name, sizeof(name)) == NULL)
+      {
+        fprintf(stderr,
+               _("ppdc: Expected name after MediaSize on line %d of "
+                 "%s.\n"), fp->line, fp->filename);
+       break;
+      }
+
+      if (cond_state)
+        continue;
+
+      m = find_size(name);
+
+      if (!m)
+      {
+        fprintf(stderr,
+               _("ppdc: Unknown media size \"%s\" on line %d of "
+                 "%s.\n"), name, fp->line, fp->filename);
+       break;
+      }
+
+      // Add this size to the driver...
+      dm = new ppdcMediaSize(m->name->value, m->text->value,
+                             m->width, m->length, d->left_margin,
+                            d->bottom_margin, d->right_margin,
+                            d->top_margin);
+      d->sizes->add(dm);
+
+      if (isdefault)
+        d->set_default_size(dm);
+    }
+    else if (!strcasecmp(temp, "MediaType"))
+    {
+      // Get the media type choice...
+      c = get_generic(fp, "MediaType", "MediaType", "cupsMediaType");
+      if (!c)
+        continue;
+
+      if (cond_state)
+      {
+        c->release();
+        continue;
+      }
+
+      // Add the choice to the MediaType option...
+      if ((o = d->find_option_group("MediaType", &mg)) == NULL)
+      {
+       // Create the MediaType option...
+       o = new ppdcOption(PPDC_PICKONE, "MediaType", "Media Type",
+                          PPDC_SECTION_ANY, 10.0f);
+       g = general;
+       g->add_option(o);
+      }
+      else if (mg != general)
+      {
+       fprintf(stderr,
+               _("ppdc: Option %s defined in two different groups on "
+                 "line %d of %s.\n"), "MediaType", fp->line,
+               fp->filename);
+       c->release();
+       continue;
+      }
+
+      o->add_choice(c);
+
+      if (isdefault)
+       o->set_defchoice(c);
+
+      o = NULL;
+    }
+    else if (!strcasecmp(temp, "MinSize"))
+    {
+      // MinSize width length
+      if (cond_state)
+      {
+        get_measurement(fp);
+       get_measurement(fp);
+      }
+      else
+      {
+       d->min_width  = get_measurement(fp);
+       d->min_length = get_measurement(fp);
+      }
+    }
+    else if (!strcasecmp(temp, "ModelName"))
+    {
+      // ModelName name
+      char     name[256];              // Model name string
+
+
+      if (!get_token(fp, name, sizeof(name)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected name after ModelName on line %d of "
+                 "%s.\n"), fp->line, fp->filename);
+       break;
+      }
+
+      if (!cond_state)
+        d->set_model_name(name);
+    }
+    else if (!strcasecmp(temp, "ModelNumber"))
+    {
+      // ModelNumber number
+      if (cond_state)
+        get_integer(fp);
+      else
+        d->model_number = get_integer(fp);
+    }
+    else if (!strcasecmp(temp, "Option"))
+    {
+      // Get an option...
+      ppdcOption *tempo = get_option(fp, d, g);
+
+      if (!tempo)
+        break;
+
+      if (cond_state)
+      {
+        if (!g->find_option(tempo->name->value))
+         tempo->release();
+      }
+      else
+      {
+        if (!g->find_option(tempo->name->value))
+         g->add_option(tempo);
+
+        o = tempo;
+      }
+    }
+    else if (!strcasecmp(temp, "FileName"))
+    {
+      // FileName name
+      char     name[256];              // Filename string
+
+
+      if (!get_token(fp, name, sizeof(name)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected name after FileName on line %d of "
+                 "%s.\n"), fp->line, fp->filename);
+       break;
+      }
+
+      if (!cond_state)
+        d->set_file_name(name);
+    }
+    else if (!strcasecmp(temp, "PCFileName"))
+    {
+      // PCFileName name
+      char     name[256];              // PC filename string
+
+
+      if (!get_token(fp, name, sizeof(name)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected name after PCFileName on line %d of "
+                 "%s.\n"), fp->line, fp->filename);
+       break;
+      }
+
+      if (!cond_state)
+        d->set_pc_file_name(name);
+    }
+    else if (!strcasecmp(temp, "Resolution"))
+    {
+      // Get the resolution choice...
+      c = get_resolution(fp);
+      if (!c)
+        continue;
+
+      if (cond_state)
+      {
+        c->release();
+        continue;
+      }
+
+      // Add the choice to the Resolution option...
+      if ((o = d->find_option_group("Resolution", &mg)) == NULL)
+      {
+       // Create the Resolution option...
+       o = new ppdcOption(PPDC_PICKONE, "Resolution", NULL, PPDC_SECTION_ANY,
+                          10.0f);
+       g = general;
+       g->add_option(o);
+      }
+      else if (mg != general)
+      {
+       fprintf(stderr,
+               _("ppdc: Option %s defined in two different groups on "
+                 "line %d of %s.\n"), "Resolution", fp->line,
+               fp->filename);
+       c->release();
+       continue;
+      }
+
+      o->add_choice(c);
+
+      if (isdefault)
+       o->set_defchoice(c);
+
+      o = NULL;
+    }
+    else if (!strcasecmp(temp, "SimpleColorProfile"))
+    {
+      ppdcProfile      *p;             // Color profile
+
+
+      // Get the color profile...
+      p = get_simple_profile(fp);
+
+      if (p)
+      {
+        if (cond_state)
+         p->release();
+       else
+          d->profiles->add(p);
+      }
+    }
+    else if (!strcasecmp(temp, "Throughput"))
+    {
+      // Throughput number
+      if (cond_state)
+        get_integer(fp);
+      else
+        d->throughput = get_integer(fp);
+    }
+    else if (!strcasecmp(temp, "UIConstraints"))
+    {
+      ppdcConstraint   *con;           // Constraint
+
+
+      con = get_constraint(fp);
+
+      if (con)
+      {
+        if (cond_state)
+         con->release();
+       else
+         d->constraints->add(con);
+      }
+    }
+    else if (!strcasecmp(temp, "VariablePaperSize"))
+    {
+      // VariablePaperSize boolean
+      if (cond_state)
+        get_boolean(fp);
+      else
+       d->variable_paper_size = get_boolean(fp);
+    }
+    else if (!strcasecmp(temp, "Version"))
+    {
+      // Version string
+      char     name[256];              // Model name string
+
+
+      if (!get_token(fp, name, sizeof(name)))
+      {
+        fprintf(stderr,
+               _("ppdc: Expected string after Version on line %d of "
+                 "%s.\n"), fp->line, fp->filename);
+       break;
+      }
+
+      if (!cond_state)
+        d->set_version(name);
+    }
+    else
+    {
+      fprintf(stderr,
+             _("ppdc: Unknown token \"%s\" seen on line %d of %s.\n"),
+             temp, fp->line, fp->filename);
+      break;
+    }
+  }
+
+  // Done processing this block, is there anything to save?
+  if (!inc)
+  {
+    if (!d->pc_file_name || !d->model_name || !d->manufacturer || !d->version ||
+       !d->sizes->count)
+    {
+      // Nothing to save...
+      d->release();
+    }
+    else
+    {
+      // Got a driver, save it...
+      drivers->add(d);
+    }
+  }
+  else if (inc && td)
+    td->release();
+}
+
+
+//
+// 'ppdcSource::set_variable()' - Set a variable.
+//
+
+ppdcVariable *                         // O - Variable
+ppdcSource::set_variable(
+    const char *name,                  // I - Name
+    const char *value)                 // I - Value
+{
+  ppdcVariable *v;                     // Variable
+
+
+  // See if the variable exists already...
+  v = find_variable(name);
+  if (v)
+  {
+    // Change the variable value...
+    v->set_value(value);
+  }
+  else
+  {
+    // Create a new variable and add it...
+    v = new ppdcVariable(name, value);
+    vars->add(v);
+  }
+
+  return (v);
+}
+
+
+//
+// 'ppdcSource::write_file()' - Write the current source data to a file.
+//
+
+int                                    // O - 0 on success, -1 on error
+ppdcSource::write_file(const char *f)  // I - File to write
+{
+  cups_file_t  *fp;                    // Output file
+  char         bckname[1024];          // Backup file
+  ppdcDriver   *d;                     // Current driver
+  ppdcString   *st;                    // Current string
+  ppdcAttr     *a;                     // Current attribute
+  ppdcConstraint *co;                  // Current constraint
+  ppdcFilter   *fi;                    // Current filter
+  ppdcFont     *fo;                    // Current font
+  ppdcGroup    *g;                     // Current group
+  ppdcOption   *o;                     // Current option
+  ppdcChoice   *ch;                    // Current choice
+  ppdcProfile  *p;                     // Current color profile
+  ppdcMediaSize        *si;                    // Current media size
+  float                left,                   // Current left margin
+               bottom,                 // Current bottom margin
+               right,                  // Current right margin
+               top;                    // Current top margin
+  int          dtused[PPDC_DRIVER_MAX];// Driver type usage...
+
+
+  // Rename the current file, if any, to .bck...
+  snprintf(bckname, sizeof(bckname), "%s.bck", f);
+  rename(f, bckname);
+
+  // Open the output file...
+  fp = cupsFileOpen(f, "w");
+
+  if (!fp)
+  {
+    // Can't create file; restore backup and return...
+    rename(bckname, f);
+    return (-1);
+  }
+
+  cupsFilePuts(fp, "// PPD Compiler of cups-filters " PACKAGE_VERSION "\n\n");
+
+  // Include standard files...
+  cupsFilePuts(fp, "// Include necessary files...\n");
+  cupsFilePuts(fp, "#include <font.defs>\n");
+  cupsFilePuts(fp, "#include <media.defs>\n");
+
+  memset(dtused, 0, sizeof(dtused));
+
+  for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next())
+    if (d->type > PPDC_DRIVER_PS && !dtused[d->type])
+    {
+      cupsFilePrintf(fp, "#include <%s.h>\n", driver_types[d->type]);
+      dtused[d->type] = 1;
+    }
+
+  // Output each driver...
+  for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next())
+  {
+    // Start the driver...
+    cupsFilePrintf(fp, "\n// %s %s\n", d->manufacturer->value,
+                   d->model_name->value);
+    cupsFilePuts(fp, "{\n");
+
+    // Write the copyright stings...
+    for (st = (ppdcString *)d->copyright->first();
+         st;
+        st = (ppdcString *)d->copyright->next())
+      quotef(fp, "  Copyright \"%s\"\n", st->value);
+
+    // Write other strings and values...
+    if (d->manufacturer && d->manufacturer->value)
+      quotef(fp, "  Manufacturer \"%s\"\n", d->manufacturer->value);
+    if (d->model_name->value)
+      quotef(fp, "  ModelName \"%s\"\n", d->model_name->value);
+    if (d->file_name && d->file_name->value)
+      quotef(fp, "  FileName \"%s\"\n", d->file_name->value);
+    if (d->pc_file_name && d->pc_file_name->value)
+      quotef(fp, "  PCFileName \"%s\"\n", d->pc_file_name->value);
+    if (d->version && d->version->value)
+      quotef(fp, "  Version \"%s\"\n", d->version->value);
+
+    cupsFilePrintf(fp, "  DriverType %s\n", driver_types[d->type]);
+
+    if (d->model_number)
+    {
+      switch (d->type)
+      {
+       case PPDC_DRIVER_LABEL :
+           cupsFilePuts(fp, "  ModelNumber ");
+
+           switch (d->model_number)
+           {
+             case DYMO_3x0 :
+                 cupsFilePuts(fp, "$DYMO_3x0\n");
+                 break;
+
+             case ZEBRA_EPL_LINE :
+                 cupsFilePuts(fp, "$ZEBRA_EPL_LINE\n");
+                 break;
+
+             case ZEBRA_EPL_PAGE :
+                 cupsFilePuts(fp, "$ZEBRA_EPL_PAGE\n");
+                 break;
+
+             case ZEBRA_ZPL :
+                 cupsFilePuts(fp, "$ZEBRA_ZPL\n");
+                 break;
+
+             case ZEBRA_CPCL :
+                 cupsFilePuts(fp, "$ZEBRA_CPCL\n");
+                 break;
+
+             case INTELLITECH_PCL :
+                 cupsFilePuts(fp, "$INTELLITECH_PCL\n");
+                 break;
+
+             default :
+                 cupsFilePrintf(fp, "%d\n", d->model_number);
+                 break;
+           }
+           break;
+
+       case PPDC_DRIVER_EPSON :
+           cupsFilePuts(fp, "  ModelNumber ");
+
+           switch (d->model_number)
+           {
+             case EPSON_9PIN :
+                 cupsFilePuts(fp, "$EPSON_9PIN\n");
+                 break;
+
+             case EPSON_24PIN :
+                 cupsFilePuts(fp, "$EPSON_24PIN\n");
+                 break;
+
+             case EPSON_COLOR :
+                 cupsFilePuts(fp, "$EPSON_COLOR\n");
+                 break;
+
+             case EPSON_PHOTO :
+                 cupsFilePuts(fp, "$EPSON_PHOTO\n");
+                 break;
+
+             case EPSON_ICOLOR :
+                 cupsFilePuts(fp, "$EPSON_ICOLOR\n");
+                 break;
+
+             case EPSON_IPHOTO :
+                 cupsFilePuts(fp, "$EPSON_IPHOTO\n");
+                 break;
+
+             default :
+                 cupsFilePrintf(fp, "%d\n", d->model_number);
+                 break;
+           }
+           break;
+
+       case PPDC_DRIVER_HP :
+           cupsFilePuts(fp, "  ModelNumber ");
+           switch (d->model_number)
+           {
+             case HP_LASERJET :
+                 cupsFilePuts(fp, "$HP_LASERJET\n");
+                 break;
+
+             case HP_DESKJET :
+                 cupsFilePuts(fp, "$HP_DESKJET\n");
+                 break;
+
+             case HP_DESKJET2 :
+                 cupsFilePuts(fp, "$HP_DESKJET2\n");
+                 break;
+
+             default :
+                 cupsFilePrintf(fp, "%d\n", d->model_number);
+                 break;
+           }
+
+           cupsFilePuts(fp, ")\n");
+           break;
+
+        default :
+            cupsFilePrintf(fp, "  ModelNumber %d\n", d->model_number);
+           break;
+      }
+    }
+
+    if (d->manual_copies)
+      cupsFilePuts(fp, "  ManualCopies Yes\n");
+
+    if (d->color_device)
+      cupsFilePuts(fp, "  ColorDevice Yes\n");
+
+    if (d->throughput)
+      cupsFilePrintf(fp, "  Throughput %d\n", d->throughput);
+
+    // Output all of the attributes...
+    for (a = (ppdcAttr *)d->attrs->first();
+         a;
+        a = (ppdcAttr *)d->attrs->next())
+      if (a->text->value && a->text->value[0])
+       quotef(fp, "  Attribute \"%s\" \"%s/%s\" \"%s\"\n",
+               a->name->value, a->selector->value ? a->selector->value : "",
+              a->text->value, a->value->value ? a->value->value : "");
+      else
+       quotef(fp, "  Attribute \"%s\" \"%s\" \"%s\"\n",
+               a->name->value, a->selector->value ? a->selector->value : "",
+              a->value->value ? a->value->value : "");
+
+    // Output all of the constraints...
+    for (co = (ppdcConstraint *)d->constraints->first();
+         co;
+        co = (ppdcConstraint *)d->constraints->next())
+    {
+      if (co->option1->value[0] == '*')
+       cupsFilePrintf(fp, "  UIConstraints \"%s %s", co->option1->value,
+                      co->choice1->value ? co->choice1->value : "");
+      else
+       cupsFilePrintf(fp, "  UIConstraints \"*%s %s", co->option1->value,
+                      co->choice1->value ? co->choice1->value : "");
+
+      if (co->option2->value[0] == '*')
+       cupsFilePrintf(fp, " %s %s\"\n", co->option2->value,
+                      co->choice2->value ? co->choice2->value : "");
+      else
+       cupsFilePrintf(fp, " *%s %s\"\n", co->option2->value,
+                      co->choice2->value ? co->choice2->value : "");
+    }
+
+    // Output all of the filters...
+    for (fi = (ppdcFilter *)d->filters->first();
+         fi;
+        fi = (ppdcFilter *)d->filters->next())
+      cupsFilePrintf(fp, "  Filter \"%s %d %s\"\n",
+                     fi->mime_type->value, fi->cost, fi->program->value);
+
+    // Output all of the fonts...
+    for (fo = (ppdcFont *)d->fonts->first();
+         fo;
+        fo = (ppdcFont *)d->fonts->next())
+      if (!strcmp(fo->name->value, "*"))
+        cupsFilePuts(fp, "  Font *\n");
+      else
+       cupsFilePrintf(fp, "  Font \"%s\" \"%s\" \"%s\" \"%s\" %s\n",
+                      fo->name->value, fo->encoding->value,
+                      fo->version->value, fo->charset->value,
+                      fo->status == PPDC_FONT_ROM ? "ROM" : "Disk");
+
+    // Output all options...
+    for (g = (ppdcGroup *)d->groups->first();
+         g;
+        g = (ppdcGroup *)d->groups->next())
+    {
+      if (g->options->count == 0)
+        continue;
+
+      if (g->text->value && g->text->value[0])
+        quotef(fp, "  Group \"%s/%s\"\n", g->name->value, g->text->value);
+      else
+        cupsFilePrintf(fp, "  Group \"%s\"\n", g->name->value);
+
+      for (o = (ppdcOption *)g->options->first();
+           o;
+          o = (ppdcOption *)g->options->next())
+      {
+        if (o->choices->count == 0)
+         continue;
+
+       if (o->text->value && o->text->value[0])
+          quotef(fp, "    Option \"%s/%s\"", o->name->value, o->text->value);
+       else
+          cupsFilePrintf(fp, "    Option \"%s\"", o->name->value);
+
+        cupsFilePrintf(fp, " %s %s %.1f\n",
+                      o->type == PPDC_BOOLEAN ? "Boolean" :
+                          o->type == PPDC_PICKONE ? "PickOne" : "PickMany",
+                      o->section == PPDC_SECTION_ANY ? "AnySetup" :
+                          o->section == PPDC_SECTION_DOCUMENT ? "DocumentSetup" :
+                          o->section == PPDC_SECTION_EXIT ? "ExitServer" :
+                          o->section == PPDC_SECTION_JCL ? "JCLSetup" :
+                          o->section == PPDC_SECTION_PAGE ? "PageSetup" :
+                          "Prolog",
+                      o->order);
+
+        for (ch = (ppdcChoice *)o->choices->first();
+            ch;
+            ch = (ppdcChoice *)o->choices->next())
+       {
+         if (ch->text->value && ch->text->value[0])
+            quotef(fp, "      %sChoice \"%s/%s\" \"%s\"\n",
+                  o->defchoice == ch->name ? "*" : "",
+                   ch->name->value, ch->text->value,
+                  ch->code->value ? ch->code->value : "");
+         else
+            quotef(fp, "      %sChoice \"%s\" \"%s\"\n",
+                  o->defchoice == ch->name ? "*" : "",
+                  ch->name->value,
+                  ch->code->value ? ch->code->value : "");
+       }
+      }
+    }
+
+    // Output all of the color profiles...
+    for (p = (ppdcProfile *)d->profiles->first();
+         p;
+        p = (ppdcProfile *)d->profiles->next())
+      cupsFilePrintf(fp, "  ColorProfile \"%s/%s\" %.3f %.3f "
+                        "%.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n",
+                    p->resolution->value, p->media_type->value,
+                    p->density, p->gamma,
+                    p->profile[0], p->profile[1], p->profile[2],
+                    p->profile[3], p->profile[4], p->profile[5],
+                    p->profile[6], p->profile[7], p->profile[8]);
+
+    // Output all of the media sizes...
+    left   = 0.0;
+    bottom = 0.0;
+    right  = 0.0;
+    top    = 0.0;
+
+    for (si = (ppdcMediaSize *)d->sizes->first();
+         si;
+        si = (ppdcMediaSize *)d->sizes->next())
+      if (si->size_code->value && si->region_code->value)
+      {
+        // Output a custom media size...
+       quotef(fp, "  %sCustomMedia \"%s/%s\" %.2f %.2f %.2f %.2f %.2f %.2f \"%s\" \"%s\"\n",
+              si->name == d->default_size ? "*" : "", si->name->value,
+              si->text->value, si->width, si->length, si->left, si->bottom,
+              si->right, si->top, si->size_code->value,
+              si->region_code->value);
+      }
+      else
+      {
+        // Output a standard media size...
+       if (fabs(left - si->left) > 0.1 ||
+            fabs(bottom - si->bottom) > 0.1 ||
+            fabs(right - si->right) > 0.1 ||
+            fabs(top - si->top) > 0.1)
+       {
+          cupsFilePrintf(fp, "  HWMargins %.2f %.2f %.2f %.2f\n",
+                        si->left, si->bottom, si->right, si->top);
+
+          left   = si->left;
+         bottom = si->bottom;
+         right  = si->right;
+         top    = si->top;
+       }
+
+       cupsFilePrintf(fp, "  %sMediaSize %s\n",
+                      si->name == d->default_size ? "*" : "",
+                      si->name->value);
+      }
+
+    if (d->variable_paper_size)
+    {
+      cupsFilePuts(fp, "  VariablePaperSize Yes\n");
+
+      if (fabs(left - d->left_margin) > 0.1 ||
+          fabs(bottom - d->bottom_margin) > 0.1 ||
+          fabs(right - d->right_margin) > 0.1 ||
+          fabs(top - d->top_margin) > 0.1)
+      {
+        cupsFilePrintf(fp, "  HWMargins %.2f %.2f %.2f %.2f\n",
+                      d->left_margin, d->bottom_margin, d->right_margin,
+                      d->top_margin);
+      }
+
+      cupsFilePrintf(fp, "  MinSize %.2f %.2f\n", d->min_width, d->min_length);
+      cupsFilePrintf(fp, "  MaxSize %.2f %.2f\n", d->max_width, d->max_length);
+    }
+
+    // End the driver...
+    cupsFilePuts(fp, "}\n");
+  }
+
+  // Close the file and return...
+  cupsFileClose(fp);
+
+  return (0);
+}
diff --git a/ppdc/ppdc-string.cxx b/ppdc/ppdc-string.cxx
new file mode 100644 (file)
index 0000000..b28294e
--- /dev/null
@@ -0,0 +1,48 @@
+//
+// Shared string class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2012 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcString::ppdcString()' - Create a shared string.
+//
+
+ppdcString::ppdcString(const char *v)  // I - String
+  : ppdcShared()
+{
+  PPDC_NEWVAL(v);
+
+  if (v)
+  {
+    size_t vlen = strlen(v);
+
+    value = new char[vlen + 1];
+    memcpy(value, v, vlen + 1);
+  }
+  else
+    value = 0;
+}
+
+
+//
+// 'ppdcString::~ppdcString()' - Destroy a shared string.
+//
+
+ppdcString::~ppdcString()
+{
+  PPDC_DELETEVAL(value);
+
+  if (value)
+    delete[] value;
+}
diff --git a/ppdc/ppdc-variable.cxx b/ppdc/ppdc-variable.cxx
new file mode 100644 (file)
index 0000000..f84deda
--- /dev/null
@@ -0,0 +1,54 @@
+//
+// Variable class for the CUPS PPD Compiler.
+//
+// Copyright 2007-2009 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+
+
+//
+// 'ppdcVariable::ppdcVariable()' - Create a variable.
+//
+
+ppdcVariable::ppdcVariable(const char *n,      // I - Name of variable
+                           const char *v)      // I - Value of variable
+  : ppdcShared()
+{
+  PPDC_NEW;
+
+  name  = new ppdcString(n);
+  value = new ppdcString(v);
+}
+
+
+//
+// 'ppdcVariable::~ppdcVariable()' - Destroy a variable.
+//
+
+ppdcVariable::~ppdcVariable()
+{
+  PPDC_DELETE;
+
+  name->release();
+  value->release();
+}
+
+
+//
+// 'ppdcVariable::set_value()' - Set the value of a variable.
+//
+
+void
+ppdcVariable::set_value(const char *v)
+{
+  value->release();
+  value = new ppdcString(v);
+}
diff --git a/ppdc/ppdc.h b/ppdc/ppdc.h
new file mode 100644 (file)
index 0000000..075cdcb
--- /dev/null
@@ -0,0 +1,523 @@
+//
+// Definitions for the CUPS PPD Compiler.
+//
+// Copyright 2007-2019 by Apple Inc.
+// Copyright 2002-2007 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+#ifndef _PPDC_H_
+#  define _PPDC_H_
+
+//
+// Include necessary headers...
+//
+
+#  include <cups/file.h>
+#  include <stdlib.h>
+
+
+//
+// Macros...
+//
+
+#  define PPDC_NAME(s) const char *class_name() { return (s); }
+
+
+//
+// Enumerations...
+//
+
+enum ppdcDrvType                       //// Driver type
+{
+  PPDC_DRIVER_CUSTOM,                  // Custom driver
+  PPDC_DRIVER_PS,                      // PostScript driver
+  PPDC_DRIVER_ESCP,                    // rastertoescpx driver
+  PPDC_DRIVER_PCL,                     // rastertopclx driver
+  PPDC_DRIVER_LABEL,                   // rastertolabel/rastertodymo driver
+  PPDC_DRIVER_EPSON,                   // rastertoepson driver
+  PPDC_DRIVER_HP,                      // rastertohp driver
+  PPDC_DRIVER_MAX                      // Number of driver types defined
+};
+
+enum ppdcFontStatus                    //// Load status of font
+{
+  PPDC_FONT_ROM,                       // Font is in ROM
+  PPDC_FONT_DISK                       // Font is on disk
+};
+
+enum ppdcOptSection                    //// Option section
+{
+  PPDC_SECTION_ANY,                    // AnySetup
+  PPDC_SECTION_DOCUMENT,               // DocumentSetup
+  PPDC_SECTION_EXIT,                   // ExitServer
+  PPDC_SECTION_JCL,                    // JCLSetup
+  PPDC_SECTION_PAGE,                   // PageSetup
+  PPDC_SECTION_PROLOG                  // Prolog
+};
+
+enum ppdcOptType                       //// Option type
+{
+  PPDC_BOOLEAN,                                // True/false option
+  PPDC_PICKONE,                                // Single choice from list
+  PPDC_PICKMANY                                // Multiple choices from list
+};
+
+enum ppdcLineEnding                    //// Line endings
+{
+  PPDC_LFONLY,                         // LF only
+  PPDC_CRONLY,                         // CR only
+  PPDC_CRLF                            // CR + LF
+};
+
+enum ppdcCondFlags                     //// Condition flags
+{
+  PPDC_COND_NORMAL = 0,                        // Normal state
+  PPDC_COND_SKIP = 1,                  // Skip state
+  PPDC_COND_SATISFIED = 2              // At least one condition satisfied
+};
+
+
+//
+// Printer description data...
+//
+
+class ppdcShared                       //// Shared Data Value
+{
+  private:
+
+  int          use;                    // Use count (delete when 0)
+
+  public:
+
+  ppdcShared();
+  virtual ~ppdcShared();
+
+  virtual const char *class_name() = 0;
+
+  void         retain();
+  void         release();
+};
+
+class ppdcArray                                //// Shared Array
+  : public ppdcShared
+{
+  public:
+
+  size_t       count,                  // Number of elements
+               alloc,                  // Allocated elements
+               current;                // Current element
+  ppdcShared   **data;                 // Elements
+
+  ppdcArray(ppdcArray *a = 0);
+  ~ppdcArray();
+
+  PPDC_NAME("ppdcArray")
+
+  void         add(ppdcShared *d);
+  ppdcShared   *first();
+  ppdcShared   *next();
+  void         remove(ppdcShared *d);
+};
+
+class ppdcString                       //// Shared String
+  : public ppdcShared
+{
+  public:
+
+  char         *value;                 // String value
+
+  ppdcString(const char *v);
+  ~ppdcString();
+
+  PPDC_NAME("ppdcString")
+};
+
+class ppdcInteger                      //// Shared integer
+  : public ppdcShared
+{
+  public:
+
+  int          *value;                 // Integer value
+
+  ppdcInteger(int *v) { value = v; }
+
+  PPDC_NAME("ppdcInteger")
+};
+
+class ppdcMessage                      //// Translation message
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *id,                    // Translation ID
+               *string;                // Translation string
+
+  ppdcMessage(const char *i, const char *s);
+  ~ppdcMessage();
+
+  PPDC_NAME("ppdcMessage")
+};
+
+class ppdcCatalog                      //// Translation catalog
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *locale;                // Name of locale
+  ppdcString   *filename;              // Name of translation file
+  ppdcArray    *messages;              // Array of translation messages
+
+  ppdcCatalog(const char *l, const char *f = 0);
+  ~ppdcCatalog();
+
+  PPDC_NAME("ppdcCatalog")
+
+  void         add_message(const char *id, const char *string = NULL);
+  const char   *find_message(const char *id);
+  int          load_messages(const char *f);
+  int          save_messages(const char *f);
+};
+
+class ppdcAttr                         //// Attribute
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *name,                  // Name of attribute
+               *selector,              // Selector string
+               *text,                  // Text string
+               *value;                 // Value string
+  bool         localizable;            // Should this attribute be localized?
+
+  ppdcAttr(const char *n, const char *s, const char *t, const char *v,
+           bool loc = false);
+  ~ppdcAttr();
+
+  PPDC_NAME("ppdcAttr")
+};
+
+class ppdcFont                         //// Shared Font
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *name,                  // Font name
+               *encoding,              // Font base encoding
+               *version,               // Font version
+               *charset;               // Font charset
+  ppdcFontStatus status;               // Font status (ROM or Disk)
+
+  ppdcFont(const char *n, const char *e, const char *v, const char *c,
+           ppdcFontStatus s);
+  ~ppdcFont();
+
+  PPDC_NAME("ppdcFont")
+};
+
+class ppdcChoice                       //// Option Choice
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *name,                  // Name of choice
+               *text,                  // Human-readable text of choice
+               *code;                  // PS code of choice
+
+  ppdcChoice(const char *n, const char *t, const char *c);
+  ~ppdcChoice();
+
+  PPDC_NAME("ppdcChoice")
+};
+
+class ppdcOption                       //// Option
+  : public ppdcShared
+{
+  public:
+
+  ppdcOptType  type;                   // Type of option
+  ppdcString   *name,                  // Name of option
+               *text;                  // Human-readable text of option
+  ppdcOptSection section;              // Section for option code
+  float                order;                  // Order number
+  ppdcArray    *choices;               // Choices
+  ppdcString   *defchoice;             // Default choice
+
+  ppdcOption(ppdcOptType ot, const char *n, const char *t, ppdcOptSection s,
+             float o);
+  ppdcOption(ppdcOption *o);
+  ~ppdcOption();
+
+  PPDC_NAME("ppdcOption")
+
+  void         add_choice(ppdcChoice *c) { choices->add(c); }
+  ppdcChoice   *find_choice(const char *n);
+  void         set_defchoice(ppdcChoice *c);
+};
+
+class ppdcGroup                        //// Group of Options
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *name,                  // Name of option
+               *text;                  // Human-readable text of option
+  ppdcArray    *options;               // Options
+
+  ppdcGroup(const char *n, const char *t);
+  ppdcGroup(ppdcGroup *g);
+  ~ppdcGroup();
+
+  PPDC_NAME("ppdcGroup")
+
+  void         add_option(ppdcOption *o) { options->add(o); }
+  ppdcOption   *find_option(const char *n);
+};
+
+class ppdcConstraint                   //// Constraint
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *option1,               // First option
+               *choice1,               // First choice
+               *option2,               // Second option
+               *choice2;               // Second choice
+
+  ppdcConstraint(const char *o1, const char *c1, const char *o2,
+                const char *c2);
+  ~ppdcConstraint();
+
+  PPDC_NAME("ppdcConstraint")
+};
+
+class ppdcFilter                       //// Filter Program
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *mime_type,             // MIME type
+               *program;               // Filter program
+  int          cost;                   // Relative cost of filter
+
+  ppdcFilter(const char *t, const char *p, int c);
+  ~ppdcFilter();
+
+  PPDC_NAME("ppdcFilter")
+};
+
+class ppdcMediaSize                    //// Media Size
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *name,                  // Name of size
+               *text;                  // Human-readable text
+  float                width,                  // Width in points
+               length,                 // Length in points
+               left,                   // Left limit in points
+               bottom,                 // Bottom limit in points
+               right,                  // Right limit in points
+               top;                    // Top limit in points
+  ppdcString   *size_code,             // PageSize code, if any
+               *region_code;           // PageRegion code, if any
+
+  ppdcMediaSize(const char *n, const char *t, float w, float l,
+                float lm, float bm, float rm, float tm,
+               const char *sc = 0, const char *rc = 0);
+  ~ppdcMediaSize();
+
+  PPDC_NAME("ppdcMediaSize")
+};
+
+class ppdcProfile                      //// Color Profile
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *resolution,            // Resolution name
+               *media_type;            // Media type name
+  float                density,                // Color profile density
+               gamma,                  // Color profile gamma
+               profile[9];             // Color profile matrix
+
+  ppdcProfile(const char *r, const char *m, float d, float g, const float *p);
+  ~ppdcProfile();
+
+  PPDC_NAME("ppdcProfile")
+};
+
+class ppdcSource;
+
+class ppdcDriver                       //// Printer Driver Data
+  : public ppdcShared
+{
+  public:
+
+  ppdcDrvType  type;                   // Driver type
+  ppdcArray    *copyright;             // Copyright strings
+  ppdcString   *manufacturer,          // Manufacturer
+               *model_name,            // Name of printer model
+               *file_name,             // Output filename for PPD
+               *pc_file_name,          // 8 character PC filename for PPD
+               *version;               // Version number
+  int          model_number,           // Model number for driver
+               manual_copies,          // Do manual copies?
+               color_device,           // Support color?
+               throughput;             // Throughput in pages per minute
+  ppdcArray    *attrs,                 // Attributes
+               *constraints,           // Constraints
+               *filters,               // Filters
+               *fonts,                 // Fonts
+               *groups,                // Option groups
+               *profiles,              // Color profiles
+               *sizes;                 // Fixed sizes
+  ppdcString   *default_font,          // Default font
+               *default_size;          // Default size option
+  int          variable_paper_size;    // Support variable sizes?
+  ppdcString   *custom_size_code;      // Custom page size code, if any
+  float                left_margin,            // Margins for device in points
+               bottom_margin,
+               right_margin,
+               top_margin,
+               max_width,              // Maximum width (points)
+               max_length,             // Maximum length (points)
+               min_width,              // Minimum width (points)
+               min_length;             // Minimum length (points)
+
+  ppdcDriver(ppdcDriver *d = 0);
+  ~ppdcDriver();
+
+  PPDC_NAME("ppdcDriver")
+
+  void         add_attr(ppdcAttr *a) { attrs->add(a); }
+  void         add_constraint(ppdcConstraint *c) { constraints->add(c); }
+  void         add_copyright(const char *c) {
+                 copyright->add(new ppdcString(c));
+               }
+  void         add_filter(ppdcFilter *f) { filters->add(f); }
+  void         add_font(ppdcFont *f) { fonts->add(f); }
+  void         add_group(ppdcGroup *g) { groups->add(g); }
+  void         add_profile(ppdcProfile *p) { profiles->add(p); }
+  void         add_size(ppdcMediaSize *m) { sizes->add(m); }
+
+  ppdcAttr     *find_attr(const char *k, const char *s);
+  ppdcGroup    *find_group(const char *n);
+  ppdcOption   *find_option(const char *n);
+  ppdcOption   *find_option_group(const char *n, ppdcGroup **mg);
+
+  void         set_custom_size_code(const char *c);
+  void         set_default_font(ppdcFont *f);
+  void         set_default_size(ppdcMediaSize *m);
+  void         set_file_name(const char *f);
+  void         set_manufacturer(const char *m);
+  void         set_model_name(const char *m);
+  void         set_pc_file_name(const char *f);
+  void         set_version(const char *v);
+
+  int          write_ppd_file(cups_file_t *fp, ppdcCatalog *catalog,
+                              ppdcArray *locales, ppdcSource *src,
+                              ppdcLineEnding le);
+};
+
+class ppdcVariable                     //// Variable Definition
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *name,                  // Name of variable
+               *value;                 // Value of variable
+
+  ppdcVariable(const char *n, const char *v);
+  ~ppdcVariable();
+
+  PPDC_NAME("ppdcVariable")
+
+  void         set_value(const char *v);
+};
+
+class ppdcFile                         //// File
+{
+  public:
+
+  bool         close_on_delete;        // Close file on delete?
+  cups_file_t  *fp;                    // File pointer
+  const char   *filename;              // Filename
+  int          line;                   // Line in file
+
+  ppdcFile(const char *f, cups_file_t *ffp = (cups_file_t *)0);
+  ~ppdcFile();
+
+  int          get();
+  int          peek();
+};
+
+class ppdcSource                       //// Source File
+  : public ppdcShared
+{
+  public:
+
+  static ppdcArray *includes;          // Include directories
+  static const char *driver_types[];   // Driver types
+
+  ppdcString   *filename;              // Filename
+  ppdcArray    *base_fonts,            // Base fonts
+               *drivers,               // Printer drivers
+               *po_files,              // Message catalogs
+               *sizes,                 // Predefined media sizes
+               *vars;                  // Defined variables
+  int          cond_state,             // Cummulative conditional state
+               *cond_current,          // Current #if state
+               cond_stack[101];        // #if state stack
+
+
+  ppdcSource(const char *f = 0, cups_file_t *ffp = (cups_file_t *)0);
+  ~ppdcSource();
+
+  PPDC_NAME("ppdcSource")
+
+  static void  add_include(const char *d);
+  ppdcDriver   *find_driver(const char *f);
+  static char  *find_include(const char *f, const char *base, char *n,
+                             int nlen);
+  ppdcCatalog  *find_po(const char *l);
+  ppdcMediaSize        *find_size(const char *s);
+  ppdcVariable *find_variable(const char *n);
+  ppdcAttr     *get_attr(ppdcFile *fp, bool loc = false);
+  int          get_boolean(ppdcFile *fp);
+  ppdcChoice   *get_choice(ppdcFile *fp);
+  ppdcChoice   *get_color_model(ppdcFile *fp);
+  int          get_color_order(const char *co);
+  ppdcProfile  *get_color_profile(ppdcFile *fp);
+  int          get_color_space(const char *cs);
+  ppdcConstraint *get_constraint(ppdcFile *fp);
+  ppdcMediaSize        *get_custom_size(ppdcFile *fp);
+  void         get_duplex(ppdcFile *fp, ppdcDriver *d);
+  ppdcFilter   *get_filter(ppdcFile *fp);
+  float                get_float(ppdcFile *fp);
+  ppdcFont     *get_font(ppdcFile *fp);
+  ppdcChoice   *get_generic(ppdcFile *fp, const char *keyword,
+                            const char *tattr, const char *nattr);
+  ppdcGroup    *get_group(ppdcFile *fp, ppdcDriver *d);
+  ppdcOption   *get_installable(ppdcFile *fp);
+  int          get_integer(const char *v);
+  int          get_integer(ppdcFile *fp);
+  float                get_measurement(ppdcFile *fp);
+  ppdcOption   *get_option(ppdcFile *fp, ppdcDriver *d, ppdcGroup *g);
+  ppdcCatalog  *get_po(ppdcFile *fp);
+  ppdcChoice   *get_resolution(ppdcFile *fp);
+  ppdcProfile  *get_simple_profile(ppdcFile *fp);
+  ppdcMediaSize        *get_size(ppdcFile *fp);
+  char         *get_token(ppdcFile *fp, char *buffer, int buflen);
+  ppdcVariable *get_variable(ppdcFile *fp);
+  int          import_ppd(const char *f);
+  int          quotef(cups_file_t *fp, const char *format, ...);
+  void         read_file(const char *f, cups_file_t *ffp = (cups_file_t *)0);
+  void         scan_file(ppdcFile *fp, ppdcDriver *td = 0, bool inc = false);
+  ppdcVariable *set_variable(const char *name, const char *value);
+  int          write_file(const char *f);
+};
+
+
+#endif // !_PPDC_H_
diff --git a/ppdc/raster.defs b/ppdc/raster.defs
new file mode 100644 (file)
index 0000000..278aac0
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file contains the standard definitions for enumerated attributes
+ * in the CUPS raster page device dictionary.
+ *
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2005 by Easy Software Products.
+ *
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
+ */
+
+/* Jog values */
+#define CUPS_JOG_NONE          0       /* Never move pages */
+#define CUPS_JOG_FILE          1       /* Move pages after this file */
+#define CUPS_JOG_JOB           2       /* Move pages after this job */
+#define CUPS_JOG_SET           3       /* Move pages after this set */
+
+/* Orientation values */
+#define CUPS_ORIENT_0          0       /* Don't rotate the page */
+#define CUPS_ORIENT_90         1       /* Rotate the page counter-clockwise */
+#define CUPS_ORIENT_180                2       /* Turn the page upside down */
+#define CUPS_ORIENT_270                3       /* Rotate the page clockwise */
+
+/* CutMedia values */
+#define CUPS_CUT_NONE          0       /* Never cut the roll */
+#define CUPS_CUT_FILE          1       /* Cut the roll after this file */
+#define CUPS_CUT_JOB           2       /* Cut the roll after this job */
+#define CUPS_CUT_SET           3       /* Cut the roll after this set */
+#define CUPS_CUT_PAGE          4       /* Cut the roll after this page */
+
+/* AdvanceMedia values */
+#define CUPS_ADVANCE_NONE      0       /* Never advance the roll */
+#define CUPS_ADVANCE_FILE      1       /* Advance the roll after this file */
+#define CUPS_ADVANCE_JOB       2       /* Advance the roll after this job */
+#define CUPS_ADVANCE_SET       3       /* Advance the roll after this set */
+#define CUPS_ADVANCE_PAGE      4       /* Advance the roll after this page */
+
+/* LeadingEdge values */
+#define CUPS_EDGE_TOP          0       /* Leading edge is the top of the page */
+#define CUPS_EDGE_RIGHT                1       /* Leading edge is the right of the page */
+#define CUPS_EDGE_BOTTOM       2       /* Leading edge is the bottom of the page */
+#define CUPS_EDGE_LEFT         3       /* Leading edge is the left of the page */
+
+/* cupsColorOrder values */
+#define CUPS_ORDER_CHUNKED     0       /* CMYK CMYK CMYK ... */
+#define CUPS_ORDER_BANDED      1       /* CCC MMM YYY KKK ... */
+#define CUPS_ORDER_PLANAR      2       /* CCC ... MMM ... YYY ... KKK ... */
+
+/* cupsColorSpace values */
+#define CUPS_CSPACE_W          0       /* Luminance */
+#define CUPS_CSPACE_RGB                1       /* Red, green, blue */
+#define CUPS_CSPACE_RGBA       2       /* Red, green, blue, alpha */
+#define CUPS_CSPACE_K          3       /* Black */
+#define CUPS_CSPACE_CMY                4       /* Cyan, magenta, yellow */
+#define CUPS_CSPACE_YMC                5       /* Yellow, magenta, cyan */
+#define CUPS_CSPACE_CMYK       6       /* Cyan, magenta, yellow, black */
+#define CUPS_CSPACE_YMCK       7       /* Yellow, magenta, cyan, black */
+#define CUPS_CSPACE_KCMY       8       /* Black, cyan, magenta, yellow */
+#define CUPS_CSPACE_KCMYcm     9       /* Black, cyan, magenta, yellow, *
+                                        * light-cyan, light-magenta     */
+#define CUPS_CSPACE_GMCK       10      /* Gold, magenta, yellow, black */
+#define CUPS_CSPACE_GMCS       11      /* Gold, magenta, yellow, silver */
+#define CUPS_CSPACE_WHITE      12      /* White ink (as black) */
+#define CUPS_CSPACE_GOLD       13      /* Gold foil */
+#define CUPS_CSPACE_SILVER     14      /* Silver foil */
+
+#define CUPS_CSPACE_CIEXYZ     15      /* CIE XYZ */
+#define CUPS_CSPACE_CIELab     16      /* CIE Lab */
+
+#define CUPS_CSPACE_ICC1       32      /* ICC-based, 1 color */
+#define CUPS_CSPACE_ICC2       33      /* ICC-based, 2 colors */
+#define CUPS_CSPACE_ICC3       34      /* ICC-based, 3 colors */
+#define CUPS_CSPACE_ICC4       35      /* ICC-based, 4 colors */
+#define CUPS_CSPACE_ICC5       36      /* ICC-based, 5 colors */
+#define CUPS_CSPACE_ICC6       37      /* ICC-based, 6 colors */
+#define CUPS_CSPACE_ICC7       38      /* ICC-based, 7 colors */
+#define CUPS_CSPACE_ICC8       39      /* ICC-based, 8 colors */
+#define CUPS_CSPACE_ICC9       40      /* ICC-based, 9 colors */
+#define CUPS_CSPACE_ICCA       41      /* ICC-based, 10 colors */
+#define CUPS_CSPACE_ICCB       42      /* ICC-based, 11 colors */
+#define CUPS_CSPACE_ICCC       43      /* ICC-based, 12 colors */
+#define CUPS_CSPACE_ICCD       44      /* ICC-based, 13 colors */
+#define CUPS_CSPACE_ICCE       45      /* ICC-based, 14 colors */
+#define CUPS_CSPACE_ICCF       46      /* ICC-based, 15 colors */