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)
$(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
$(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
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])
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}
# 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, ...
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.
--- /dev/null
+//
+// 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("\");");
+}
--- /dev/null
+//
+// 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);
+}
--- /dev/null
+//
+// 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);
+}
--- /dev/null
+//
+// 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);
+}
--- /dev/null
+//
+// 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);
+}