]> git.ipfire.org Git - thirdparty/cups-filters.git/commitdiff
Added the ppdc utilities from CUPS, updated ppd/README.md
authorTill Kamppeter <till.kamppeter@gmail.com>
Tue, 26 Oct 2021 20:12:20 +0000 (22:12 +0200)
committerTill Kamppeter <till.kamppeter@gmail.com>
Tue, 26 Oct 2021 20:12:20 +0000 (22:12 +0200)
Also ported the utilities, for *.drv file handling in Printer
Applications.

Note that they are only installed when ./configure is called with
"--enable-ppdc-utils" to not conflict with CUPS.

Makefile.am
configure.ac
ppd/README.md
ppd/genstrings.cxx [new file with mode: 0644]
ppd/ppdc.cxx [moved from ppd/drv2ppd.cxx with 100% similarity]
ppd/ppdhtml.cxx [new file with mode: 0644]
ppd/ppdi.cxx [new file with mode: 0644]
ppd/ppdmerge.cxx [new file with mode: 0644]
ppd/ppdpo.cxx [new file with mode: 0644]

index 11f7d7ab208c6e38f241001a0ea857c7306c288b..504441c1e3a447713a95a19df8a4fcc44d29ce64 100644 (file)
@@ -257,21 +257,71 @@ EXTRA_DIST += \
        ppd/test2.ppd \
        ppd/README.md
 
-# =========================
-# ppdc PPD compiler utility
-# =========================
+# ===========================
+# ppdc PPD compiler utilities
+# ===========================
 
-if ENABLE_PPDC
+if ENABLE_PPDC_UTILS
 pkgutils_PROGRAMS += \
-       drv2ppd
+       genstrings \
+       ppdc \
+       ppdhtml \
+       ppdi \
+       ppdmerge \
+       ppdpo
 endif
 
-drv2ppd_SOURCES = \
-       ppd/drv2ppd.cxx
-drv2ppd_LDADD = \
+genstrings_SOURCES = \
+       ppd/genstrings.cxx
+genstrings_LDADD = \
        libppd.la \
        $(CUPS_LIBS)
-drv2ppd_CFLAGS = \
+genstrings_CFLAGS = \
+       -I$(srcdir)/ppd/ \
+       $(CUPS_CFLAGS)
+
+ppdc_SOURCES = \
+       ppd/ppdc.cxx
+ppdc_LDADD = \
+       libppd.la \
+       $(CUPS_LIBS)
+ppdc_CFLAGS = \
+       -I$(srcdir)/ppd/ \
+       $(CUPS_CFLAGS)
+
+ppdhtml_SOURCES = \
+       ppd/ppdhtml.cxx
+ppdhtml_LDADD = \
+       libppd.la \
+       $(CUPS_LIBS)
+ppdhtml_CFLAGS = \
+       -I$(srcdir)/ppd/ \
+       $(CUPS_CFLAGS)
+
+ppdi_SOURCES = \
+       ppd/ppdi.cxx
+ppdi_LDADD = \
+       libppd.la \
+       $(CUPS_LIBS)
+ppdi_CFLAGS = \
+       -I$(srcdir)/ppd/ \
+       $(CUPS_CFLAGS)
+
+ppdmerge_SOURCES = \
+       ppd/ppdmerge.cxx
+ppdmerge_LDADD = \
+       libppd.la \
+       $(CUPS_LIBS)
+ppdmerge_CFLAGS = \
+       -I$(srcdir)/ppd/ \
+       $(CUPS_CFLAGS)
+
+ppdpo_SOURCES = \
+       ppd/ppdpo.cxx
+ppdpo_LDADD = \
+       libppd.la \
+       $(CUPS_LIBS)
+ppdpo_CFLAGS = \
        -I$(srcdir)/ppd/ \
        $(CUPS_CFLAGS)
 
@@ -1220,9 +1270,6 @@ if ENABLE_DRIVERLESS
        $(LN_SRF) $(DESTDIR)$(pkgppdgendir)/driverless-fax $(DESTDIR)$(bindir)
        $(LN_SRF) $(DESTDIR)$(pkgppdgendir)/driverless-fax $(DESTDIR)$(pkgbackenddir)
 endif
-if ENABLE_PPDC
-       $(LN_SRF) $(DESTDIR)$(bindir)/drv2ppd $(DESTDIR)$(bindir)/ppdc
-endif
 if ENABLE_BRAILLE
        $(LN_S) -f imagetobrf $(DESTDIR)$(pkgfilterdir)/imagetoubrl
        $(LN_S) -f vectortopdf $(DESTDIR)$(pkgfilterdir)/svgtopdf
@@ -1270,9 +1317,6 @@ if ENABLE_DRIVERLESS
        $(RM) $(DESTDIR)$(bindir)/driverless-fax
        $(RM) $(DESTDIR)$(pkgbackenddir)/driverless-fax
 endif
-if ENABLE_PPDC
-       $(RM) $(DESTDIR)$(bindir)/ppdc
-endif
 if ENABLE_BRAILLE
        $(RM) $(DESTDIR)$(pkgfilterdir)/imagetoubrl
        $(RM) $(DESTDIR)$(pkgfilterdir)/svgtopdf
index 574b568e4712186c576da9683f8f12cbf2864c02..929e710c19a81282dce6e4e6b26731cac9fee78e 100644 (file)
@@ -177,19 +177,11 @@ else
        fi
 fi
 
-# Do not overwrite a ppdc which is already there from CUPS, in this case
-# default to not include a ppdc utility
-if test ! -e "$bindir/ppdc" || test -h "$bindir/ppdc"; then
-        no_cups_ppdc=yes
-else
-        no_cups_ppdc=no
-fi
-AC_ARG_ENABLE([ppdc], [AS_HELP_STRING([--enable-ppdc], [enable ppdc utility, to build PPD files from driver information files (*.drv).])],
-        [enable_ppdc="$enableval"],
-        [enable_ppdc="$no_cups_ppdc"]
-)
-AM_CONDITIONAL([ENABLE_PPDC],
-[test "x$enable_ppdc" != "xno"])
+AC_ARG_ENABLE([ppdc-utils], [AS_HELP_STRING([--enable-ppdc-utils], [enable ppdc utilities, to build PPD files from driver information files (*.drv).])],
+        [enable_ppdc_utils="$enableval"],
+        [enable_ppdc_utils="no"])
+AM_CONDITIONAL([ENABLE_PPDC_UTILS],
+[test "x$enable_ppdc_utils" != "xno"])
 
 PPDC_DATADIR="$datadir/ppdc"
 AC_DEFINE_UNQUOTED(PPDC_DATADIR, "$PPDC_DATADIR", [ppdc include dir])
@@ -1017,7 +1009,7 @@ Build configuration:
        driverless:      ${enable_driverless}
        apple-raster:    ${APPLE_RASTER_FILTER}
        pclm:            ${enable_pclm}
-       ppdc:            ${enable_ppdc}
+       ppdc utilities:  ${enable_ppdc_utils}
        local queue naming for remote CUPS queues: ${REMOTE_CUPS_LOCAL_QUEUE_NAMING}
        keep generated queues during shutdown:     ${SAVING_CREATED_QUEUES}
        all ipp printer auto-setup: ${enable_auto_setup_all}
index 5bdd3c487acab6412422032f277ad8a15565709d..9d39ca274ed0a78aa7f97329013041a800bd69a2 100644 (file)
@@ -1,6 +1,6 @@
 # libppd
 
-One important point for the CUPS Snap is that it does not support classic printer drivers, consisting of PPD files and filters. So for using it as default CUPS implementation in a Linux distribution, all existing printer drivers need to get turned into Printer Applications, and this with a minimum effort of coding.
+One important point for the CUPS Snap (and also CUPS from version 3.x on) is that it does not support classic printer drivers, consisting of PPD files and filters. So for using it as default CUPS implementation in a Linux distribution, all existing printer drivers need to get turned into Printer Applications, and this with a minimum effort of coding.
 
 Most of them (probably all except Gutenprint) are difficult to get converted by their original authors, as they do not maintain the drivers any more, supported printers are old and no one wants to code for that any more, driver is simply only a big bunch of PPD files, no code, driver is proprietary, closed source, ...
 
@@ -8,14 +8,15 @@ So we need a way to retro-fit these drivers by wrapping them into Printer Applic
 
 This works best if we use the driver's PPD files inside the Printer Application and so we need to handle PPD files, also after the PPD handling support got removed from CUPS and especially libcups.
 
-To avoid that we have to invent the wheel again, writing a lot of handling code for a totally obsolete file format, I have grabbed all the PPD handling functions from libcups (current GitHub state, CUPS 2.3.3) and put them into the new libppd which I have added to cups-filters.
+To avoid that we have to invent the wheel again, writing a lot of handling code for a totally obsolete file format, I have grabbed all the PPD handling functions from libcups and from ppdc/ (current GitHub state, CUPS 2.3.3) and put them into the new libppd which I have added to cups-filters.
 
 It has the following properties:
 - All PPD-handling-related functions from libcups (except loading the PPD from a CUPS queue or polling a PPD repository on a CUPS server) are overtaken
 - Also the CUPS-private functions related to PPDs are overtaken and added to libppd's public API.
 - Other private or internal functions are overtaken from libcups as they are needed for the PPD-related functions to work. They are not added to the API.
 - Some functions of tools and utilities like ippeveprinter and ippeveps are overtaken.
-- All API functions have names starting with "ppd" and written in camel-case, some needed to get renamed for that.
+- The PPD compiler code (ppdc/ directory) is also overtaken into libppd and the ppdc utilities are overtaken to cups-filters. This allows retro-fitting printer drivers with driver information files (*.drv) instead of ready-made PPDs, both by pre-building the PPDs or by letting them get generated on-the-fly.
+- All API functions have names starting with "ppd" (or "ppdc") and written in camel-case, some needed to get renamed for that.
 - libppd is separate from libcupsfilters, so it does not need to get included in a Printer Application which uses functionality of cups-filters but does not use PPD files
 
-NOTE: This is NOT to encourage printer driver developers to continue to create new PPD files for new printers. It is ONLY for retro-fitting existing classic CUPS drivers and PostScript PPD files. There will be no further development on the library's code, especially no new PPD format extensions.
+NOTE: This is NOT to encourage printer driver developers to continue to create new PPD files and *.drv files for new printers. It is ONLY for retro-fitting existing classic CUPS drivers and PostScript PPD files. There will be no further development on the library's code, especially no new PPD or drv format extensions.
diff --git a/ppd/genstrings.cxx b/ppd/genstrings.cxx
new file mode 100644 (file)
index 0000000..ef8be72
--- /dev/null
@@ -0,0 +1,197 @@
+//
+// GNU gettext message generator for the CUPS PPD Compiler.
+//
+// This program is used to generate a dummy source file containing all of
+// the standard media and sample driver strings.  The results are picked up
+// by GNU gettext and placed in the CUPS message catalog.
+//
+// Copyright 2008-2014 by Apple Inc.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+// Usage:
+//
+//   ./genstrings >sample.c
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#include <unistd.h>
+
+
+//
+// Local functions...
+//
+
+static void    add_ui_strings(ppdcDriver *d, ppdcCatalog *catalog);
+static void    write_cstring(const char *s);
+
+
+//
+// 'main()' - Main entry for the PPD compiler.
+//
+
+int                                    // O - Exit status
+main(void)
+{
+  ppdcSource   *src;                   // PPD source file data
+  ppdcCatalog  *catalog;               // Catalog to hold all of the UI strings
+
+
+  // Make sure we are in the right place...
+  if (access("../data", 0) || access("sample.drv", 0))
+  {
+    puts("You must run genstrings from the ppdc directory.");
+    return (1);
+  }
+
+  // Load the sample drivers...
+  ppdcSource::add_include("../data");
+
+  src     = new ppdcSource("sample.drv");
+  catalog = new ppdcCatalog(NULL);
+
+  catalog->add_message("ISOLatin1");
+  catalog->add_message("English");
+
+  // Add the media size strings...
+  ppdcMediaSize        *size;                  // Current media size
+
+  for (size = (ppdcMediaSize *)src->sizes->first();
+       size;
+       size = (ppdcMediaSize *)src->sizes->next())
+    catalog->add_message(size->text->value);
+
+  // Then collect all of the UI strings from the sample drivers...
+  ppdcDriver   *d;                     // Current driver
+
+  for (d = (ppdcDriver *)src->drivers->first();
+       d;
+       d = (ppdcDriver *)src->drivers->next())
+    add_ui_strings(d, catalog);
+
+  // Finally, write all of the strings...
+  ppdcMessage *message;
+
+  for (message = (ppdcMessage *)catalog->messages->first();
+       message;
+       message = (ppdcMessage *)catalog->messages->next())
+    write_cstring(message->id->value);
+
+  src->release();
+  catalog->release();
+
+  // 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 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] &&
+        (a->localizable ||
+        !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") ||
+         !strcmp(a->name->value, "cupsMarkerName")))
+    {
+      catalog->add_message(a->text->value);
+
+      if ((a->localizable && a->value->value[0]) ||
+          !strcmp(a->name->value, "cupsIPPReason"))
+        catalog->add_message(a->value->value);
+    }
+    else if (!strncmp(a->name->value, "Custom", 6) ||
+             !strncmp(a->name->value, "ParamCustom", 11))
+      catalog->add_message(a->name->value);
+  }
+}
+
+
+//
+// 'write_cstring()' - Write a translation string as a valid C string to stdout.
+//
+
+static void
+write_cstring(const char *s)           /* I - String to write */
+{
+  fputs("_(\"", stdout);
+  if (s)
+  {
+    while (*s)
+    {
+      if (*s == '\\')
+        fputs("\\\\", stdout);
+      else if (*s == '\"')
+        fputs("\\\"", stdout);
+      else if (*s == '\t')
+        fputs("\\t", stdout);
+      else if (*s == '\n')
+        fputs("\\n", stdout);
+      else
+        putchar(*s);
+
+      s ++;
+    }
+  }
+  puts("\");");
+}
similarity index 100%
rename from ppd/drv2ppd.cxx
rename to ppd/ppdc.cxx
diff --git a/ppd/ppdhtml.cxx b/ppd/ppdhtml.cxx
new file mode 100644 (file)
index 0000000..590dedb
--- /dev/null
@@ -0,0 +1,177 @@
+//
+// PPD to HTML utility for the CUPS PPD Compiler.
+//
+// Copyright 2007-2015 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#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
+  char         *value;                 // Value in option
+
+
+  // Scan the command-line...
+  src = new ppdcSource();
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+          case 'D' :                   // Define variable
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if ((value = strchr(argv[i], '=')) != NULL)
+             {
+               *value++ = '\0';
+
+               src->set_variable(argv[i], value);
+             }
+             else
+               src->set_variable(argv[i], "1");
+              break;
+
+          case 'I' :                   // Include directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+             ppdcSource::add_include(argv[i]);
+             break;
+
+         default :                     // Unknown
+             usage();
+       }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      src->read_file(argv[i]);
+    }
+
+  if ((d = (ppdcDriver *)src->drivers->first()) != NULL)
+  {
+    // Create a composite group with all of the features from the
+    // drivers in the info file...
+    composite = new ppdcGroup("", "");
+
+    while (d != NULL)
+    {
+      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 (!composite->find_option(o->name->value))
+           composite->add_option(new ppdcOption(o));
+       }
+
+      d = (ppdcDriver *)src->drivers->next();
+    }
+
+    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...
+    composite->release();
+  }
+  else
+  {
+    // If no drivers have been loaded, display the program usage message.
+    usage();
+  }
+
+  src->release();
+
+  // 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(_("  -D name=value           Set named variable to "
+                          "value."));
+  puts(_("  -I include-dir          Add include directory "
+                          "to search path."));
+
+  exit(1);
+}
diff --git a/ppd/ppdi.cxx b/ppd/ppdi.cxx
new file mode 100644 (file)
index 0000000..ff5fc20
--- /dev/null
@@ -0,0 +1,123 @@
+//
+// PPD file import utility for the CUPS PPD Compiler.
+//
+// Copyright 2007-2011 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#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();
+        }
+    }
+    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...
+  src->release();
+
+  // 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          Add include directory to "
+                          "search path."));
+  puts(_("  -o filename.drv         Set driver information "
+                          "file (otherwise ppdi.drv)."));
+
+  exit(1);
+}
diff --git a/ppd/ppdmerge.cxx b/ppd/ppdmerge.cxx
new file mode 100644 (file)
index 0000000..399ffb0
--- /dev/null
@@ -0,0 +1,359 @@
+//
+// PPD file merge utility for the CUPS PPD Compiler.
+//
+// Copyright Â©Â 2007-2018 by Apple Inc.
+// Copyright Â©Â 2002-2007 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more
+// information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include <ppd/ppdc-private.h>
+#include "ppd.h"
+#include <cups/array.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)
+  char         bckname[1024];          // Backup filename
+  cups_file_t  *infile,                // Input file
+               *outfile;               // Output file
+  cups_array_t *languages;             // Languages in file
+  const char   *locale;                // Current locale
+  char         line[1024];             // Line from file
+
+
+  // Scan the command-line...
+  inname    = NULL;
+  outname   = NULL;
+  outfile   = NULL;
+  languages = NULL;
+  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();
+        }
+    }
+    else
+    {
+      // Open and load the PPD file...
+      if ((infile = cupsFileOpen(argv[i], "r")) == NULL)
+      {
+        fprintf(stderr, _("%s: Unable to open %s: %s"), "ppdmerge",
+                       argv[i], strerror(errno));
+       return (1);
+      }
+
+      // Open the PPD file...
+      if ((ppd = ppdOpen2(infile)) == NULL)
+      {
+        ppd_status_t   status;         // PPD open status
+       int             curline,        // Current line
+                       linenum;        // Line number
+
+
+        status = ppdLastError(&linenum);
+
+       fprintf(stderr,
+                       _("%s: Unable to open PPD file: %s on line %d."),
+                       "ppdmerge", ppdErrorString(status), linenum);
+        cupsFileRewind(infile);
+
+        line[0] = '\0';
+       curline = 0;
+
+        while (cupsFileGets(infile, line, sizeof(line)))
+       {
+         curline ++;
+         if (curline >= linenum)
+           break;
+       }
+
+       fprintf(stderr, "%d: %s", linenum, line);
+
+        cupsFileClose(infile);
+       return (1);
+      }
+
+      // Figure out the locale...
+      if ((locale = ppd_locale(ppd)) == NULL)
+      {
+        fprintf(stderr,
+                       _("ppdmerge: Bad LanguageVersion \"%s\" in %s."),
+                       ppd->lang_version, argv[i]);
+        cupsFileClose(infile);
+       ppdClose(ppd);
+       return (1);
+      }
+
+      if (!strcmp(locale, "en") && !inname && !outfile)
+      {
+        // Set the English PPD's filename...
+       inname    = argv[i];
+       languages = ppdGetLanguages(ppd);
+
+        if (outname && !strcmp(inname, outname))
+       {
+         // Rename input filename so that we don't overwrite it...
+         snprintf(bckname, sizeof(bckname), "%s.bck", inname);
+
+         if (rename(inname, bckname))
+         {
+           fprintf(stderr,
+                           _("ppdmerge: Unable to backup %s to %s - %s"),
+                           inname, bckname, strerror(errno));
+           return (1);
+         }
+
+         inname = bckname;
+       }
+      }
+      else if (strcmp(locale, "en"))
+      {
+       // Save this PPD for later processing...
+        cupsArrayAdd(ppds, ppd);
+      }
+      else
+      {
+        // Don't need this PPD...
+       fprintf(stderr, _("ppdmerge: Ignoring PPD file %s."),
+                       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 generate a new language list...
+  if (!languages)
+    languages = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+
+  for (ppd = (ppd_file_t *)cupsArrayFirst(ppds);
+       ppd;
+       ppd = (ppd_file_t *)cupsArrayNext(ppds))
+  {
+    locale = ppd_locale(ppd);
+
+    if (cupsArrayFind(languages, (void *)locale))
+    {
+      // Already have this language, remove the PPD from the list.
+      ppdClose(ppd);
+      cupsArrayRemove(ppds, ppd);
+    }
+    else
+      cupsArrayAdd(languages, (void *)locale);
+  }
+
+  // Copy the English PPD starting with a cupsLanguages line...
+  infile = cupsFileOpen(inname, "r");
+
+  if (outname)
+  {
+    const char *ext = strrchr(outname, '.');
+    if (ext && !strcmp(ext, ".gz"))
+      outfile = cupsFileOpen(outname, "w9");
+    else
+      outfile = cupsFileOpen(outname, "w");
+  }
+  else
+    outfile = cupsFileStdout();
+
+  cupsFileGets(infile, line, sizeof(line));
+  cupsFilePrintf(outfile, "%s\n", line);
+  if ((locale = (char *)cupsArrayFirst(languages)) != NULL)
+  {
+    cupsFilePrintf(outfile, "*cupsLanguages: \"%s", locale);
+    while ((locale = (char *)cupsArrayNext(languages)) != NULL)
+      cupsFilePrintf(outfile, " %s", locale);
+    cupsFilePuts(outfile, "\"\n");
+  }
+
+  while (cupsFileGets(infile, line, sizeof(line)))
+  {
+    if (strncmp(line, "*cupsLanguages:", 15))
+      cupsFilePrintf(outfile, "%s\n", line);
+  }
+
+  // Loop through the other 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
+    ppd_coption_t      *co;            // Custom option
+    ppd_cparam_t       *cp;            // Custom parameter
+    ppd_attr_t         *attr;          // PPD attribute
+
+    locale = ppd_locale(ppd);
+
+    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 ((co = ppdFindCustomOption(ppd, o->keyword)) != NULL)
+       {
+         snprintf(line, sizeof(line), "Custom%s", o->keyword);
+         attr = ppdFindAttr(ppd, line, "True");
+         cupsFilePrintf(outfile, "*%s.Custom%s True/%s: \"\"\n", locale,
+                        o->keyword, attr->text);
+         for (cp = ppdFirstCustomParam(co); cp; cp = ppdNextCustomParam(co))
+           cupsFilePrintf(outfile, "*%s.ParamCustom%s %s/%s: \"\"\n", locale,
+                          o->keyword, cp->name, cp->text);
+       }
+      }
+    }
+
+    ppdClose(ppd);
+  }
+
+  cupsArrayDelete(ppds);
+
+  cupsFileClose(outfile);
+
+  // Return with no errors.
+  return (0);
+}
+
+
+//
+// '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
+  size_t       vlen;                   // Length of LanguageVersion string
+  static char  locale[256];            // Locale string
+  static struct                                // LanguageVersion translation table
+  {
+    const char *version,               // LanguageVersion string */
+               *language;              // Language code */
+  }            languages[] =
+  {
+    { "chinese",               "zh" },
+    { "czech",                 "cs" },
+    { "danish",                        "da" },
+    { "dutch",                 "nl" },
+    { "english",               "en" },
+    { "finnish",               "fi" },
+    { "french",                        "fr" },
+    { "german",                        "de" },
+    { "greek",                 "el" },
+    { "hungarian",             "hu" },
+    { "italian",               "it" },
+    { "japanese",              "ja" },
+    { "korean",                        "ko" },
+    { "norwegian",             "no" },
+    { "polish",                        "pl" },
+    { "portuguese",            "pt" },
+    { "russian",               "ru" },
+    { "simplified chinese",    "zh_CN" },
+    { "slovak",                        "sk" },
+    { "spanish",               "es" },
+    { "swedish",               "sv" },
+    { "traditional chinese",   "zh_TW" },
+    { "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
+        strncpy(locale, languages[i].language, sizeof(locale) - 1);
+
+      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]    Set output file "
+                          "(otherwise stdout)."));
+
+  exit(1);
+}
diff --git a/ppd/ppdpo.cxx b/ppd/ppdpo.cxx
new file mode 100644 (file)
index 0000000..b845912
--- /dev/null
@@ -0,0 +1,248 @@
+//
+// PPD file message catalog program for the CUPS PPD Compiler.
+//
+// Copyright 2007-2015 by Apple Inc.
+// Copyright 2002-2005 by Easy Software Products.
+//
+// Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "ppdc-private.h"
+#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
+  char         *value;                 // Value in option
+
+
+  // Scan the command-line...
+  catalog = new ppdcCatalog("en");
+  src     = new ppdcSource();
+  verbose = 0;
+  outfile = 0;
+
+  for (i = 1; i < argc; i ++)
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+        switch (*opt)
+       {
+          case 'D' :                   // Define variable
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if ((value = strchr(argv[i], '=')) != NULL)
+             {
+               *value++ = '\0';
+
+               src->set_variable(argv[i], value);
+             }
+             else
+               src->set_variable(argv[i], "1");
+              break;
+
+          case 'I' :                   // Include directory...
+             i ++;
+             if (i >= argc)
+               usage();
+
+              if (verbose > 1)
+               printf(
+                               _("ppdc: Adding include directory \"%s\"."),
+                               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();
+       }
+    }
+    else
+    {
+      // Open and load the driver info file...
+      if (verbose > 1)
+        printf(
+                       _("ppdc: Loading driver information file \"%s\"."),
+                       argv[i]);
+
+      src->read_file(argv[i]);
+    }
+
+  // If no drivers have been loaded, display the program usage message.
+  if ((d = (ppdcDriver *)src->drivers->first()) != NULL)
+  {
+    // Add UI strings...
+    while (d != NULL)
+    {
+      if (verbose)
+       fprintf(stderr, _("ppdc: Adding/updating UI text from %s."), argv[i]);
+
+      add_ui_strings(d, catalog);
+
+      d = (ppdcDriver *)src->drivers->next();
+    }
+  }
+  else
+    usage();
+
+  // Delete the printer driver information...
+  src->release();
+
+  // Write the message catalog...
+  if (!outfile)
+    usage();
+  else
+    catalog->save_messages(outfile);
+
+  catalog->release();
+
+  // 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)
+        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)
+          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] &&
+        (a->localizable ||
+        !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") ||
+         !strcmp(a->name->value, "cupsMarkerName")))
+    {
+      catalog->add_message(a->text->value);
+
+      if ((a->localizable && a->value->value[0]) ||
+          !strcmp(a->name->value, "cupsIPPReason"))
+        catalog->add_message(a->value->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(_("  -D name=value           Set named variable to "
+                          "value."));
+  puts(_("  -I include-dir          Add include directory to "
+                          "search path."));
+  puts(_("  -v                      Be verbose."));
+
+  exit(1);
+}