]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Start adding PPD compiler to CUPS trunk (not finished!)
authormike <mike@7a7537e8-13f0-0310-91df-b6672ffda945>
Wed, 13 Feb 2008 17:31:20 +0000 (17:31 +0000)
committermike <mike@7a7537e8-13f0-0310-91df-b6672ffda945>
Wed, 13 Feb 2008 17:31:20 +0000 (17:31 +0000)
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@7303 7a7537e8-13f0-0310-91df-b6672ffda945

34 files changed:
Makedefs.in
Makefile
config-scripts/cups-common.m4
config-scripts/cups-compiler.m4
ppdc/Dependencies [new file with mode: 0644]
ppdc/Makefile [new file with mode: 0644]
ppdc/drv.cxx [new file with mode: 0644]
ppdc/foo-fr.po [new file with mode: 0644]
ppdc/foo.drv [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-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.cxx [new file with mode: 0644]
ppdc/ppdc.h [new file with mode: 0644]
ppdc/ppdhtml.cxx [new file with mode: 0644]
ppdc/ppdi.cxx [new file with mode: 0644]
ppdc/ppdmerge.cxx [new file with mode: 0644]
ppdc/ppdpo.cxx [new file with mode: 0644]

index 09ce76f3797d6ef98f8cff2e8ba41f418a8ebe00..c9717f80289050b6d4dacb03f68b8b362a76095e 100644 (file)
@@ -20,6 +20,7 @@
 AR             =       @AR@
 AWK            =       @AWK@
 CC             =       @LIBTOOL@ @CC@
+CXX            =       @LIBTOOL@ @CXX@
 DSO            =       @DSO@
 HTMLDOC                =       @HTMLDOC@
 INSTALL                =       @INSTALL@
@@ -109,12 +110,16 @@ INSTALLSTATIC     =       @INSTALLSTATIC@
 
 ALL_CFLAGS     =       -I.. -D_CUPS_SOURCE $(CFLAGS) $(SSLFLAGS) \
                        @LARGEFILE@ @PTHREAD_FLAGS@ $(OPTIONS)
+ALL_CXXFLAGS   =       -I.. -D_CUPS_SOURCE $(CXXFLAGS) $(SSLFLAGS) \
+                       @LARGEFILE@ @PTHREAD_FLAGS@ $(OPTIONS)
 ARCHFLAGS      =       @ARCHFLAGS@
 ARFLAGS                =       @ARFLAGS@
 BACKLIBS       =       @BACKLIBS@
 CFLAGS         =       @CPPFLAGS@ @CFLAGS@
 COMMONLIBS     =       @LIBS@
 CUPSDLIBS      =       @CUPSDLIBS@
+CXXFLAGS       =       @CPPFLAGS@ @CXXFLAGS@
+CXXLIBS                =       @CXXLIBS@
 DSOFLAGS       =       @DSOFLAGS@
 DSOLIBS                =       @DSOLIBS@ $(COMMONLIBS)
 DNSSDLIBS      =       @DNSSDLIBS@
@@ -237,7 +242,7 @@ DBUSDIR             =       @DBUSDIR@
 #
 
 .SILENT:
-.SUFFIXES:     .1 .1.gz .1m .1m.gz .5 .5.gz .7 .7.gz .8 .8.gz .a .c .h .man .o .32.o .64.o .gz
+.SUFFIXES:     .1 .1.gz .1m .1m.gz .3 .3.gz .5 .5.gz .7 .7.gz .8 .8.gz .a .c .cxx .h .man .o .32.o .64.o .gz
 
 .c.o:
        echo Compiling $<...
@@ -251,12 +256,16 @@ DBUSDIR           =       @DBUSDIR@
        echo Compiling 64-bit $<...
        $(CC) $(ARCH64FLAGS) $(OPTIM) $(ALL_CFLAGS) -c -o $@ $<
 
-.man.1 .man.1m .man.5 .man.7 .man.8:
+.cxx.o:
+       echo Compiling $<...
+       $(CXX) $(ARCHFLAGS) $(OPTIM) $(ALL_CXXFLAGS) -c $<
+
+.man.1 .man.1m .man.3 .man.5 .man.7 .man.8:
        echo Linking $<...
        $(RM) $@
        $(LN) $< $@
 
-.man.1.gz .man.1m.gz .man.5.gz .man.7.gz .man.8.gz .man.gz:
+.man.1.gz .man.1m.gz .man.3.gz .man.5.gz .man.7.gz .man.8.gz .man.gz:
        echo -n Compressing $<...
        $(RM) $@
        gzip -v9 <$< >$@
index cd5b9516ffc87daad03c75e4eaeab44cf5862d28..72e2ac50d922786db05bbea557ccab82b8ab3809 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ include Makedefs
 #
 
 DIRS   =       cups backend berkeley cgi-bin filter locale man monitor \
-               notifier scheduler systemv test \
+               notifier ppdc scheduler systemv test \
                $(PHPDIR) \
                conf data doc $(FONTS) ppd templates
 
index 369eba217326e24f935f2433bc030da710c070d0..e83c2f23fd83570ee698f2fcb315e7392ef73ab4 100644 (file)
@@ -34,12 +34,14 @@ AC_DEFINE_UNQUOTED(CUPS_MINIMAL, "CUPS/$CUPS_VERSION$CUPS_REVISION")
 dnl Default compiler flags...
 CFLAGS="${CFLAGS:=}"
 CPPFLAGS="${CPPFLAGS:=}"
+CXXFLAGS="${CXXFLAGS:=}"
 LDFLAGS="${LDFLAGS:=}"
 
 dnl Checks for programs...
 AC_PROG_AWK
 AC_PROG_CC
 AC_PROG_CPP
+AC_PROG_CXX
 AC_PROG_INSTALL
 if test "$INSTALL" = "$ac_install_sh"; then
        # Use full path to install-sh script...
index e47d681ed61c92aa35fe77419c475bb48fc9ec34..ea89e7679fefeec184738663108ca38ba763d9a9 100644 (file)
@@ -60,6 +60,9 @@ dnl Read-only data/program support on Linux...
 AC_ARG_ENABLE(relro, [  --enable-relro          use GCC relro option, default=no])
 
 dnl Update compiler options...
+CXXLIBS="${CXXLIBS:=}"
+AC_SUBST(CXXLIBS)
+
 PIEFLAGS=""
 AC_SUBST(PIEFLAGS)
 
diff --git a/ppdc/Dependencies b/ppdc/Dependencies
new file mode 100644 (file)
index 0000000..4122e56
--- /dev/null
@@ -0,0 +1,26 @@
+# DO NOT DELETE
+
+ppdc-array.o: ppdc.h ../cups/string.h ../config.h
+ppdc-attr.o: ppdc.h ../cups/string.h ../config.h
+ppdc-catalog.o: ppdc.h ../cups/string.h ../config.h
+ppdc-choice.o: ppdc.h ../cups/string.h ../config.h
+ppdc-constraint.o: ppdc.h ../cups/string.h ../config.h
+ppdc.o: ppdc.h ../cups/string.h ../config.h
+ppdc-driver.o: ppdc.h ../cups/string.h ../config.h
+ppdc-file.o: ppdc.h ../cups/string.h ../config.h
+ppdc-filter.o: ppdc.h ../cups/string.h ../config.h
+ppdc-font.o: ppdc.h ../cups/string.h ../config.h
+ppdc-group.o: ppdc.h ../cups/string.h ../config.h
+ppdc-import.o: ppdc.h ../cups/string.h ../config.h
+ppdc-mediasize.o: ppdc.h ../cups/string.h ../config.h
+ppdc-message.o: ppdc.h ../cups/string.h ../config.h
+ppdc-option.o: ppdc.h ../cups/string.h ../config.h
+ppdc-profile.o: ppdc.h ../cups/string.h ../config.h
+ppdc-shared.o: ppdc.h ../cups/string.h ../config.h
+ppdc-source.o: ppdc.h ../cups/string.h ../config.h ../data/epson.h
+ppdc-source.o: ../data/escp.h ../data/hp.h ../data/label.h ../data/pcl.h
+ppdc-string.o: ppdc.h ../cups/string.h ../config.h
+ppdc-variable.o: ppdc.h ../cups/string.h ../config.h
+ppdhtml.o: ppdc.h ../cups/string.h ../config.h
+ppdi.o: ppdc.h ../cups/string.h ../config.h
+ppdpo.o: ppdc.h ../cups/string.h ../config.h
diff --git a/ppdc/Makefile b/ppdc/Makefile
new file mode 100644 (file)
index 0000000..6975541
--- /dev/null
@@ -0,0 +1,215 @@
+#
+# "$Id$"
+#
+#   Makefile for the CUPS PPD Compiler.
+#
+#   Copyright 2007 by Apple Inc.
+#   Copyright 2002-2006 by Easy Software Products.
+#
+#   These coded instructions, statements, and computer programs are the
+#   property of Apple Inc. and are protected by Federal copyright
+#   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+#   which should have been included with this file.  If this file is
+#   file is missing or damaged, see the license at "http://www.cups.org/".
+#
+
+#
+# Include standard definitions...
+#
+
+include ../Makedefs
+
+
+#
+# Object files...
+#
+
+PPDCOBJS       = \
+               ppdc-array.o \
+               ppdc-attr.o \
+               ppdc-catalog.o \
+               ppdc-choice.o \
+               ppdc-constraint.o \
+               ppdc-driver.o \
+               ppdc-file.o \
+               ppdc-filter.o \
+               ppdc-font.o \
+               ppdc-group.o \
+               ppdc-import.o \
+               ppdc-mediasize.o \
+               ppdc-message.o \
+               ppdc-option.o \
+               ppdc-profile.o \
+               ppdc-shared.o \
+               ppdc-source.o \
+               ppdc-string.o \
+               ppdc-variable.o
+
+OBJS           = \
+               drv.o \
+               ppdc.o \
+               ppdhtml.o \
+               ppdi.o \
+               ppdmerge.o \
+               ppdpo.o \
+               $(PPDCOBJS)
+
+TARGETS                = \
+               libppdc.a \
+               drv \
+               ppdc-static \
+               ppdc \
+               ppdhtml \
+               ppdi \
+               ppdmerge \
+               ppdpo
+
+
+#
+# Make everything...
+#
+
+all:           $(TARGETS)
+
+
+#
+# Clean everything...
+#
+
+clean:
+       $(RM) $(OBJS) core
+       $(RM) *.bak *.bck core.*
+       $(RM) $(TARGETS)
+       $(RM) -r ppd
+       $(RM) test.drv
+
+
+#
+# Update dependencies...
+#
+
+depend:
+       makedepend -Y -I.. -f Dependencies *.cxx
+
+
+#
+# Install...
+#
+
+install:
+       echo Installing PPD compiler programs...
+       $(INSTALL_DIR) $(BUILDROOT)$(bindir)
+       $(INSTALL_BIN) ppdc $(BUILDROOT)$(bindir)
+       $(INSTALL_BIN) ppdhtml $(BUILDROOT)$(bindir)
+       $(INSTALL_BIN) ppdi $(BUILDROOT)$(bindir)
+       $(INSTALL_BIN) ppdmerge $(BUILDROOT)$(bindir)
+       $(INSTALL_BIN) ppdpo $(BUILDROOT)$(bindir)
+       $(INSTALL_DIR) $(BUILDROOT)$(CUPS_SERVERBIN)/driver
+       $(INSTALL_BIN) drv $(BUILDROOT)$(CUPS_SERVERBIN)/driver
+       $(INSTALL_DIR) $(BUILDROOT)$(CUPS_DATADIR)/drv
+
+
+#
+# Uninstall...
+#
+
+uninstall:
+       $(RM) $(BUILDROOT)$(bindir)/ppdc
+       $(RM) $(BUILDROOT)$(bindir)/ppdhtml
+       $(RM) $(BUILDROOT)$(bindir)/ppdi
+       $(RM) $(BUILDROOT)$(bindir)/ppdmerge
+       $(RM) $(BUILDROOT)$(bindir)/ppdpo
+       $(RM) $(BUILDROOT)$(CUPS_SERVERBIN)/driver/drv
+
+
+#
+# drv, the CUPS driver interface program.
+#
+
+drv:                   drv.o libppdc.a ../cups/libcupsdriver.a
+       echo Linking $@...
+       $(CXX) $(LDFLAGS) -o $@ drv.o libppdc.a \
+               ../cups/libcupsdriver.a $(CUPSLIBS) $(LIBS)
+
+
+#
+# ppdc, the PPD compiler.
+#
+
+ppdc:                  ppdc.o libppdc.a ../cups/libcupsdriver.a
+       echo Linking $@...
+       $(CXX) $(LDFLAGS) -o $@ ppdc.o libppdc.a \
+               ../cups/libcupsdriver.a $(CUPSLIBS) $(LIBS)
+
+
+ppdc-static:           ppdc.o libppdc.a ../cups/libcupsdriver.a \
+                       foo.drv foo-fr.po
+       echo Linking $@...
+       $(CXX) $(LDFLAGS) -o ppdc-static ppdc.o libppdc.a \
+               ../cups/libcupsdriver.a $(CUPSLIBS_ST) $(LIBS)
+       echo Testing PPD compiler...
+       ./ppdc-static -l en,fr -I ../data foo.drv
+       ./ppdc-static -l en,fr -z -I ../data foo.drv
+
+
+#
+# ppdhtml, the PPD to HTML utility.
+#
+
+ppdhtml:                       ppdhtml.o libppdc.a ../cups/libcupsdriver.a
+       echo Linking $@...
+       $(CXX) $(LDFLAGS) -o $@ ppdhtml.o libppdc.a \
+               ../cups/libcupsdriver.a $(CUPSLIBS) $(LIBS)
+
+
+#
+# ppdi, import PPD files.
+#
+
+ppdi:                  ppdi.o libppdc.a ../cups/libcupsdriver.a
+       echo Linking $@...
+       $(CXX) $(LDFLAGS) -o $@ ppdi.o libppdc.a \
+               ../cups/libcupsdriver.a $(CUPSLIBS) $(LIBS)
+
+
+#
+# ppdmerge, merge PPD files.
+#
+
+ppdmerge:                      ppdmerge.o ../cups/libcupsdriver.a
+       echo Linking $@...
+       $(CXX) $(LDFLAGS) -o $@ ppdmerge.o \
+               ../cups/libcupsdriver.a $(CUPSLIBS) $(LIBS)
+
+
+#
+# ppdpo, create message catalog files.
+#
+
+ppdpo:                 ppdpo.o libppdc.a ../cups/libcupsdriver.a
+       echo Linking $@...
+       $(CXX) $(LDFLAGS) -o $@ ppdpo.o libppdc.a \
+               ../cups/libcupsdriver.a $(CUPSLIBS) $(LIBS)
+
+
+#
+# libppdc.a, the PPD compiler library...
+#
+
+libppdc.a:             $(PPDCOBJS)
+       echo Creating $@...
+       $(RM) $@
+       $(AR) $(ARFLAGS) $@ $(PPDCOBJS)
+       $(RANLIB) $@
+
+
+#
+# Include dependencies...
+#
+
+include Dependencies
+
+
+#
+# End of "$Id$".
+#
diff --git a/ppdc/drv.cxx b/ppdc/drv.cxx
new file mode 100644 (file)
index 0000000..23b9ece
--- /dev/null
@@ -0,0 +1,427 @@
+//
+// "$Id$"
+//
+//   DDK driver interface main entry for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2006 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   main()      - Enumerate or display PPD files.
+//   cat_ppd()   - Display a PPD file.
+//   list_ppds() - List PPDs.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <cups/cups.h>
+#include <cups/dir.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+//
+// Local functions...
+//
+
+static int     cat_ppd(ppdcSource *src, const char *name);
+static int     list_drvs(const char *pathname, const char *prefix);
+static int     list_ppds(ppdcSource *src, const char *name);
+
+
+//
+// 'main()' - Enumerate or display PPD files.
+//
+
+int                                    // O - Exit status
+main(int  argc,                                // I - Number of command-line arguments
+     char *argv[])                     // I - Command-line arguments
+{
+  const char   *datadir;               // CUPS_DATADIR
+  ppdcSource   *src;                   // PPD source file data
+  char         filename[1024],         // Full path to .drv file(s)
+               scheme[32],             // URI scheme ("drv")
+               userpass[256],          // User/password info (unused)
+               host[2],                // Hostname (unused)
+               resource[1024],         // Resource path (/dir/to/filename.drv)
+               *pc_file_name;          // Filename portion of URI
+  int          port,                   // Port number (unused)
+               status;                 // Exit status
+
+
+  // Determine where CUPS has installed the data files...
+  if ((datadir = getenv("CUPS_DATADIR")) == NULL)
+    datadir = CUPS_DATADIR;
+
+  // List all available PPDs or cat a single PPD...
+  if (argc == 2 && !strcmp(argv[1], "list"))
+  {
+    snprintf(filename, sizeof(filename), "%s/drv", datadir);
+    return (list_drvs(filename, "/"));
+  }
+  else if (argc == 3 && !strcmp(argv[1], "cat"))
+  {
+    httpSeparateURI(HTTP_URI_CODING_ALL, argv[2], scheme, sizeof(scheme),
+                    userpass, sizeof(userpass), host, sizeof(host), &port,
+                   resource, sizeof(resource));
+
+    if (strstr(resource, "../") ||
+        (pc_file_name = strrchr(resource, '/')) == NULL ||
+       pc_file_name == resource)
+    {
+      fprintf(stderr, "ERROR: Bad driver info URI \"%s\"!\n", argv[2]);
+      return (1);
+    }
+
+    *pc_file_name++ = '\0';
+
+    snprintf(filename, sizeof(filename), "%s/drv%s", datadir, resource);
+
+    src = new ppdcSource(filename);
+
+    status = cat_ppd(src, pc_file_name);
+
+    delete src;
+
+    return (status);
+  }
+  
+  fprintf(stderr, "ERROR: Usage: %s cat URI\n", argv[0]);
+  fprintf(stderr, "ERROR: Usage: %s list\n", argv[0]);
+
+  return (1);
+}
+
+
+//
+// 'cat_ppd()' - Display a PPD file.
+//
+
+static int                             // O - Exit status
+cat_ppd(ppdcSource *src,               // I - Driver info file
+        const char *name)              // I - PC filename
+{
+  ppdcDriver   *d;                     // Current driver
+  cups_file_t  *out;                   // Stdout via CUPS file API 
+  
+  for (d = (ppdcDriver *)src->drivers->first();
+       d;
+       d = (ppdcDriver *)src->drivers->next())
+    if (!strcmp(name, d->pc_file_name->value))
+    {
+      out = cupsFileStdout();
+
+      d->write_ppd_file(out, NULL, NULL, src, PPDC_LFONLY);
+      cupsFileClose(out);
+      return (0);
+    }
+  
+  return (1);
+}
+
+
+//
+// 'list_drvs()' - List all drv files in the given path...
+//
+
+static int                             // O - Exit status
+list_drvs(const char *pathname,                // I - Full path to directory
+          const char *prefix)          // I - Prefix for directory
+{
+  char         *ext,                   // Extension on file
+               filename[1024],         // Full path to .drv file(s)
+               newprefix[1024];        // New prefix for directory
+  cups_dir_t   *dir;                   // Current directory
+  cups_dentry_t        *dent;                  // Current directory entry
+
+
+  if ((dir = cupsDirOpen(pathname)) == NULL)
+    return (1);
+
+  while ((dent = cupsDirRead(dir)) != NULL)
+  {
+    // Skip "dot" files...
+    if (dent->filename[0] == '.')
+      continue;
+
+    // See if this is a file or directory...
+    snprintf(filename, sizeof(filename), "%s/%s", pathname, dent->filename);
+
+    if (S_ISDIR(dent->fileinfo.st_mode))
+    {
+      // Descend into the subdirectory...
+      snprintf(newprefix, sizeof(newprefix), "%s%s/", prefix, dent->filename);
+
+      if (list_drvs(filename, newprefix))
+      {
+       cupsDirClose(dir);
+       return (1);
+      }
+    }
+    else if ((ext = strrchr(dent->filename, '.')) != NULL &&
+             (!strcmp(ext, ".drv") || !strcmp(ext, ".drv.gz")))
+    {
+      // List the PPDs in this driver info file...
+      ppdcSource *src = new ppdcSource(filename);
+                                       // Driver info file
+
+      snprintf(newprefix, sizeof(newprefix), "%s%s", prefix, dent->filename);
+      list_ppds(src, newprefix);
+      delete src;
+    }
+  }
+
+  cupsDirClose(dir);
+
+  return (0);
+}
+
+
+//
+// 'list_ppds()' - List PPDs in a driver info file.
+//
+
+static int                             // O - Exit status
+list_ppds(ppdcSource *src,             // I - Driver info file
+          const char *name)            // I - Name of driver info file
+{
+  ppdcDriver   *d;                     // Current driver
+  ppdcAttr     *attr;                  // 1284DeviceID attribute
+  char         uri[1024];              // Driver URI
+
+
+  for (d = (ppdcDriver *)src->drivers->first();
+       d;
+       d = (ppdcDriver *)src->drivers->next())
+  {
+    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "drv", "", "", 0,
+                     "%s/%s", name, d->pc_file_name->value);
+
+    attr = d->find_attr("1284DeviceID", NULL);
+
+    printf("\"%s\" en \"%s\" \"%s\" \"%s\"\n", uri, d->manufacturer->value,
+           d->model_name->value, attr ? attr->value->value : "");
+  }
+
+  return (0);
+}
+
+
+
+
+#if 0
+
+
+
+  // Scan the command-line...
+  catalog = NULL;
+  outdir  = "ppd";
+  src     = 0;
+  verbose = 0;
+  locales = NULL;
+  comp    = PPDC_NO_COMPRESSION;
+  le      = PPDC_LFONLY;
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+         case 'c' :                    // Message catalog...
+             i ++;
+              if (i >= argc)
+                usage();
+
+              if (verbose > 1)
+               printf("ppdc: Loading messages from \"%s\"...\n", argv[i]);
+
+              if (!catalog)
+               catalog = new ppdcCatalog("en");
+
+              if (catalog->load_messages(argv[i]))
+             {
+               fprintf(stderr,
+                       "ppdc: Unable to load localization file \"%s\" - %s\n",
+                       argv[i], strerror(errno));
+                return (1);
+             }
+             break;
+
+          case 'd' :                   // Output directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (verbose > 1)
+               printf("ppdc: Writing PPD files to directory \"%s\"...\n",
+                      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();
+
+               strlcpy(temp, argv[i], sizeof(temp));
+               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
+             {
+               if (verbose > 1)
+                 printf("ppdc: Loading messages for locale \"%s\"...\n",
+                        argv[i]);
+
+               if (catalog)
+                 delete catalog;
+
+               catalog = new ppdcCatalog(argv[i]);
+
+               if (catalog->messages->count == 0)
+               {
+                 fprintf(stderr,
+                         "ppdc: Unable to find localization for \"%s\" - %s\n",
+                         argv[i], strerror(errno));
+                  return (1);
+               }
+             }
+             break;
+
+          case 'I' :                   // Include directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (verbose > 1)
+               printf("ppdc: Adding include directory \"%s\"...\n", argv[i]);
+
+             ppdcSource::add_include(argv[i]);
+             break;
+
+          case 'v' :                   // Be verbose...
+             verbose ++;
+             break;
+           
+          case 'z' :                   // Compress files...
+             comp = PPDC_GZIP_COMPRESSION;
+             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();
+             break;
+       }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      if (verbose > 1)
+        printf("ppdc: Loading driver information file \"%s\"...\n", argv[i]);
+
+      src = new ppdcSource(argv[i]);
+
+      // Create the output directory...
+      if (mkdir(outdir, 0777))
+      {
+        if (errno != EEXIST)
+       {
+         fprintf(stderr, "ppdc: Unable to create output directory %s: %s\n",
+                 outdir, strerror(errno));
+          return (1);
+       }
+      }
+
+      // Write PPD files...
+      for (d = (ppdcDriver *)src->drivers->first();
+           d;
+          d = (ppdcDriver *)src->drivers->next())
+      {
+        // Write the PPD file for this driver...
+       for (j = 0; d->pc_file_name->value[j]; j ++)
+         pcfilename[j] = tolower(d->pc_file_name->value[j]);
+
+       pcfilename[j] = '\0';
+
+       if (comp == PPDC_GZIP_COMPRESSION)
+         snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename);
+       else
+         snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename);
+
+       if (verbose)
+         printf("ppdc: Writing %s...\n", filename);
+
+       if (d->write_ppd_file(filename, catalog, locales, src, le, comp))
+         return (1);
+      }
+
+      // Delete the printer driver information...
+      delete src;
+    }
+
+  if (catalog)
+    delete catalog;
+
+  // If no drivers have been loaded, display the program usage message.
+  if (!src)
+    usage();
+
+  // Return with no errors.
+  return (0);
+}
+#endif // 0
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/foo-fr.po b/ppdc/foo-fr.po
new file mode 100644 (file)
index 0000000..1b1561b
--- /dev/null
@@ -0,0 +1,11 @@
+msgid "A Serious Error"
+msgstr "La Error Serious"
+
+msgid "http://foo.com/serious.html"
+msgstr "http://foo.com/fr/serious.html"
+
+msgid "Foo Letter"
+msgstr "La Foo Letter"
+
+msgid "Foo Photo"
+msgstr "La Foo Photo"
diff --git a/ppdc/foo.drv b/ppdc/foo.drv
new file mode 100644 (file)
index 0000000..138a582
--- /dev/null
@@ -0,0 +1,548 @@
+//
+// "$Id: foo.drv 361 2007-10-01 20:03:46Z mike $"
+//
+//   PPD file compiler test data file for the Common UNIX Printing
+//   System (CUPS).
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 1997-2003 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+
+/*
+ * C-style comments are supported.
+ */
+
+//
+// C++-style comments are supported.
+//
+
+//
+// Include the common media size definitions...
+//
+// #include directives support both <name> to include a standard file
+// and "name" or just name without the quotes for a local file.  Local
+// files resolve relative to the current file's path.  Unlike C/C++,
+// #include <name> does not look in multiple directories, and
+// #include "name" does not look in the standard directory.
+//
+
+#include <media.defs>
+
+
+//
+// Include the CUPS raster definitions...
+//
+
+#include <raster.defs>
+
+
+//
+// Include the standard CUPS fonts...
+//
+
+#include <font.defs>
+
+
+//
+// Define variables using the #define directive.  In this case we
+// are defining constants for the model number, which is used by
+// our imaginary rastertofoo filter to determine which model-specific
+// features to use/support.
+//
+
+#define MODEL_BW       0
+#define MODEL_COLOR    1
+
+#define MODEL_LASER    0
+#define MODEL_PHOTO    2
+
+
+//
+// Media sizes are defined using the #media directive.  The order of
+// values is: size name/text, width, length.
+//
+// "Size name" is an alphanumeric string of up to 40 characters as
+// defined by the Adobe PPD specification.
+//
+// "Size text" is a text string of up to 80 characters as defined by
+// the Adobe PPD specification.
+//
+// "Width" and "length" are the width and length of the media size.
+// Numbers by themselves represent points (72 points = 1 inch).  The
+// suffixes "cm", "ft", "in", "m", "mm", and "pt" are recognized to
+// specify centimeters, feet, inches, meters, millimeters, and points,
+// respectively.
+//
+
+#media "FooLetter/Foo Letter" 8in 10in
+#media "FooPhoto/Foo Photo" 200mm 300mm
+
+
+//
+// Message catalogs can be included using #po...
+//
+
+#po fr foo-fr.po
+
+
+//
+// Specify that the drivers use all of the standard base fonts...
+//
+
+Font *
+
+
+//
+// All copyright lines are put at the top of the PPD file in order
+// of their appearance.  Copyright text can span multiple lines and
+// will be properly included in the PPD file with comment prefixes
+// on each line.
+//
+// First an MIT-style copyright/license notice...
+//
+
+Copyright "Copyright 2007 by Foo Industries."
+Copyright "
+Permission is granted for redistribution of this file as long as
+this copyright notice is intact and the contents of the file are
+not altered in any way from their original form.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the \"Software\"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+"
+
+//
+// Then a GPL notice...
+//
+
+Copyright "Copyright 2007 by Foo Industries."
+Copyright "
+This software is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
+
+This software is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public
+License along with this software; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+MA 02111 USA
+"
+
+
+//
+// All printer drivers must define the manufacturer, model, PC
+// filename, and version strings; since this test file contains
+// drivers for an imaginary manufacturer "Foo", all of the drivers
+// listed in this file share common manufacturer and version
+// strings.
+//
+
+Manufacturer "Foo"
+Version 1.0
+
+
+//
+// Printer drivers can access driver-specific attributes in a PPD
+// file; these attributes are specified using lines of the form:
+//
+//     Attribute name selector value
+//
+// "Name" is the name of the attribute and should start with either
+// the "cups" prefix or the name of the vendor, e.g. "hpFoo",
+// "epsonBar", etc.  The name can be any alphanumeric character (a-z,
+// A-Z, and 0-9) and cannot be a common prefix of another attribute,
+// e.g. "fooLines" and "fooLinesPerInch" cannot be in the same file.
+//
+// "Selector" is a selector string containing any characters except
+// colon (:).  Typically this will be one or more keywords separated
+// by the forward slash (/), however the empty string ("") can be used
+// to omit the selector.
+//
+// "Value" is a quoted value string that can contain any printable
+// characters except the double quote (").  Hexadecimal numbers
+// inside angle brackets (<xx>) can be used to substitute escape
+// codes and other special characters.
+//
+
+Attribute fooOutputFormat "" "PCL"
+Attribute fooPJL Begin "<1B>%-12345X@PJL<0D0A>"
+Attribute fooPJL Enter/PCL "@PJL ENTER LANGUAGE=PCL<0D0A>"
+Attribute fooPJL End "<1B>%-12345X@PJL END JOB<0D0A>"
+
+//
+// Most printer drivers use filters; exceptions include PostScript
+// printers and PPD files for software RIPs.
+//
+// The format is:
+//
+//     Filter mime-type cost program
+//
+// The "mime-type" field defines the MIME type that the filter program
+// accepts; for CUPS raster printer drivers, this will be
+// "application/vnd.cups-raster".
+//
+// The "cost" field defines the relative cost of the filter in terms of
+// both CPU and memory usage, and is used to limit the number of
+// simultaneous jobs in some configurations.  Most raster filters should
+// have a cost of 100, unless the filter does no dithering - then a cost
+// of 33 is more appropriate.
+//
+// The "program" field defined the filter program to run; use the null
+// filter "-" to define a MIME type that the printer accepts directly.
+// If no path information is provided, then the program will be run
+// from the standard CUPS filter directory, usually
+// /usr/lib/cups/filter.
+//
+// When compiling PPD files for PostScript capable devices that use
+// additional filters, add a null filter for the MIME type
+// "application/vnd.cups-postscript" so that printer commands, user
+// job filters, and page markings can be added to the PostScript
+// output that is sent to the printer.
+//
+
+Filter application/vnd.cups-raster 100 rastertofoo
+
+
+//
+// Attributes are included thusly...
+//
+
+Attribute cupsIPPReason "com.foo-serious-error/A Serious Error" "http://foo.com/serious.html"
+
+
+//
+// Curley braces are used for grouping common data and for isolating
+// individual printer models.  All data values are inherited *except*
+// for the PCFilename and ModelName strings.
+//
+
+{
+  //
+  // Define two printer drivers that support only the FooLetter and
+  // FooPhoto media size.  One is color, the other is black-and-white.
+  //
+  // Both printers share two MediaSize definitions; the name listed
+  // after the MediaSize keyword must be one of the Adobe standard
+  // names listed in the PPD specification or any named size defined
+  // using the #media directive.
+  //
+  // Default options are indicated by placing an asterisk (*) before
+  // the keyword.
+  //
+  // For custom size and margin specification, see the next group of
+  // printer drivers.
+  //
+
+  MediaSize FooLetter
+  *MediaSize FooPhoto
+
+
+  //
+  // These imaginary printers support printing at 300, 600x300,
+  // and 600 DPI.  We'll use the old-style Resolution convenience
+  // keyword which accepts the following parameters: colorspace/
+  // order, bits-per-color, row count, row feed, row step, and
+  // name/text.
+  //
+  // The name must be of the form NNNsuffix or NNNxMMMsuffix,
+  // where NNN and MMM represent the X and Y resolution in dots
+  // per inch.
+  //
+
+  Resolution - 8 0 0 0 "300dpi/300 DPI"
+  Resolution - 8 0 0 0 "600x300dpi/600 x 300 DPI"
+  *Resolution - 8 0 0 0 "600dpi/600 DPI"
+
+
+  //
+  // One printer is grayscale only, and the other does grayscale
+  // and color.  Define the grayscale color model for both printers
+  // using the old-style ColorModel convenience keyword which
+  // accepts the name/text, colorspace, color order, and compression
+  // parameters.
+  //
+
+  ColorModel   Gray/Grayscale w chunked 0
+
+
+  {
+    //
+    // The first sub-group contains the grayscale printer, which
+    // only needs the model name, PC filename, and model number
+    // values set...
+    //
+    // The ModelName keyword defines the string that is shown to
+    // the user.
+    //
+
+    ModelName "Mono Photo Printer"
+
+
+    //
+    // The ModelNumber keyword defines the cupsModelNumber
+    // attribute value.  We use the "(name name)" notation
+    // to perform a bitwise OR of the #define'd constants.
+    //
+
+    ModelNumber ($MODEL_BW $MODEL_PHOTO)
+
+
+    //
+    // The PCFileName keyword defines the filename of the PPD
+    // file and should be 8 characters or less + the .ppd
+    // extension.
+    //
+
+    PCFileName "foogphot.ppd"
+  }
+
+
+  {
+    //
+    // The second sub-group contains the color printer, which
+    // needs another ColorModel definition along with the model
+    // name, PC filename, and model number values.  For fun, we'll
+    // add some input slots (paper trays) as well.
+    //
+    // The ModelName keyword defines the string that is shown to
+    // the user.
+    //
+
+    ModelName "Color Photo Printer"
+
+
+    //
+    // The ModelNumber keyword defines the cupsModelNumber
+    // attribute value.  We use the "(name name)" notation
+    // to perform a bitwise OR of the #define'd constants.
+    //
+
+    ModelNumber ($MODEL_COLOR $MODEL_PHOTO)
+
+
+    //
+    // The PCFileName keyword defines the filename of the PPD
+    // file and should be 8 characters or less + the .ppd
+    // extension.
+    //
+
+    PCFileName "foocphot.ppd"
+
+
+    //
+    // This printer does color printing, too, so add it and make
+    // RGB the default...
+    //
+
+    ColorDevice Yes
+
+    *ColorModel        RGB/Color rgb chunked 0
+
+
+    //
+    // The old-style InputSlot keyword accepts tray definitions
+    // of the form:
+    //
+    //    InputSlot position name/text
+    //
+
+    InputSlot 0 "Upper/Main Paper Tray"
+    InputSlot 1 "LargeCapacity/Large Paper Tray"
+  }
+}
+
+
+{
+  //
+  // Define two printer drivers that support two typical laser
+  // printers with custom page sizes.  One is color, the other is
+  // black-and-white.
+  //
+  // Both printers share several MediaSize definitions and support
+  // custom page sizes from 3x5 to 13x19 inches.
+  //
+  // All US media sizes use hardware margins of 0.25 inches on the sides
+  // and 12 points (1/6th inch) at the top and bottom.  European sizes
+  // and custom sizes use margins of 12 points all around.
+  //
+  // The order of the HWMargins numbers are left, bottom, right, and top.
+  // The current HWMargins values are used when defining each media size.
+  // The last HWMargins values are used for custom page size margins.
+  //
+
+  HWMargins 0.25in 12pt 0.25in 12pt
+
+  *MediaSize Letter
+  MediaSize Legal
+  MediaSize Tabloid
+  MediaSize TabloidExtra
+
+  HWMargins 12pt 12pt 12pt 12pt
+  MediaSize A4
+  MediaSize A3
+
+  //
+  // Specify that custom/variable paper sizes are supported, and the
+  // range of sizes that are supported...
+  //
+
+  VariablePaperSize Yes
+  MinSize 3in 5in
+  MaxSize 13in 19in
+
+
+  //
+  // These imaginary printers support printing at 600 and 1200 DPI.
+  // We'll use the new Option and Choice keywords to define the
+  // Resolution options...
+  //
+  // Option option-name option-text option-type
+  // Choice choice-name choice-text code
+  //
+  // "Option-type" is the type of option: boolean, pickone, or pickmany.
+  //
+
+  Option Resolution PickOne AnySetup 10
+  Choice "600dpi/600 DPI" "<</HWResolution[600 600]/cupsBitsPerColor 8>>setpagedevice"
+  Choice "1200dpi/1200 DPI" "<</HWResolution[1200 1200]/cupsBitsPerColor 8>>setpagedevice"
+  
+
+  //
+  // One printer is grayscale only, and the other does grayscale
+  // and color.  Define the grayscale color model for both printers
+  // using the new Option and Choice keywords.
+  //
+
+  Option "ColorModel/Color Mode" PickOne AnySetup 10
+  Choice Gray/Grayscale "<</cupsColorSpace $CUPS_CSPACE_W>>setpagedevice"
+
+
+  //
+  // Both printers provide two paper trays, which we'll define using
+  // the new Option and Choice keywords...
+  //
+
+  Option "InputSlot/Input Slot" PickOne AnySetup 10
+  Choice "Upper/Main Paper Tray" "<</MediaPosition 0>>setpagedevice"
+  Choice "LargeCapacity/Large Paper Tray" "<</MediaPosition 1>>setpagedevice"
+
+
+  //
+  // Both printers support duplexing...
+  //
+  // The Duplex keyword accepts values of "none" (no duplexing capability),
+  // "normal" (standard duplexing capability), and "flip" (auto-duplex that
+  // requires the back side to be flipped by the RIP...)
+  //
+
+  Duplex normal
+
+
+  {
+    //
+    // The first sub-group contains the grayscale printer, which
+    // only needs the model name, PC filename, and model number
+    // values set...
+    //
+    // The ModelName keyword defines the string that is shown to
+    // the user.
+    //
+
+    ModelName "Mono Laser Printer"
+
+
+    //
+    // The ModelNumber keyword defines the cupsModelNumber
+    // attribute value.  We use the "(name name)" notation
+    // to perform a bitwise OR of the #define'd constants.
+    //
+
+    ModelNumber ($MODEL_BW $MODEL_LASER)
+
+
+    //
+    // The PCFileName keyword defines the filename of the PPD
+    // file and should be 8 characters or less + the .ppd
+    // extension.
+    //
+
+    PCFileName "fooglasr.ppd"
+  }
+
+
+  {
+    //
+    // The second sub-group contains the color printer, which
+    // needs another ColorModel definition along with the model
+    // name, PC filename, and model number values.
+    //
+    // The ModelName keyword defines the string that is shown to
+    // the user.
+    //
+
+    ModelName "Color Laser Printer"
+
+
+    //
+    // The ModelNumber keyword defines the cupsModelNumber
+    // attribute value.  We use the "(name name)" notation
+    // to perform a bitwise OR of the #define'd constants.
+    //
+
+    ModelNumber ($MODEL_COLOR $MODEL_LASER)
+
+
+    //
+    // The PCFileName keyword defines the filename of the PPD
+    // file and should be 8 characters or less + the .ppd
+    // extension.
+    //
+
+    PCFileName "fooclasr.ppd"
+
+
+    //
+    // This printer does color printing, too, so add it and make
+    // RGB the default...
+    //
+
+    ColorDevice Yes
+
+    Option "ColorModel/Color Mode" PickOne AnySetup 10
+    *Choice RGB/Color "<</cupsColorSpace $CUPS_CSPACE_RGB>>setpagedevice"
+  }
+}
+
+
+//
+// End of "$Id: foo.drv 361 2007-10-01 20:03:46Z mike $".
+//
diff --git a/ppdc/ppdc-array.cxx b/ppdc/ppdc-array.cxx
new file mode 100644 (file)
index 0000000..3cccad3
--- /dev/null
@@ -0,0 +1,163 @@
+//
+// "$Id$"
+//
+//   Array class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcArray::ppdcArray()  - Create a new array.
+//   ppdcArray::~ppdcArray() - Destroy an array.
+//   ppdcArray::add()        - Add an element to an array.
+//   ppdcArray::first()      - Return the first element in the array.
+//   ppdcArray::next()       - Return the next element in the array.
+//   ppdcArray::remove()     - Remove an element from the array.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcArray::ppdcArray()' - Create a new array.
+//
+
+ppdcArray::ppdcArray(ppdcArray *a)
+{
+  if (a)
+  {
+    count = a->count;
+    alloc = count;
+
+    if (count)
+    {
+      // Make a copy of the array...
+      data = new ppdcShared *[count];
+
+      memcpy(data, a->data, count * sizeof(ppdcShared *));
+
+      for (int i = 0; i < count; i ++)
+        data[i]->get();
+    }
+    else
+      data = 0;
+  }
+  else
+  {
+    count = 0;
+    alloc = 0;
+    data  = 0;
+  }
+
+  current = 0;
+}
+
+
+//
+// 'ppdcArray::~ppdcArray()' - Destroy an array.
+//
+
+ppdcArray::~ppdcArray()
+{
+  for (int 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, 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
+{
+  int  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, (count - i) * sizeof(ppdcShared *));
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-attr.cxx b/ppdc/ppdc-attr.cxx
new file mode 100644 (file)
index 0000000..094e387
--- /dev/null
@@ -0,0 +1,60 @@
+//
+// "$Id$"
+//
+//   Attribute class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcAttr::ppdcAttr()  - Create an attribute.
+//   ppdcAttr::~ppdcAttr() - Destroy an attribute.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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
+  : ppdcShared()
+{
+  name     = new ppdcString(n);
+  selector = new ppdcString(s);
+  text     = new ppdcString(t);
+  value    = new ppdcString(v);
+}
+
+
+//
+// 'ppdcAttr::~ppdcAttr()' - Destroy an attribute.
+//
+
+ppdcAttr::~ppdcAttr()
+{
+  name->release();
+  selector->release();
+  text->release();
+  value->release();
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-catalog.cxx b/ppdc/ppdc-catalog.cxx
new file mode 100644 (file)
index 0000000..e6f656e
--- /dev/null
@@ -0,0 +1,368 @@
+//
+// "$Id$"
+//
+//   Shared message catalog class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2006 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcCatalog::ppdcCatalog()   - Create a shared message catalog.
+//   ppdcCatalog::~ppdcCatalog()  - Destroy a shared message catalog.
+//   ppdcCatalog::add_message()   - Add a new message.
+//   ppdcCatalog::find_message()  - Find a message in a catalog...
+//   ppdcCatalog::load_messages() - Load messages from a .po file.
+//   ppdcCatalog::save_messages() - Save the messages to a .po file.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
+//
+
+ppdcCatalog::ppdcCatalog(const char *l,        // I - Locale
+                         const char *f)        // I - Message catalog file
+  : ppdcShared()
+{
+  locale   = new ppdcString(l);
+  filename = new ppdcString(f);
+  messages = new ppdcArray();
+
+  if (l)
+  {
+    // Try loading the base messages for this locale...
+    const char *podir;                 // Message catalog directory
+    char       pofile[1024];           // Message catalog file
+
+
+    if ((podir = getenv("PPDC_PO_DIR")) == NULL)
+      podir = PPDC_PO_DIR;
+
+    snprintf(pofile, sizeof(pofile), "%s/%s.po", podir, l);
+
+    if (load_messages(pofile) && strchr(l, '_'))
+    {
+      // Try the base locale...
+      char     baseloc[3];             // Base locale...
+
+
+      strlcpy(baseloc, l, sizeof(baseloc));
+      snprintf(pofile, sizeof(pofile), "%s/%s.po", podir, baseloc);
+
+      load_messages(pofile);
+    }
+  }
+
+  if (f)
+    load_messages(f);
+}
+
+
+//
+// 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
+//
+
+ppdcCatalog::~ppdcCatalog()
+{
+  delete locale;
+  delete filename;
+  delete messages;
+}
+
+
+//
+// 'ppdcCatalog::add_message()' - Add a new message.
+//
+
+void
+ppdcCatalog::add_message(const char *id)// I - Message ID to add
+{
+  ppdcMessage  *m;                     // Current message
+  char         text[1024];             // Text to translate
+
+
+  // Range check input...
+  if (!id || !*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))
+      return;
+
+  // Add the message...
+  snprintf(text, sizeof(text), "TRANSLATE %s", id);
+  messages->add(new ppdcMessage(id, text));
+}
+
+
+//
+// '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
+
+
+  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
+  ppdcMessage  *temp;                  // Current message
+  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);
+
+ /*
+  * 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"
+  */
+
+  linenum = 0;
+  id[0]   = '\0';
+  str[0]  = '\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 = strrchr(line, '\"')) == NULL)
+    {
+      fprintf(stderr, "load_messages: 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, "load_messages: 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 (id[0] && str[0])
+      {
+       temp = new ppdcMessage(id, str);
+
+       messages->add(temp);
+      }
+
+      strlcpy(id, ptr, sizeof(id));
+      str[0] = '\0';
+    }
+    else if (!strncmp(line, "msgstr", 6))
+    {
+      if (!id[0])
+      {
+       fprintf(stderr, "load_messages: Need a msgid line before any "
+                       "translation strings on line %d of %s!\n",
+               linenum, f);
+       cupsFileClose(fp);
+       return (-1);
+      }
+
+      strlcpy(str, ptr, sizeof(str));
+    }
+    else if (line[0] == '\"' && str[0])
+      strlcat(str, ptr, sizeof(str));
+    else if (line[0] == '\"' && id[0])
+      strlcat(id, ptr, sizeof(id));
+    else
+    {
+      fprintf(stderr, "load_messages: Unexpected text on line %d of %s!\n",
+              linenum, f);
+      cupsFileClose(fp);
+      return (-1);
+    }
+  }
+
+  if (id[0] && str[0])
+  {
+    temp = new ppdcMessage(id, str);
+
+    messages->add(temp);
+  }
+
+  cupsFileClose(fp);
+
+  return (0);
+}
+
+
+//
+// '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
+  const char   *ptr;                   // Pointer into string
+
+
+  if ((fp = cupsFileOpen(f, "w")) == NULL)
+    return (-1);
+
+  for (m = (ppdcMessage *)messages->first();
+       m;
+       m = (ppdcMessage *)messages->next())
+  {
+    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);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-choice.cxx b/ppdc/ppdc-choice.cxx
new file mode 100644 (file)
index 0000000..22ae055
--- /dev/null
@@ -0,0 +1,57 @@
+//
+// "$Id$"
+//
+//   Option choice class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcChoice::ppdcChoice()  - Create a new option choice.
+//   ppdcChoice::~ppdcChoice() - Destroy an option choice.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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()
+{
+  name = new ppdcString(n);
+  text = new ppdcString(t);
+  code = new ppdcString(c);
+}
+
+
+//
+// 'ppdcChoice::~ppdcChoice()' - Destroy an option choice.
+//
+
+ppdcChoice::~ppdcChoice()
+{
+  name->release();
+  text->release();
+  code->release();
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-constraint.cxx b/ppdc/ppdc-constraint.cxx
new file mode 100644 (file)
index 0000000..2a651b9
--- /dev/null
@@ -0,0 +1,60 @@
+//
+// "$Id$"
+//
+//   Contraint class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcConstraint::ppdcConstraint()  - Create a constraint.
+//   ppdcConstraint::~ppdcConstraint() - Destroy a constraint.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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()
+{
+  option1 = new ppdcString(o1);
+  choice1 = new ppdcString(c1);
+  option2 = new ppdcString(o2);
+  choice2 = new ppdcString(c2);
+}
+
+
+//
+// 'ppdcConstraint::~ppdcConstraint()' - Destroy a constraint.
+//
+
+ppdcConstraint::~ppdcConstraint()
+{
+  option1->release();
+  choice1->release();
+  option2->release();
+  choice2->release();
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-driver.cxx b/ppdc/ppdc-driver.cxx
new file mode 100644 (file)
index 0000000..3d2f2a3
--- /dev/null
@@ -0,0 +1,1208 @@
+//
+// "$Id$"
+//
+//   PPD file compiler definitions for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2006 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcDriver::ppdcDriver()       - Create a new printer driver.
+//   ppdcDriver::~ppdcDriver()      - Destroy a printer driver.
+//   ppdcDriver::find_attr()        - Find an attribute.
+//   ppdcDriver::find_group()       - Find a group.
+//   ppdcDriver::find_option()      - Find an option.
+//   ppdcDriver::set_default_size() - Set the default size name.
+//   ppdcDriver::set_manufacturer() - Set the manufacturer name.
+//   ppdcDriver::set_model_name()   - Set the model name.
+//   ppdcDriver::set_pc_file_name() - Set the PC filename.
+//   ppdcDriver::set_version()      - Set the version string.
+//   ppdcDriver::write_ppd_file()   - Write a PPD file...
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <cups/cups.h>
+
+
+//
+// 'ppdcDriver::ppdcDriver()' - Create a new printer driver.
+//
+
+ppdcDriver::ppdcDriver(ppdcDriver *d)  // I - Printer driver template
+{
+  ppdcGroup    *g;                     // Current group
+
+
+  if (d)
+  {
+    // Bump the use count of any strings we inherit...
+    if (d->manufacturer)
+      d->manufacturer->get();
+    if (d->version)
+      d->version->get();
+    if (d->default_font)
+      d->default_font->get();
+    if (d->default_size)
+      d->default_size->get();
+    if (d->custom_size_code)
+      d->custom_size_code->get();
+
+    // Copy all of the data from the driver template...
+    copyright           = new ppdcArray(d->copyright);
+    manufacturer        = d->manufacturer;
+    model_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;
+    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()
+{
+  delete copyright;
+
+  if (manufacturer)
+    manufacturer->release();
+  if (model_name)
+    model_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();
+
+  delete attrs;
+  delete constraints;
+  delete filters;
+  delete fonts;
+  delete groups;
+  delete profiles;
+  delete sizes;
+}
+
+
+//
+// '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
+{
+  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))
+        return (o);
+
+  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->get();
+    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->get();
+    default_size = m->name;
+  }
+  else
+    default_size = 0;
+}
+
+
+//
+// '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
+  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("en");
+    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 CUPS PPD Compiler " DDK_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: \"(ESP Ghostscript)\"%s", lf);
+    cupsFilePrintf(fp, "*Product: \"(GPL Ghostscript)\"%s", lf);
+    cupsFilePrintf(fp, "*Product: \"(GNU Ghostscript)\"%s", 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) 705\"%s", lf);
+    cupsFilePrintf(fp, "*PSVersion: \"(3010.000) 707\"%s", lf);
+    cupsFilePrintf(fp, "*PSVersion: \"(3010.000) 815\"%s", lf);
+    cupsFilePrintf(fp, "*PSVersion: \"(3010.000) 853\"%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);
+
+  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") ||
+          a->name->value[0] == '?')
+       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'))
+          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())
+      cupsFilePrintf(fp,
+                     "*cupsColorProfile %s/%s: \"%.3f %.3f %.3f %.3f %.3f %.3f "
+                    "%.3f %.3f %.3f %.3f %.3f\"%s",
+                    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], 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);
+          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))
+      cupsFilePrintf(fp, "*NonUIConstraints: ");
+    else
+      cupsFilePrintf(fp, "*UIConstraints: ");
+
+    if (cn->option1->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePrintf(fp, cn->option1->value);
+
+    if (cn->choice1->value)
+      cupsFilePrintf(fp, " %s", cn->choice1->value);
+
+    cupsFilePutChar(fp, ' ');
+
+    if (cn->option2->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePrintf(fp, cn->option2->value);
+
+    if (cn->choice2->value)
+      cupsFilePrintf(fp, " %s", cn->choice2->value);
+
+    cupsFilePrintf(fp, "%s", lf);
+
+    // Then constrain 2 against 1...
+    if (!strncmp(cn->option1->value, "*Custom", 7) ||
+        !strncmp(cn->option2->value, "*Custom", 7))
+      cupsFilePrintf(fp, "*NonUIConstraints: ");
+    else
+      cupsFilePrintf(fp, "*UIConstraints: ");
+
+    if (cn->option2->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePrintf(fp, cn->option2->value);
+
+    if (cn->choice2->value)
+      cupsFilePrintf(fp, " %s", cn->choice2->value);
+
+    cupsFilePutChar(fp, ' ');
+
+    if (cn->option1->value[0] != '*')
+      cupsFilePutChar(fp, '*');
+
+    cupsFilePrintf(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);
+
+  for (m = (ppdcMediaSize *)sizes->first();
+       m;
+       m = (ppdcMediaSize *)sizes->next())
+    cupsFilePrintf(fp, "*ImageableArea %s/%s: \"%.2f %.2f %.2f %.2f\"%s",
+                   m->name->value, catalog->find_message(m->text->value),
+                  m->left, m->bottom, m->width - m->right, m->length - m->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);
+
+  for (m = (ppdcMediaSize *)sizes->first();
+       m;
+       m = (ppdcMediaSize *)sizes->next())
+    cupsFilePrintf(fp, "*PaperDimension %s/%s: \"%.2f %.2f\"%s",
+                   m->name->value, catalog->find_message(m->text->value),
+                  m->width, m->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)
+  {
+    cupsFilePrintf(fp, "*MaxMediaWidth: \"%.2f\"%s", max_width, lf);
+    cupsFilePrintf(fp, "*MaxMediaHeight: \"%.2f\"%s", max_length, lf);
+    cupsFilePrintf(fp, "*HWMargins: %.2f %.2f %.2f %.2f\n",
+                  left_margin, bottom_margin, right_margin, top_margin);
+
+    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
+      cupsFilePrintf(fp, "*ParamCustomPageSize Width: 1 points %.2f %.2f%s",
+                     min_width, max_width, lf);
+
+    if ((a = find_attr("ParamCustomPageSize", "Height")) != NULL)
+      cupsFilePrintf(fp, "*ParamCustomPageSize Height: %s%s", a->value->value,
+                    lf);
+    else
+      cupsFilePrintf(fp, "*ParamCustomPageSize Height: 2 points %.2f %.2f%s",
+                     min_length, max_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);
+  }
+
+  if (type != PPDC_DRIVER_PS && !find_attr("RequiresPageRegion", NULL))
+    cupsFilePrintf(fp, "*RequiresPageRegion All: True%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->text->value || !strcmp(o->name->value, o->text->value))
+       cupsFilePrintf(fp, "*OpenUI *%s: ", 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;
+      }
+
+      cupsFilePrintf(fp, "*OrderDependency: %.1f ", o->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 || !strcmp(c->name->value, c->text->value))
+          cupsFilePrintf(fp, "*%s %s: \"%s\"%s", o->name->value, 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\"\n", query, a->value->value);
+
+       if (strchr(a->value->value, '\n') ||
+            strchr(a->value->value, '\r'))
+         cupsFilePrintf(fp, "*End%s", lf);
+      }
+
+      cupsFilePrintf(fp, "*CloseUI: *%s%s", o->name->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->text || !a->text->value || !a->text->value[0]) &&
+           strncmp(a->name->value, "Custom", 6) &&
+           strncmp(a->name->value, "ParamCustom", 11))
+         continue;
+
+        if (strcmp(a->name->value, "APCustomColorMatchingName") &&
+           strcmp(a->name->value, "APPrinterPreset") &&
+           strcmp(a->name->value, "cupsICCProfile") &&
+           strcmp(a->name->value, "cupsIPPReason") &&
+           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),
+                      !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)(cupsFileTell(fp) + 25 + strlen(pc_file_name->value)),
+                lf);
+
+  if (delete_cat)
+    delete catalog;
+
+  return (0);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-file.cxx b/ppdc/ppdc-file.cxx
new file mode 100644 (file)
index 0000000..3af0e49
--- /dev/null
@@ -0,0 +1,100 @@
+//
+// "$Id$"
+//
+//   File class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcFile::ppdcFile()  - Create (open) a file.
+//   ppdcFile::~ppdcFile() - Delete (close) a file.
+//   ppdcFile::get()       - Get a character from a file.
+//   ppdcFile::peek()      - Look at the next character from a file.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcFile::ppdcFile()' - Create (open) a file.
+//
+
+ppdcFile::ppdcFile(const char *f)              // I - File to open
+{
+  fp       = cupsFileOpen(f, "r");
+  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 (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));
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-filter.cxx b/ppdc/ppdc-filter.cxx
new file mode 100644 (file)
index 0000000..bc33add
--- /dev/null
@@ -0,0 +1,55 @@
+//
+// "$Id$"
+//
+//   Filter class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcFilter::ppdcFilter()  - Create a filter.
+//   ppdcFilter::~ppdcFilter() - Destroy a filter.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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
+{
+  mime_type = new ppdcString(t);
+  program   = new ppdcString(p);
+  cost      = c;
+}
+
+
+//
+// 'ppdcFilter::~ppdcFilter()' - Destroy a filter.
+//
+
+ppdcFilter::~ppdcFilter()
+{
+  mime_type->release();
+  program->release();
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-font.cxx b/ppdc/ppdc-font.cxx
new file mode 100644 (file)
index 0000000..85a719d
--- /dev/null
@@ -0,0 +1,62 @@
+//
+// "$Id$"
+//
+//   Shared font class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcFont::ppdcFont()  - Create a shared font.
+//   ppdcFont::~ppdcFont() - Destroy a shared font.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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()
+{
+  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()
+{
+  name->release();
+  encoding->release();
+  version->release();
+  charset->release();
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-group.cxx b/ppdc/ppdc-group.cxx
new file mode 100644 (file)
index 0000000..c93fc20
--- /dev/null
@@ -0,0 +1,96 @@
+//
+// "$Id$"
+//
+//   Group class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcGroup::ppdcGroup()   - Create a new group.
+//   ppdcGroup::ppdcGroup()   - Copy a new group.
+//   ppdcGroup::~ppdcGroup()  - Destroy a group.
+//   ppdcGroup::find_option() - Find an option in a group.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcGroup::ppdcGroup()' - Create a new group.
+//
+
+ppdcGroup::ppdcGroup(const char *n,    // I - Name of group
+                     const char *t)    // I - Text of group
+{
+  name    = new ppdcString(n);
+  text    = new ppdcString(t);
+  options = new ppdcArray();
+}
+
+
+//
+// 'ppdcGroup::ppdcGroup()' - Copy a new group.
+//
+
+ppdcGroup::ppdcGroup(ppdcGroup *g)     // I - Group template
+{
+  ppdcOption   *o;                     // Current option
+
+
+  g->name->get();
+  g->text->get();
+
+  name = g->name;
+  text = g->text;
+
+  options = new ppdcArray();
+  for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next())
+    options->add(new ppdcOption(o));
+}
+
+
+//
+// 'ppdcGroup::~ppdcGroup()' - Destroy a group.
+//
+
+ppdcGroup::~ppdcGroup()
+{
+  name->release();
+  text->release();
+  delete options;
+}
+
+
+//
+// '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);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-import.cxx b/ppdc/ppdc-import.cxx
new file mode 100644 (file)
index 0000000..486f9b7
--- /dev/null
@@ -0,0 +1,283 @@
+//
+// "$Id$"
+//
+//   PPD file import methods for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2006 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcSource::import_ppd() - Import a PPD file.
+//   ppd_gets()               - Get a line from a PPD file.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <cups/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
+  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
+  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 ((driver = find_driver(ppd->pcfilename)) == NULL)
+  {
+    // 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
+      {
+        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;
+
+    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;
+
+    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 ++)
+        {
+          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 ++)
+    {
+      for (j = i - 1, constraint2 = constraint;
+           j > 0;
+          j --, constraint2 ++)
+       if (constraint != constraint2 &&
+           !strcmp(constraint->option1, constraint2->option2) &&
+           (constraint->choice1 == constraint2->choice2 ||
+            (constraint->choice1 && constraint2->choice2 &&
+             !strcmp(constraint->choice1, constraint2->choice2))) &&
+           !strcmp(constraint->option2, constraint2->option1) &&
+           (constraint->choice2 == constraint2->choice1 ||
+            (constraint->choice2 && constraint2->choice1 &&
+             !strcmp(constraint->choice2, constraint2->choice1))))
+          break;
+
+      if (j)
+        continue;
+
+      cconstraint = new ppdcConstraint(constraint->option1, constraint->choice1,
+                                       constraint->option2, constraint->choice2);
+      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, "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 ((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, "ShortNickName") &&
+              strcmp(attr->name, "Throughput") &&
+              strcmp(attr->name, "PCFileName") &&
+              strcmp(attr->name, "FileVersion") &&
+              strcmp(attr->name, "FormatVersion") &&
+              strcmp(attr->name, "VariablePaperSize") &&
+              strcmp(attr->name, "LanguageEncoding") &&
+              strcmp(attr->name, "LanguageVersion"))
+      {
+        // 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));
+      }
+    }
+  }
+
+  return (1);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-mediasize.cxx b/ppdc/ppdc-mediasize.cxx
new file mode 100644 (file)
index 0000000..eb1f697
--- /dev/null
@@ -0,0 +1,81 @@
+//
+// "$Id$"
+//
+//   Shared media size class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcMediaSize::ppdcMediaSize()  - Create a new media size.
+//   ppdcMediaSize::~ppdcMediaSize() - Destroy a media size.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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()
+{
+  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()
+{
+  name->release();
+  text->release();
+  size_code->release();
+  region_code->release();
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-message.cxx b/ppdc/ppdc-message.cxx
new file mode 100644 (file)
index 0000000..e0285d2
--- /dev/null
@@ -0,0 +1,54 @@
+//
+// "$Id$"
+//
+//   Shared message class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcMessage::ppdcMessage()  - Create a shared message.
+//   ppdcMessage::~ppdcMessage() - Destroy a shared message.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcMessage::ppdcMessage()' - Create a shared message.
+//
+
+ppdcMessage::ppdcMessage(const char *i,        // I - ID
+                         const char *s)        // I - Text
+  : ppdcShared()
+{
+  id     = new ppdcString(i);
+  string = new ppdcString(s);
+}
+
+
+//
+// 'ppdcMessage::~ppdcMessage()' - Destroy a shared message.
+//
+
+ppdcMessage::~ppdcMessage()
+{
+  delete id;
+  delete string;
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-option.cxx b/ppdc/ppdc-option.cxx
new file mode 100644 (file)
index 0000000..3eb46aa
--- /dev/null
@@ -0,0 +1,126 @@
+//
+// "$Id$"
+//
+//   Option class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcOption::ppdcOption()    - Create a new option.
+//   ppdcOption::ppdcOption()    - Copy a new option.
+//   ppdcOption::~ppdcOption()   - Destroy an option.
+//   ppdcOption::find_choice()   - Find an option choice.
+//   ppdcOption::set_defchoice() - Set the default choice.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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
+{
+//  printf("ppdcOption(ot=%d, n=\"%s\", t=\"%s\"), this=%p\n",
+//         ot, n, t, this);
+
+  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
+{
+  o->name->get();
+  o->text->get();
+  if (o->defchoice)
+    o->defchoice->get();
+
+  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()
+{
+  name->release();
+  text->release();
+  if (defchoice)
+    defchoice->release();
+  delete choices;
+}
+
+
+//
+// '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->get();
+
+  defchoice = c->name;
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-profile.cxx b/ppdc/ppdc-profile.cxx
new file mode 100644 (file)
index 0000000..813339e
--- /dev/null
@@ -0,0 +1,60 @@
+//
+// "$Id$"
+//
+//   Color profile class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcProfile::ppdcProfile()  - Create a color profile.
+//   ppdcProfile::~ppdcProfile() - Destroy a color profile.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.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
+{
+  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()
+{
+  resolution->release();
+  media_type->release();
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-shared.cxx b/ppdc/ppdc-shared.cxx
new file mode 100644 (file)
index 0000000..28874dd
--- /dev/null
@@ -0,0 +1,75 @@
+//
+// "$Id$"
+//
+//   Shared data class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcShared::ppdcShared()  - Create shared data.
+//   ppdcShared::~ppdcShared() - Destroy shared data.
+//   ppdcShared::get()         - Increment the use count for this data.
+//   ppdcShared::release()     - Decrement the use count and delete as needed.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcShared::ppdcShared()' - Create shared data.
+//
+
+ppdcShared::ppdcShared()
+{
+  use = 1;
+}
+
+
+//
+// 'ppdcShared::~ppdcShared()' - Destroy shared data.
+//
+
+ppdcShared::~ppdcShared()
+{
+}
+
+
+//
+// 'ppdcShared::get()' - Increment the use count for this data.
+//
+
+void
+ppdcShared::get(void)
+{
+  use ++;
+}
+
+
+//
+// 'ppdcShared::release()' - Decrement the use count and delete as needed.
+//
+
+void
+ppdcShared::release(void)
+{
+  use --;
+  if (!use)
+    delete this;
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-source.cxx b/ppdc/ppdc-source.cxx
new file mode 100644 (file)
index 0000000..20588d8
--- /dev/null
@@ -0,0 +1,3246 @@
+//
+// "$Id$"
+//
+//   Source class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2007 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcSource::ppdcSource()         - Load a driver source file.
+//   ppdcSource::~ppdcSource()        - Free a driver source file.
+//   ppdcSource::add_include()        - Add an include directory.
+//   ppdcSource::find_driver()        - Find a driver.
+//   ppdcSource::find_include()       - Find an include file.
+//   ppdcSource::find_size()          - Find a media size.
+//   ppdcSource::find_variable()      - Find a variable.
+//   ppdcSource::get_attr()           - Get an attribute.
+//   ppdcSource::get_boolean()        - Get a boolean value.
+//   ppdcSource::get_choice()         - Get a choice.
+//   ppdcSource::get_color_model()    - Get an old-style color model option.
+//   ppdcSource::get_color_order()    - Get an old-style color order value.
+//   ppdcSource::get_color_profile()  - Get a color profile definition.
+//   ppdcSource::get_color_space()    - Get an old-style colorspace value.
+//   ppdcSource::get_constraint()     - Get a constraint.
+//   ppdcSource::get_custom_size()    - Get a custom media size definition
+//                                      from a file.
+//   ppdcSource::get_filter()         - Get a filter.
+//   ppdcSource::get_float()          - Get a single floating-point number.
+//   ppdcSource::get_font()           - Get a font definition.
+//   ppdcSource::get_generic()        - Get a generic old-style option.
+//   ppdcSource::get_group()          - Get an option group.
+//   ppdcSource::get_installable()    - Get an installable option.
+//   ppdcSource::get_integer()        - Get an integer value from a string.
+//   ppdcSource::get_integer()        - Get an integer value from a file.
+//   ppdcSource::get_measurement()    - Get a measurement value.
+//   ppdcSource::get_option()         - Get an option definition.
+//   ppdcSource::get_resolution()     - Get an old-style resolution option.
+//   ppdcSource::get_simple_profile() - Get a simple color profile definition.
+//   ppdcSource::get_size()           - Get a media size definition from a file.
+//   ppdcSource::get_token()          - Get a token from a file.
+//   ppdcSource::get_variable()       - Get a variable definition.
+//   ppdcSource::quotef()             - Write a formatted, quoted string...
+//   ppdcSource::read_file()          - Read a driver source file.
+//   ppdcSource::scan_file()          - Scan a driver source file.
+//   ppdcSource::set_variable()       - Set a variable.
+//   ppdcSource::write_file()         - Write the current source data to a file.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <limits.h>
+#include <math.h>
+#include <unistd.h>
+#include <cups/raster.h>
+#include "data/epson.h"
+#include "data/escp.h"
+#include "data/hp.h"
+#include "data/label.h"
+#include "data/pcl.h"
+
+
+//
+// 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
+{
+  filename   = new ppdcString(f);
+  base_fonts = new ppdcArray();
+  drivers    = new ppdcArray();
+  po_files   = new ppdcArray();
+  sizes      = new ppdcArray();
+  vars       = new ppdcArray();
+
+  if (f)
+    read_file(f);
+}
+
+
+//
+// 'ppdcSource::~ppdcSource()' - Free a driver source file.
+//
+
+ppdcSource::~ppdcSource()
+{
+  delete filename;
+  delete base_fonts;
+  delete drivers;
+  delete po_files;
+  delete sizes;
+  delete vars;
+}
+
+
+//
+// '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
+
+
+  // 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...
+    strlcpy(temp, f + 1, sizeof(temp));
+    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, nlen, "%s/%s", base, f);
+    else
+      strlcpy(n, f, 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, nlen, "%s/%s", dir->value, f);
+      if (!access(n, 0))
+        return (n);
+    }
+  }
+
+  // Search the standard include directories...
+  const char *env;                     // Directory environment variable
+
+  if ((env = getenv("PPDC_INCLUDE_DIR")) == NULL)
+    env = PPDC_INCLUDE_DIR;
+
+  snprintf(n, nlen, "%s/%s", env, f);
+  if (!access(n, 0))
+    return (n);
+
+  if ((env = getenv("PPDC_PO_DIR")) == NULL)
+    env = PPDC_PO_DIR;
+
+  snprintf(n, nlen, "%s/%s", env, f);
+  if (!access(n, 0))
+    return (n);
+  else
+    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
+{
+  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 Attribute on line %d of %s!\n",
+            fp->line, fp->filename);
+    return (0);
+  }
+
+  if (!get_token(fp, selector, sizeof(selector)))
+  {
+    fprintf(stderr, "ppdc: Expected selector after Attribute on line %d of %s!\n",
+            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 Attribute on line %d of %s!\n",
+            fp->line, fp->filename);
+    return (0);
+  }
+
+//  printf("name=\"%s\", selector=\"%s\", value=\"%s\"\n", name, selector, value);
+
+  return (new ppdcAttr(name, selector, text, value));
+}
+
+
+//
+// '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, g, d, 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 (!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"))
+  {
+    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"));
+  }
+  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 ++;
+
+    strcpy(program, ptr);
+  }
+  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[256];           // 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);
+    d->add_group(g);
+  }
+
+  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.
+//
+
+int                                    // O - Integer value
+ppdcSource::get_integer(const char *v) // I - Value string
+{
+  int  val;                            // Value
+  int  temp;                           // Temporary value
+  char *newv;                          // New value string pointer
+
+
+  // Parse the value string...
+  if (!v)
+    return (-1);
+
+  if (isdigit(*v) || *v == '-' || *v == '+')
+  {
+    // Return a simple integer value
+    val = strtol(v, (char **)&v, 0);
+    if (*v || val == LONG_MIN)
+      return (-1);
+    else
+      return (val);
+  }
+  else if (*v == '(')
+  {
+    // Return the bitwise OR of each integer in parenthesis...
+    v ++;
+    val = 0;
+
+    while (*v && *v != ')')
+    {
+      temp = strtol(v, &newv, 0);
+
+      if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') ||
+          temp == LONG_MIN)
+        return (-1);
+
+      val |= temp;
+      v   = newv;
+    }
+
+    if (*v == ')')
+      return (val);
+    else
+      return (-1);
+  }
+  else
+    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
+
+
+  // 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(name)) == NULL)
+  {
+    // Nope, add a new one...
+    o = new ppdcOption(ot, name, text, section, order);
+    g->add_option(o);
+  }
+  else if (o->type != ot)
+  {
+//    printf("o=%p, o->type=%d, o->name=%s, ot=%d, name=%s\n",
+//           o, o->type, o->name->value, ot, name);
+    fprintf(stderr, "ppdc: Option already defined with a different type on line %d of %s!\n",
+            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...
+  strlcpy(basedir, fp->filename, sizeof(basedir));
+
+  if ((baseptr = strrchr(basedir, '/')) != NULL)
+    *baseptr = '\0';
+  else
+    strcpy(basedir, ".");
+
+  // Find the po file...
+  if (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...
+    delete cat->filename;
+    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 0 :
+        fprintf(stderr, "ppdc: Bad resolution name \"%s\" on line %d of %s!\n",
+               name, fp->line, fp->filename);
+        break;
+    case 1 :
+        ydpi = xdpi;
+       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) - (commptr - command),
+             "/cupsColorOrder %d", color_order);
+    commptr += strlen(commptr);
+  }
+
+  if (color_space >= 0)
+  {
+    snprintf(commptr, sizeof(command) - (commptr - command),
+             "/cupsColorSpace %d", color_space);
+    commptr += strlen(commptr);
+  }
+
+  if (compression >= 0)
+  {
+    snprintf(commptr, sizeof(command) - (commptr - command),
+             "/cupsCompression %d", compression);
+    commptr += strlen(commptr);
+  }
+
+  snprintf(commptr, sizeof(command) - (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, g, kd, 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++ = fp->get();
+      }
+
+      if (nameptr == name)
+      {
+        // Just substitute this character...
+       if (ch == '$')
+       {
+         // $$ = $
+         if (bufptr < bufend)
+           *bufptr++ = 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, bufend - bufptr);
+         bufptr += strlen(var->value->value);
+       }
+       else
+       {
+         fprintf(stderr, "ppdc: Undefined variable (%s) on line %d of %s.\n",
+                 name, fp->line, fp->filename);
+         snprintf(bufptr, bufend - bufptr + 1, "$%s", name);
+         bufptr += strlen(name) + 1;
+       }
+      }
+    }
+    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++ = 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++ = 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++ = ch;
+    }
+    else if ((ch == ')' && quote == '(') || (ch == '>' && quote == '<'))
+    {
+      quote = 0;
+
+      if (bufptr < bufend)
+       *bufptr++ = ch;
+    }
+    else if (ch == '\\')
+    {
+      empty = 0;
+
+      if ((ch = fp->get()) == EOF)
+        break;
+
+      if (bufptr < bufend)
+        *bufptr++ = ch;
+    }
+    else if (bufptr < bufend)
+    {
+      empty = 0;
+
+      *bufptr++ = 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';
+//    puts(buffer);
+    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++;
+
+      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;
+
+           strncpy(tformat, bufformat, 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;
+
+           strncpy(tformat, bufformat, format - bufformat);
+           tformat[format - bufformat] = '\0';
+
+           bytes += cupsFilePrintf(fp, tformat, va_arg(ap, int));
+           break;
+           
+       case 'p' : // Pointer value
+           if ((format - bufformat + 1) > (int)sizeof(tformat))
+             break;
+
+           strncpy(tformat, bufformat, 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 *), width);
+             bytes += width;
+           }
+           break;
+
+       case 's' : // String
+           if ((s = va_arg(ap, char *)) == NULL)
+             s = (char *)"(nil)";
+
+           slen = 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
+{
+  ppdcFile *fp = new ppdcFile(f);
+  scan_file(fp);
+  delete fp;
+}
+
+
+//
+// '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
+               *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;
+  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, "#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
+
+
+      // 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;
+      }
+
+      // Figure out the current directory...
+      strlcpy(basedir, fp->filename, sizeof(basedir));
+
+      if ((baseptr = strrchr(basedir, '/')) != NULL)
+        *baseptr = '\0';
+      else
+        strcpy(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;
+      }
+      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)
+        sizes->add(m);
+    }
+    else if (!strcasecmp(temp, "#po"))
+    {
+      ppdcCatalog      *cat;           // Message catalog
+
+
+      // Get a message catalog...
+      cat = get_po(fp);
+      if (cat)
+        po_files->add(cat);
+    }
+    else if (!strcasecmp(temp, "Attribute"))
+    {
+      ppdcAttr *a;                     // Attribute
+
+
+      // Get an attribute...
+      a = get_attr(fp);
+      if (a)
+        d->add_attr(a);
+    }
+    else if (!strcasecmp(temp, "Choice"))
+    {
+      // Get a choice...
+      if (!o)
+      {
+        fprintf(stderr, "ppdc: Choice found on line %d of %s with no Option!\n",
+               fp->line, fp->filename);
+        break;
+      }
+
+      c = get_choice(fp);
+      if (!c)
+        break;
+
+      // Add it to the current option...
+      o->add_choice(c);
+
+      if (isdefault)
+        o->set_defchoice(c);
+    }
+    else if (!strcasecmp(temp, "ColorDevice"))
+    {
+      // ColorDevice boolean
+      d->color_device = get_boolean(fp);
+    }
+    else if (!strcasecmp(temp, "ColorModel"))
+    {
+      // Get the color model
+      c = get_color_model(fp);
+      if (!c)
+        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)
+        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;
+      }
+
+      // 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 (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)
+        continue;
+
+      if ((o = d->find_option("CutMedia")) == NULL)
+      {
+        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;
+
+      // Add the choice to the cupsDarkness option...
+      if ((o = d->find_option("cupsDarkness")) == NULL)
+      {
+       // Create the cupsDarkness option...
+       o = new ppdcOption(PPDC_PICKONE, "cupsDarkness", "Darkness", 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, "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;
+      }
+
+      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)
+        d->filters->add(f);
+    }
+    else if (!strcasecmp(temp, "Finishing"))
+    {
+      // Get the finishing choice...
+      c = get_generic(fp, "Finishing", "OutputType", NULL);
+      if (!c)
+        continue;
+
+      // Add the choice to the cupsFinishing option...
+      if ((o = d->find_option("cupsFinishing")) == NULL)
+      {
+       // Create the cupsFinishing option...
+       o = new ppdcOption(PPDC_PICKONE, "cupsFinishing", "Finishing", 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, "Font") ||
+             !strcasecmp(temp, "#font"))
+    {
+      ppdcFont *f;                     // Font
+
+
+      // Get a font...
+      f = get_font(fp);
+      if (f)
+      {
+       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...
+      g = get_group(fp, d);
+      if (!g)
+        break;
+    }
+    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;
+
+      // Add the choice to the InputSlot option...
+      if ((o = d->find_option("InputSlot")) == NULL)
+      {
+       // Create the InputSlot option...
+       o = new ppdcOption(PPDC_PICKONE, "InputSlot", "Media Source",
+                          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, "Installable"))
+    {
+      // Get the installable option...
+      o = get_installable(fp);
+
+      // Add it as needed...
+      if (o)
+      {
+        install->add_option(o);
+        o = NULL;
+      }
+    }
+    else if (!strcasecmp(temp, "ManualCopies"))
+    {
+      // ManualCopies boolean
+      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;
+      }
+
+      d->set_manufacturer(name);
+    }
+    else if (!strcasecmp(temp, "MaxSize"))
+    {
+      // MaxSize width length
+      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;
+      }
+
+      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;
+
+      // Add the choice to the MediaType option...
+      if ((o = d->find_option("MediaType")) == NULL)
+      {
+       // Create the MediaType option...
+       o = new ppdcOption(PPDC_PICKONE, "MediaType", "Media Type",
+                          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, "MinSize"))
+    {
+      // MinSize width length
+      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;
+      }
+
+      d->set_model_name(name);
+    }
+    else if (!strcasecmp(temp, "ModelNumber"))
+    {
+      // ModelNumber number
+      d->model_number = get_integer(fp);
+    }
+    else if (!strcasecmp(temp, "Option"))
+    {
+      // Get an option...
+      o = get_option(fp, d, g);
+      if (!o)
+        break;
+    }
+    else if (!strcasecmp(temp, "PCFileName"))
+    {
+      // PCFileName name
+      char     name[256];              // Model name 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;
+      }
+
+      d->set_pc_file_name(name);
+    }
+    else if (!strcasecmp(temp, "Resolution"))
+    {
+      // Get the resolution choice...
+      c = get_resolution(fp);
+      if (!c)
+        continue;
+
+      // Add the choice to the Resolution option...
+      if ((o = d->find_option("Resolution")) == NULL)
+      {
+       // Create the Resolution option...
+       o = new ppdcOption(PPDC_PICKONE, "Resolution", NULL, 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, "SimpleColorProfile"))
+    {
+      ppdcProfile      *p;             // Color profile
+
+
+      // Get the color profile...
+      p = get_simple_profile(fp);
+
+      if (p)
+        d->profiles->add(p);
+    }
+    else if (!strcasecmp(temp, "Throughput"))
+    {
+      // Throughput number
+      d->throughput = get_integer(fp);
+    }
+    else if (!strcasecmp(temp, "UIConstraints"))
+    {
+      ppdcConstraint   *con;           // Constraint
+
+
+      con = get_constraint(fp);
+
+      if (con)
+        d->constraints->add(con);
+    }
+    else if (!strcasecmp(temp, "VariablePaperSize"))
+    {
+      // VariablePaperSize boolean
+      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;
+      }
+
+      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);
+    }
+  }
+}
+
+
+//
+// '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, "// CUPS PPD Compiler " DDK_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->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->pc_file_name->value)
+      quotef(fp, "  PCFileName \"%s\"\n", d->pc_file_name->value);
+    if (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_ESCP :
+           cupsFilePuts(fp, "  ModelNumber (");
+
+           if (d->model_number & ESCP_DOTMATRIX)
+             cupsFilePuts(fp, " $ESCP_DOTMATRIX");
+           if (d->model_number & ESCP_MICROWEAVE)
+             cupsFilePuts(fp, " $ESCP_MICROWEAVE");
+           if (d->model_number & ESCP_STAGGER)
+             cupsFilePuts(fp, " $ESCP_STAGGER");
+           if (d->model_number & ESCP_ESCK)
+             cupsFilePuts(fp, " $ESCP_ESCK");
+           if (d->model_number & ESCP_EXT_UNITS)
+             cupsFilePuts(fp, " $ESCP_EXT_UNITS");
+           if (d->model_number & ESCP_EXT_MARGINS)
+             cupsFilePuts(fp, " $ESCP_EXT_MARGINS");
+           if (d->model_number & ESCP_USB)
+             cupsFilePuts(fp, " $ESCP_USB");
+           if (d->model_number & ESCP_PAGE_SIZE)
+             cupsFilePuts(fp, " $ESCP_PAGE_SIZE");
+           if (d->model_number & ESCP_RASTER_ESCI)
+             cupsFilePuts(fp, " $ESCP_RASTER_ESCI");
+           if (d->model_number & ESCP_REMOTE)
+             cupsFilePuts(fp, " $ESCP_REMOTE");
+
+           cupsFilePuts(fp, ")\n");
+           break;
+
+       case PPDC_DRIVER_PCL :
+           cupsFilePuts(fp, "  ModelNumber (");
+
+           if (d->model_number & PCL_PAPER_SIZE)
+             cupsFilePuts(fp, " $PCL_PAPER_SIZE");
+           if (d->model_number & PCL_INKJET)
+             cupsFilePuts(fp, " $PCL_INKJET");
+           if (d->model_number & PCL_RASTER_END_COLOR)
+             cupsFilePuts(fp, " $PCL_RASTER_END_COLOR");
+           if (d->model_number & PCL_RASTER_CID)
+             cupsFilePuts(fp, " $PCL_RASTER_CID");
+           if (d->model_number & PCL_RASTER_CRD)
+             cupsFilePuts(fp, " $PCL_RASTER_CRD");
+           if (d->model_number & PCL_RASTER_SIMPLE)
+             cupsFilePuts(fp, " $PCL_RASTER_SIMPLE");
+           if (d->model_number & PCL_RASTER_RGB24)
+             cupsFilePuts(fp, " $PCL_RASTER_RGB24");
+           if (d->model_number & PCL_PJL)
+             cupsFilePuts(fp, " $PCL_PJL");
+           if (d->model_number & PCL_PJL_PAPERWIDTH)
+             cupsFilePuts(fp, " $PCL_PJL_PAPERWIDTH");
+           if (d->model_number & PCL_PJL_HPGL2)
+             cupsFilePuts(fp, " $PCL_PJL_HPGL2");
+           if (d->model_number & PCL_PJL_PCL3GUI)
+             cupsFilePuts(fp, " $PCL_PJL_PCL3GUI");
+           if (d->model_number & PCL_PJL_RESOLUTION)
+             cupsFilePuts(fp, " $PCL_PJL_RESOLUTION");
+
+           cupsFilePuts(fp, ")\n");
+           break;
+
+       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);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-string.cxx b/ppdc/ppdc-string.cxx
new file mode 100644 (file)
index 0000000..9b55d58
--- /dev/null
@@ -0,0 +1,58 @@
+//
+// "$Id$"
+//
+//   Shared string class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcString::ppdcString()  - Create a shared string.
+//   ppdcString::~ppdcString() - Destroy a shared string.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcString::ppdcString()' - Create a shared string.
+//
+
+ppdcString::ppdcString(const char *v)  // I - String
+  : ppdcShared()
+{
+  if (v)
+  {
+    value = new char[strlen(v) + 1];
+    strcpy(value, v);
+  }
+  else
+    value = 0;
+}
+
+
+//
+// 'ppdcString::~ppdcString()' - Destroy a shared string.
+//
+
+ppdcString::~ppdcString()
+{
+  if (value)
+    delete[] value;
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc-variable.cxx b/ppdc/ppdc-variable.cxx
new file mode 100644 (file)
index 0000000..6cea762
--- /dev/null
@@ -0,0 +1,66 @@
+//
+// "$Id$"
+//
+//   Variable class for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   ppdcVariable::ppdcVariable()  - Create a variable.
+//   ppdcVariable::~ppdcVariable() - Destroy a variable.
+//   ppdcVariable::set_value()     - Set the value of a variable.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+
+
+//
+// 'ppdcVariable::ppdcVariable()' - Create a variable.
+//
+
+ppdcVariable::ppdcVariable(const char *n,      // I - Name of variable
+                           const char *v)      // I - Value of variable
+{
+  name  = new ppdcString(n);
+  value = new ppdcString(v);
+}
+
+
+//
+// 'ppdcVariable::~ppdcVariable()' - Destroy a variable.
+//
+
+ppdcVariable::~ppdcVariable()
+{
+  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);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc.cxx b/ppdc/ppdc.cxx
new file mode 100644 (file)
index 0000000..02852ca
--- /dev/null
@@ -0,0 +1,328 @@
+//
+// "$Id$"
+//
+//   PPD file compiler main entry for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2007 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   main()  - Main entry for the PPD compiler.
+//   usage() - Show usage and exit.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+//
+// 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
+                       pcfilename[1024],
+                                       // Lowercase pcfilename
+                       filename[1024]; // PPD filename
+  int                  verbose;        // Verbosity
+  ppdcArray            *locales;       // List of locales
+  int                  comp;           // Compress
+  ppdcLineEnding       le;             // Line ending to use
+
+
+  // Scan the command-line...
+  catalog = NULL;
+  outdir  = "ppd";
+  src     = new ppdcSource();
+  verbose = 0;
+  locales = NULL;
+  comp    = 0;
+  le      = PPDC_LFONLY;
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+         case 'c' :                    // Message catalog...
+             i ++;
+              if (i >= argc)
+                usage();
+
+              if (verbose > 1)
+               printf("ppdc: Loading messages from \"%s\"...\n", argv[i]);
+
+              if (!catalog)
+               catalog = new ppdcCatalog("en");
+
+              if (catalog->load_messages(argv[i]))
+             {
+               fprintf(stderr,
+                       "ppdc: Unable to load localization file \"%s\" - %s\n",
+                       argv[i], strerror(errno));
+                return (1);
+             }
+             break;
+
+          case 'd' :                   // Output directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (verbose > 1)
+               printf("ppdc: Writing PPD files to directory \"%s\"...\n",
+                      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();
+
+               strlcpy(temp, argv[i], sizeof(temp));
+               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
+             {
+               if (verbose > 1)
+                 printf("ppdc: Loading messages for locale \"%s\"...\n",
+                        argv[i]);
+
+               if (catalog)
+                 delete catalog;
+
+               catalog = new ppdcCatalog(argv[i]);
+
+               if (catalog->messages->count == 0)
+               {
+                 fprintf(stderr,
+                         "ppdc: Unable to find localization for \"%s\" - %s\n",
+                         argv[i], strerror(errno));
+                  return (1);
+               }
+             }
+             break;
+
+          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)
+               printf("ppdc: Adding include directory \"%s\"...\n", argv[i]);
+
+             ppdcSource::add_include(argv[i]);
+             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();
+             break;
+       }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      if (verbose > 1)
+        printf("ppdc: Loading driver information file \"%s\"...\n", 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, "ppdc: Unable to create output directory %s: %s\n",
+               outdir, strerror(errno));
+        return (1);
+      }
+    }
+
+    // Write PPD files...
+    for (d = (ppdcDriver *)src->drivers->first();
+         d;
+        d = (ppdcDriver *)src->drivers->next())
+    {
+      // Write the PPD file for this driver...
+      if (strstr(d->pc_file_name->value, ".PPD"))
+      {
+       // Convert PCFileName to lowercase...
+       for (j = 0;
+            d->pc_file_name->value[j] && j < (int)(sizeof(pcfilename) - 1);
+            j ++)
+         pcfilename[j] = tolower(d->pc_file_name->value[j]);
+
+       pcfilename[j] = '\0';
+      }
+      else
+      {
+       // Leave PCFileName as-is...
+       strlcpy(pcfilename, d->pc_file_name->value, 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);
+
+      fp = cupsFileOpen(filename, comp ? "w9" : "w");
+      if (!fp)
+      {
+       fprintf(stderr, "ppdc: Unable to create PPD file \"%s\" - %s.\n",
+               filename, strerror(errno));
+       return (1);
+      }
+
+      if (verbose)
+       printf("ppdc: Writing %s...\n", filename);
+
+      if (d->write_ppd_file(fp, catalog, locales, src, le))
+      {
+       cupsFileClose(fp);
+       return (1);
+      }
+
+      cupsFileClose(fp);
+    }
+  }
+  else
+    usage();
+
+  // Delete the printer driver information...
+  delete src;
+
+  // Message catalog...
+  if (catalog)
+    delete catalog;
+
+  // Return with no errors.
+  return (0);
+}
+
+
+//
+// 'usage()' - Show usage and exit.
+//
+
+static void
+usage(void)
+{
+  puts("Usage: ppdc [options] filename.drv [ ... filenameN.drv ]");
+  puts("Options:");
+  puts("  -D name=value        Set named variable to value.");
+  puts("  -I include-dir       Add include directory to search path.");
+  puts("  -c catalog.po        Load the specified message catalog.");
+  puts("  -d output-dir        Specify the output directory.");
+  puts("  -l lang[,lang,...]   Specify the output language(s) (locale).");
+  puts("  -v                   Be verbose (more v's for more verbosity).");
+  puts("  -z                   Compress PPD files using GNU zip.");
+  puts("  --cr                 End lines with CR (Mac OS 9).");
+  puts("  --crlf               End lines with CR + LF (Windows).");
+  puts("  --lf                 End lines with LF (UNIX/Linux/Mac OS X).");
+
+  exit(1);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdc.h b/ppdc/ppdc.h
new file mode 100644 (file)
index 0000000..ab67a36
--- /dev/null
@@ -0,0 +1,477 @@
+//
+// "$Id$"
+//
+//   Definitions for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2007 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+
+#ifndef _PPDC_H_
+#  define _PPDC_H_
+
+//
+// Include necessary headers...
+//
+
+#  include <cups/string.h>
+
+#  include <cups/file.h>
+#  include <stdlib.h>
+#  include <errno.h>
+
+
+//
+// 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
+};
+
+
+//
+// Printer description data...
+//
+
+class ppdcShared                       //// Shared Data Value
+{
+  private:
+
+  int          use;                    // Use count (delete when 0)
+
+  public:
+
+  ppdcShared();
+  virtual ~ppdcShared();
+
+  void         get(void);
+  void         release(void);
+};
+
+class ppdcArray                                //// Shared Array
+  : public ppdcShared
+{
+  public:
+
+  int          count,                  // Number of elements
+               alloc,                  // Allocated elements
+               current;                // Current element
+  ppdcShared   **data;                 // Elements
+
+  ppdcArray(ppdcArray *a = 0);
+  ~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();
+};
+
+class ppdcInteger                      //// Shared integer
+  : public ppdcShared
+{
+  public:
+
+  int          *value;                 // Integer value
+
+  ppdcInteger(int *v) { value = v; }
+};
+
+class ppdcMessage                      //// Translation message
+  : public ppdcShared
+{
+  public:
+
+  ppdcString   *id,                    // Translation ID
+               *string;                // Translation string
+
+  ppdcMessage(const char *i, const char *s);
+  ~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();
+
+  void         add_message(ppdcMessage *m) { messages->add(m); }
+  void         add_message(const char *id);
+  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
+
+  ppdcAttr(const char *n, const char *s, const char *t, const char *v);
+  ~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();
+};
+
+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();
+};
+
+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();
+
+  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();
+
+  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();
+};
+
+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();
+};
+
+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();
+};
+
+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();
+};
+
+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
+               *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();
+
+  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);
+
+  void         set_custom_size_code(const char *c);
+  void         set_default_font(ppdcFont *f);
+  void         set_default_size(ppdcMediaSize *m);
+  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();
+
+  void         set_value(const char *v);
+};
+
+class ppdcFile                         //// File
+{
+  public:
+
+  cups_file_t  *fp;                    // File pointer
+  const char   *filename;              // Filename
+  int          line;                   // Line in file
+
+  ppdcFile(const char *f);
+  ~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
+
+  ppdcSource(const char *f = 0);
+  ~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);
+  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);
+  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_
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdhtml.cxx b/ppdc/ppdhtml.cxx
new file mode 100644 (file)
index 0000000..dd85129
--- /dev/null
@@ -0,0 +1,164 @@
+//
+// "$Id$"
+//
+//   PPD to HTML utility for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   main()  - Main entry for the PPD to HTML utility.
+//   usage() - Show usage and exit.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+//
+// 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;                      // Looping var
+  ppdcSource   *src;                   // PPD source file data
+  ppdcDriver   *d;                     // Current driver
+  ppdcGroup    *g,                     // Current group
+               *composite;             // Composite of all drivers
+  ppdcOption   *o,                     // Current option
+               *compo;                 // Composite option
+  ppdcChoice   *c;                     // Current choice
+  char         *opt;                   // Current option char
+  ppdcMediaSize        *size;                  // Current media size
+
+
+  // Scan the command-line...
+  src = 0;
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+          case 'I' :                   // Include directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+             ppdcSource::add_include(argv[i]);
+             break;
+
+         default :                     // Unknown
+             usage();
+             break;
+       }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      src = new ppdcSource(argv[i]);
+
+      // Create a composite group with all of the features from the
+      // drivers in the info file...
+      composite = new ppdcGroup("", "");
+
+      for (d = (ppdcDriver *)src->drivers->first(); d; d = (ppdcDriver *)src->drivers->next())
+        for (g = (ppdcGroup *)d->groups->first(); g; g = (ppdcGroup *)d->groups->next())
+         for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next())
+         {
+           if ((compo = composite->find_option(o->name->value)) == NULL)
+             composite->add_option(new ppdcOption(o));
+         }
+
+      puts("<html>");
+      printf("<head><title>Driver Summary for %s</title></head>\n", argv[i]);
+      printf("<body><h1>Driver Summary for %s</h1>\n", argv[i]);
+      printf("<p><table border='1'><thead><tr><th>Printer</th><th>Media Size</th>");
+      for (compo = (ppdcOption *)composite->options->first(); compo; compo = (ppdcOption *)composite->options->next())
+        printf("<th>%s</th>", compo->text->value);
+      puts("</tr></thead><tbody>");
+
+      // Write HTML summary...
+      for (d = (ppdcDriver *)src->drivers->first(); d; d = (ppdcDriver *)src->drivers->next())
+      {
+        // Write the summary for this driver...
+       printf("<tr valign='top'><td nowrap>%s</td><td nowrap>", d->model_name->value);
+       for (size = (ppdcMediaSize *)d->sizes->first(); size;
+            size = (ppdcMediaSize *)d->sizes->next())
+          printf("%s<br>", size->text->value);
+        printf("</td>");
+
+        for (compo = (ppdcOption *)composite->options->first(); compo;
+            compo = (ppdcOption *)composite->options->next())
+         if ((o = d->find_option(compo->name->value)) != NULL)
+         {
+           printf("<td nowrap>");
+           for (c = (ppdcChoice *)o->choices->first(); c;
+                c = (ppdcChoice *)o->choices->next())
+             printf("%s<br>", c->text->value);
+           printf("</td>");
+         }
+         else
+           printf("<td>N/A</td>");
+
+        puts("</tr>");
+      }
+
+      puts("</tbody></table></p>");
+      puts("</body>");
+      puts("</html>");
+      // Delete the printer driver information...
+      delete composite;
+      delete src;
+    }
+
+  // If no drivers have been loaded, display the program usage message.
+  if (!src)
+    usage();
+
+  // Return with no errors.
+  return (0);
+}
+
+
+//
+// 'usage()' - Show usage and exit.
+//
+
+static void
+usage(void)
+{
+  puts("Usage: ppdhtml [options] filename.drv >filename.html");
+  puts("Options:");
+  puts("  -I include-dir    Add include directory to search path.");
+
+  exit(1);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdi.cxx b/ppdc/ppdi.cxx
new file mode 100644 (file)
index 0000000..2fe4a5f
--- /dev/null
@@ -0,0 +1,137 @@
+//
+// "$Id$"
+//
+//   PPD file import utility for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   main()  - Main entry for the PPD import utility.
+//   usage() - Show usage and exit.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+//
+// Local functions...
+//
+
+static void    usage(void);
+
+
+//
+// 'main()' - Main entry for the PPD import utility.
+//
+
+int                                    // O - Exit status
+main(int  argc,                                // I - Number of command-line arguments
+     char *argv[])                     // I - Command-line arguments
+{
+  int          i;                      // Looping var
+  char         *opt;                   // Current option
+  const char   *srcfile;               // Output file
+  ppdcSource   *src;                   // PPD source file data
+
+
+  // Scan the command-line...
+  srcfile = NULL;
+  src     = NULL;
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+         case 'o' :                    // Output file
+              if (srcfile || src)
+               usage();
+
+             i ++;
+             if (i >= argc)
+               usage();
+
+             srcfile = argv[i];
+             break;
+
+         case 'I' :                    // Include dir
+             i ++;
+             if (i >= argc)
+               usage();
+
+             ppdcSource::add_include(argv[i]);
+             break;
+
+         default :                     // Unknown
+             usage();
+             break;
+        }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      if (!srcfile)
+        srcfile = "ppdi.drv";
+
+      if (!src)
+      {
+        if (access(srcfile, 0))
+         src = new ppdcSource();
+       else
+          src = new ppdcSource(srcfile);
+      }
+
+      // Import the PPD file...
+      src->import_ppd(argv[i]);
+    }
+
+  // If no drivers have been loaded, display the program usage message.
+  if (!src)
+    usage();
+
+  // Write the driver info file back to disk...
+  src->write_file(srcfile);
+
+  // Delete the printer driver information...
+  delete src;
+
+  // Return with no errors.
+  return (0);
+}
+
+
+//
+// 'usage()' - Show usage and exit.
+//
+
+static void
+usage(void)
+{
+  puts("Usage: ppdi [options] filename.ppd [ ... filenameN.ppd ]");
+  puts("Options:");
+  puts("  -I include-dir");
+  puts("  -o filename.drv");
+
+  exit(1);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdmerge.cxx b/ppdc/ppdmerge.cxx
new file mode 100644 (file)
index 0000000..598b4b8
--- /dev/null
@@ -0,0 +1,358 @@
+//
+// "$Id$"
+//
+//   PPD file merge utility for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2007 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   main()       - Main entry for the PPD merge utility.
+//   ppd_locale() - Return the locale associated with a PPD file.
+//   usage()      - Show usage and exit.
+//
+
+//
+// Include necessary headers...
+//
+
+#include <cups/cups.h>
+#include <cups/array.h>
+#include <cups/string.h>
+#include <errno.h>
+
+
+//
+// Local functions...
+//
+
+static const char      *ppd_locale(ppd_file_t *ppd);
+static void            usage(void);
+
+
+//
+// 'main()' - Main entry for the PPD merge utility.
+//
+
+int                                    // O - Exit status
+main(int  argc,                                // I - Number of command-line arguments
+     char *argv[])                     // I - Command-line arguments
+{
+  int          i;                      // Looping var
+  char         *opt;                   // Current option
+  ppd_file_t   *ppd;                   // PPD file
+  cups_array_t *ppds;                  // Array of PPD files
+  const char   *inname,                // First input filename
+               *outname;               // Output filename (if any)
+  cups_file_t  *infile,                // Input file
+               *outfile;               // Output file
+  char         languages[1024];        // Languages in file
+
+
+  // Scan the command-line...
+  inname       = NULL;
+  outname      = NULL;
+  outfile      = NULL;
+  languages[0] = '\0';
+  ppds         = cupsArrayNew(NULL, NULL);
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+         case 'o' :                    // Output file
+              if (outname)
+               usage();
+
+             i ++;
+             if (i >= argc)
+               usage();
+
+             outname = argv[i];
+             break;
+
+         default :                     // Unknown
+             usage();
+             break;
+        }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      if ((infile = cupsFileOpen(argv[i], "r")) == NULL)
+      {
+        fprintf(stderr, "ppdmerge: Unable to open %s - %s\n", argv[i],
+               strerror(errno));
+       goto error;
+      }
+
+      // Open the PPD file...
+      if ((ppd = ppdOpen2(infile)) == NULL)
+      {
+        ppd_status_t   status;         // PPD open status
+       int             linenum;        // Line number
+        char           line[1024];     // First line from file
+       
+       
+        status = ppdLastError(&linenum);
+       
+       fprintf(stderr, "ppdmerge: Unable to open %s - %s on line %d.\n",
+               argv[i], ppdErrorString(status), linenum);
+       
+        fprintf(stderr, "%d: ", linenum);
+        cupsFileRewind(infile);
+       
+        line[0] = '\0';
+       
+        while (cupsFileGets(infile, line, sizeof(line)))
+       {
+         linenum --;
+         if (!linenum)
+           break;
+       }
+       
+       fprintf(stderr, "%s\n", line);
+       
+        cupsFileClose(infile);
+
+        goto error;
+      }
+      
+      // Figure out the locale...
+      const char *locale = ppd_locale(ppd);
+
+      if (!locale)
+      {
+        fprintf(stderr, "ppdmerge: Bad LanguageVersion \"%s\" in %s!\n",
+               ppd->lang_version, argv[i]);
+        cupsFileClose(infile);
+       ppdClose(ppd);
+
+       goto error;
+      }
+      
+      if (!strcmp(locale, "en") && !inname && !outfile)
+      {
+        // Set the English PPD's filename...
+        inname = argv[i];
+       
+        if (outname && !strcmp(inname, outname))
+       {       
+         // Rename input filename so that we don't overwrite it...
+         char bckname[1024];           // Backup filename
+         
+         
+         snprintf(bckname, sizeof(bckname), "%s.bck", inname);
+         
+         if (rename(inname, bckname))
+         {
+           fprintf(stderr, "ppdmerge: Unable to backup %s to %s- %s\n",
+                   inname, bckname, strerror(errno));
+           return (1);
+         }
+       }
+
+        // Open the output stream...
+       if (outname)
+       {
+         const char *ext = strrchr(outname, '.');
+         if (ext && !strcmp(ext, ".ppd.gz"))
+           outfile = cupsFileOpen(outname, "w9");
+         else
+           outfile = cupsFileOpen(outname, "w");
+       }
+       else
+         outfile = cupsFileStdout();
+
+        // Copy the PPD file to the output stream...
+       char line[1024];                // Line from file
+       
+       cupsFileRewind(infile);
+
+       while (cupsFileGets(infile, line, sizeof(line)))
+       {
+         if (!strncmp(line, "*cupsLanguages:", 15))
+         {
+           if (sscanf(line, "*cupsLanguages:%*[ \t]\"%1023[^\"]",
+                      languages) != 1)
+             languages[0] = '\0';
+         }
+         else
+           cupsFilePrintf(outfile, "%s\n", line);
+       }
+      }
+      else if (strcmp(locale, "en") && !strstr(languages, locale))
+      {
+       // Save this PPD for later processing...
+        cupsArrayAdd(ppds, ppd);
+      }
+      else
+      {
+        // Don't need this PPD...
+       fprintf(stderr, "ppdmerge: Ignoring PPD file %s...\n", argv[i]);
+        ppdClose(ppd);
+      }
+      
+      // Close and move on...
+      cupsFileClose(infile);
+    }
+
+  // If no PPDs have been loaded, display the program usage message.
+  if (!inname)
+    usage();
+      
+  // Loop through the PPD files we loaded to provide the translations...
+  for (ppd = (ppd_file_t *)cupsArrayFirst(ppds);
+       ppd;
+       ppd = (ppd_file_t *)cupsArrayNext(ppds))
+  {
+    // Output all of the UI text for this language...
+    int                        j, k, l;        // Looping vars
+    ppd_group_t                *g;             // Option group
+    ppd_option_t       *o;             // Option
+    ppd_choice_t       *c;             // Choice
+    const char         *locale = ppd_locale(ppd);
+                                       // Locale
+
+    
+    cupsFilePrintf(outfile, "*%% %s localization\n", ppd->lang_version);
+    
+    cupsFilePrintf(outfile, "*%s.Translation ModelName/%s: \"\"\n", locale,
+                  ppd->modelname);
+    
+    for (j = ppd->num_groups, g = ppd->groups; j > 0; j --, g ++)
+    {
+      cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale,
+                    g->name, g->text);
+      
+      for (k = g->num_options, o = g->options; k > 0; k --, o ++)
+      {
+       cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale,
+                      o->keyword, o->text);
+       
+       for (l = o->num_choices, c = o->choices; l > 0; l --, c ++)
+         cupsFilePrintf(outfile, "*%s.%s %s/%s: \"\"\n", locale,
+                        o->keyword, c->choice, c->text);
+      }
+    }
+    
+    if (languages[0])
+      strlcat(languages, " ", sizeof(languages));
+    
+    strlcat(languages, locale, sizeof(languages));
+
+    ppdClose(ppd);
+  }
+
+  cupsArrayDelete(ppds);
+
+  if (languages[0])
+    cupsFilePrintf(outfile, "*cupsLanguages: \"%s\"\n", languages);
+
+  cupsFileClose(outfile);
+
+  // Return with no errors.
+  return (0);
+
+  // Error out...
+error:
+
+  for (ppd = (ppd_file_t *)cupsArrayFirst(ppds);
+       ppd;
+       ppd = (ppd_file_t *)cupsArrayNext(ppds))
+    ppdClose(ppd);
+
+  cupsArrayDelete(ppds);
+
+  if (outfile)
+    cupsFileClose(outfile);
+
+  return (1);
+}
+
+
+//
+// 'ppd_locale()' - Return the locale associated with a PPD file.
+//
+
+static const char *                    // O - Locale string
+ppd_locale(ppd_file_t *ppd)            // I - PPD file
+{
+  int          i,                      // Looping var
+               vlen;                   // Length of LanguageVersion string
+  static char  locale[255];            // Locale string
+  static struct                                // LanguageVersion translation table
+  {
+    const char *version,               // LanguageVersion string */
+               *language;              // Language code */
+  }            languages[] =
+  {
+    { "chinese",       "zh" },
+    { "danish",                "da" },
+    { "dutch",         "nl" },
+    { "english",       "en" },
+    { "finnish",       "fi" },
+    { "french",                "fr" },
+    { "german",                "de" },
+    { "greek",         "el" },
+    { "italian",       "it" },
+    { "japanese",      "ja" },
+    { "korean",                "ko" },
+    { "norwegian",     "no" },
+    { "polish",                "pl" },
+    { "portuguese",    "pt" },
+    { "russian",       "ru" },
+    { "slovak",                "sk" },
+    { "spanish",       "es" },
+    { "swedish",       "sv" },
+    { "turkish",       "tr" }
+  };
+
+
+  for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
+  {
+    vlen = strlen(languages[i].version);
+
+    if (!strncasecmp(ppd->lang_version, languages[i].version, vlen))
+    {
+      if (ppd->lang_version[vlen] == '-' ||
+          ppd->lang_version[vlen] == '_')
+        snprintf(locale, sizeof(locale), "%s_%s", languages[i].language,
+                ppd->lang_version + vlen + 1);
+      else
+        strlcpy(locale, languages[i].language, sizeof(locale));
+
+      return (locale);
+    }
+  }
+
+  return (NULL);
+}
+
+//
+// 'usage()' - Show usage and exit.
+//
+
+static void
+usage(void)
+{
+  puts("Usage: ppdmerge [options] filename.ppd [ ... filenameN.ppd ]");
+  puts("Options:");
+  puts("  -o filename.ppd[.gz]");
+
+  exit(1);
+}
+
+
+//
+// End of "$Id$".
+//
diff --git a/ppdc/ppdpo.cxx b/ppdc/ppdpo.cxx
new file mode 100644 (file)
index 0000000..0ee273e
--- /dev/null
@@ -0,0 +1,232 @@
+//
+// "$Id$"
+//
+//   PPD file message catalog program for the CUPS PPD Compiler.
+//
+//   Copyright 2007 by Apple Inc.
+//   Copyright 2002-2005 by Easy Software Products.
+//
+//   These coded instructions, statements, and computer programs are the
+//   property of Apple Inc. and are protected by Federal copyright
+//   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+//   which should have been included with this file.  If this file is
+//   file is missing or damaged, see the license at "http://www.cups.org/".
+//
+// Contents:
+//
+//   main()           - Main entry for the PPD compiler.
+//   add_ui_strings() - Add all UI strings from the driver.
+//   usage()          - Show usage and exit.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+//
+// Local functions...
+//
+
+static void    add_ui_strings(ppdcDriver *d, ppdcCatalog *catalog);
+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;                      // Looping var
+  ppdcCatalog  *catalog;               // Message catalog
+  ppdcSource   *src;                   // PPD source file data
+  ppdcDriver   *d;                     // Current driver
+  char         *opt;                   // Current option
+  int          verbose;                // Verbosity
+  const char   *outfile;               // Output file
+
+
+  // Scan the command-line...
+  catalog = new ppdcCatalog("en");
+  src     = 0;
+  verbose = 0;
+  outfile = 0;
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+          case 'I' :                   // Include directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (verbose > 1)
+               printf("ppdc: Adding include directory \"%s\"...\n", argv[i]);
+
+             ppdcSource::add_include(argv[i]);
+             break;
+
+          case 'o' :                   // Output file...
+             i ++;
+             if (i >= argc || outfile)
+               usage();
+
+              outfile = argv[i];
+
+             catalog->load_messages(outfile);
+             break;
+
+          case 'v' :                   // Be verbose...
+             verbose ++;
+             break;
+
+         default :                     // Unknown
+             usage();
+             break;
+       }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      if (verbose > 1)
+        printf("ppdc: Loading driver information file \"%s\"...\n", argv[i]);
+
+      src = new ppdcSource(argv[i]);
+
+      // Add UI strings...
+      for (d = (ppdcDriver *)src->drivers->first();
+           d;
+          d = (ppdcDriver *)src->drivers->next())
+      {
+       if (verbose)
+         printf("ppdc: Adding/updating UI text from %s...\n", argv[i]);
+
+        add_ui_strings(d, catalog);
+      }
+
+      // Delete the printer driver information...
+      delete src;
+    }
+
+  // Write the message catalog...
+  if (!outfile)
+    usage();
+  else
+    catalog->save_messages(outfile);
+
+  delete catalog;
+
+  // If no drivers have been loaded, display the program usage message.
+  if (!src)
+    usage();
+
+  // Return with no errors.
+  return (0);
+}
+
+
+//
+// 'add_ui_strings()' - Add all UI strings from the driver.
+//
+
+static void
+add_ui_strings(ppdcDriver  *d,         // I - Driver data
+               ppdcCatalog *catalog)   // I - Message catalog
+{
+  // Add the make/model/language strings...
+  catalog->add_message(d->manufacturer->value);
+  catalog->add_message(d->model_name->value);
+
+  // Add the media size strings...
+  ppdcMediaSize        *m;                     // Current media size
+
+  for (m = (ppdcMediaSize *)d->sizes->first();
+       m;
+       m = (ppdcMediaSize *)d->sizes->next())
+    catalog->add_message(m->text->value);
+
+  // Add the group/option/choice strings...
+  ppdcGroup    *g;                     // Current group
+  ppdcOption   *o;                     // Current option
+  ppdcChoice   *c;                     // Current choice
+
+  for (g = (ppdcGroup *)d->groups->first();
+       g;
+       g = (ppdcGroup *)d->groups->next())
+  {
+    if (!g->options->count)
+      continue;
+
+    if (strcasecmp(g->name->value, "General"))
+      catalog->add_message(g->text->value);
+
+    for (o = (ppdcOption *)g->options->first();
+         o;
+        o = (ppdcOption *)g->options->next())
+    {
+      if (!o->choices->count)
+        continue;
+
+      if (o->text->value && strcmp(o->name->value, o->text->value))
+        catalog->add_message(o->text->value);
+      else
+        catalog->add_message(o->name->value);
+
+      for (c = (ppdcChoice *)o->choices->first();
+           c;
+          c = (ppdcChoice *)o->choices->next())
+       if (c->text->value && strcmp(c->name->value, c->text->value))
+          catalog->add_message(c->text->value);
+        else
+          catalog->add_message(c->name->value);
+    }
+  }
+
+  // Add profile and preset strings...
+  ppdcAttr *a;                         // Current attribute
+  for (a = (ppdcAttr *)d->attrs->first();
+       a;
+       a = (ppdcAttr *)d->attrs->next())
+    if (a->text->value && a->text->value[0] &&
+        (!strncmp(a->name->value, "Custom", 6) ||
+         !strncmp(a->name->value, "ParamCustom", 11) ||
+         !strcmp(a->name->value, "APCustomColorMatchingName") ||
+         !strcmp(a->name->value, "APPrinterPreset") ||
+         !strcmp(a->name->value, "cupsICCProfile") ||
+         !strcmp(a->name->value, "cupsIPPReason")))
+      catalog->add_message(a->text->value);
+    else if (!strncmp(a->name->value, "Custom", 6) ||
+             !strncmp(a->name->value, "ParamCustom", 11))
+      catalog->add_message(a->name->value);
+}
+
+
+//
+// 'usage()' - Show usage and exit.
+//
+
+static void
+usage(void)
+{
+  puts("Usage: ppdpo [options] -o filename.po filename.drv [ ... filenameN.drv ]");
+  puts("Options:");
+  puts("  -I include-dir    Add include directory to search path.");
+  puts("  -v                Be verbose (more v's for more verbosity).");
+
+  exit(1);
+}
+
+
+//
+// End of "$Id$".
+//