summaryrefslogtreecommitdiff
path: root/foomaticrip.c
diff options
context:
space:
mode:
Diffstat (limited to 'foomaticrip.c')
-rw-r--r--foomaticrip.c1570
1 files changed, 1570 insertions, 0 deletions
diff --git a/foomaticrip.c b/foomaticrip.c
new file mode 100644
index 0000000..37d0822
--- /dev/null
+++ b/foomaticrip.c
@@ -0,0 +1,1570 @@
+
+#include "foomaticrip.h"
+#include "util.h"
+#include "options.h"
+#include "pdf.h"
+#include "postscript.h"
+#include "process.h"
+#include "spooler.h"
+#include "renderer.h"
+#include "fileconverter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <memory.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <math.h>
+#include <signal.h>
+#include <pwd.h>
+
+
+/* Logging */
+FILE* logh = NULL;
+
+void _logv(const char *msg, va_list ap)
+{
+ if (!logh)
+ return;
+ vfprintf(logh, msg, ap);
+ fflush(logh);
+}
+
+void _log(const char* msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ _logv(msg, ap);
+ va_end(ap);
+}
+
+void close_log()
+{
+ if (logh && logh != stderr)
+ fclose(logh);
+}
+
+int redirect_log_to_stderr()
+{
+ if (dup2(fileno(logh), fileno(stderr)) < 0) {
+ _log("Could not dup logh to stderr\n");
+ return 0;
+ }
+ return 1;
+}
+
+void rip_die(int status, const char *msg, ...)
+{
+ va_list ap;
+
+ _log("Process is dying with \"");
+ va_start(ap, msg);
+ _logv(msg, ap);
+ va_end(ap);
+ _log("\", exit stat %d\n", status);
+
+ _log("Cleaning up...\n");
+ kill_all_processes();
+
+ exit(status);
+}
+
+
+jobparams_t *job = NULL;
+
+jobparams_t * get_current_job()
+{
+ assert(job);
+ return job;
+}
+
+
+dstr_t *postpipe; /* command into which the output of this filter should be piped */
+FILE *postpipe_fh = NULL;
+
+FILE * open_postpipe()
+{
+ const char *p;
+
+ if (postpipe_fh)
+ return postpipe_fh;
+
+ if (isempty(postpipe->data))
+ return stdout;
+
+ /* Delete possible '|' symbol in the beginning */
+ p = skip_whitespace(postpipe->data);
+ if (*p && *p == '|')
+ p += 1;
+
+ if (start_system_process("postpipe", p, &postpipe_fh, NULL) < 0)
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
+ "Cannot execute postpipe %s\n", postpipe->data);
+
+ return postpipe_fh;
+}
+
+
+char printer_model[256] = "";
+const char *accounting_prolog = NULL;
+char attrpath[256] = "";
+
+
+int spooler = SPOOLER_DIRECT;
+int do_docs = 0;
+int dontparse = 0;
+int jobhasjcl;
+int pdfconvertedtops;
+
+/* Variable for PPR's backend interface name (parallel, tcpip, atalk, ...) */
+char backend [64];
+
+/* Array to collect unknown options so that they can get passed to the
+backend interface of PPR. For other spoolers we ignore them. */
+dstr_t *backendoptions = NULL;
+
+/* These variables were in 'dat' before */
+char colorprofile [128];
+char cupsfilter[256];
+char **jclprepend = NULL;
+dstr_t *jclappend;
+
+/* Set debug to 1 to enable the debug logfile for this filter; it will appear
+ * as defined by LOG_FILE. It will contain status from this filter, plus the
+ * renderer's stderr output. You can also add a line "debug: 1" to your
+ * /etc/foomatic/filter.conf to get all your Foomatic filters into debug mode.
+ * WARNING: This logfile is a security hole; do not use in production. */
+int debug = 0;
+
+/* Path to the GhostScript which foomatic-rip shall use */
+char gspath[PATH_MAX] = "gs";
+
+/* What 'echo' program to use. It needs -e and -n. Linux's builtin
+and regular echo work fine; non-GNU platforms may need to install
+gnu echo and put gecho here or something. */
+char echopath[PATH_MAX] = "echo";
+
+/* CUPS raster drivers are searched here */
+char cupsfilterpath[PATH_MAX] = "/usr/local/lib/cups/filter:"
+ "/usr/local/libexec/cups/filter:"
+ "/opt/cups/filter:"
+ "/usr/lib/cups/filter";
+
+char modern_shell[64] = "/bin/bash";
+
+void config_set_option(const char *key, const char *value)
+{
+ if (strcmp(key, "debug") == 0)
+ debug = atoi(value);
+
+ /* What path to use for filter programs and such. Your printer driver must be
+ * in the path, as must be the renderer, $enscriptcommand, and possibly other
+ * stuff. The default path is often fine on Linux, but may not be on other
+ * systems. */
+ else if (strcmp(key, "execpath") == 0 && !isempty(value))
+ setenv("PATH", value, 1);
+
+ else if (strcmp(key, "cupsfilterpath") == 0)
+ strlcpy(cupsfilterpath, value, PATH_MAX);
+ else if (strcmp(key, "preferred_shell") == 0)
+ strlcpy(modern_shell, value, 32);
+ else if (strcmp(key, "textfilter") == 0)
+ set_fileconverter(value);
+ else if (strcmp(key, "gspath") == 0)
+ strlcpy(gspath, value, PATH_MAX);
+ else if (strcmp(key, "echo") == 0)
+ strlcpy(echopath, value, PATH_MAX);
+}
+
+void config_from_file(const char *filename)
+{
+ FILE *fh;
+ char line[256];
+ char *key, *value;
+
+ fh = fopen(filename, "r");
+ if (fh == NULL)
+ return; /* no error here, only read config file if it is present */
+
+ while (fgets(line, 256, fh) != NULL)
+ {
+ key = strtok(line, " :\t\r\n");
+ if (key == NULL || key[0] == '#')
+ continue;
+ value = strtok(NULL, " \t\r\n#");
+ config_set_option(key, value);
+ }
+ fclose(fh);
+}
+
+const char * get_modern_shell()
+{
+ return modern_shell;
+}
+
+/* returns position in 'str' after the option */
+char * extract_next_option(char *str, char **pagerange, char **key, char **value)
+{
+ char *p = str;
+ char quotechar;
+
+ *pagerange = NULL;
+ *key = NULL;
+ *value = NULL;
+
+ if (!str)
+ return NULL;
+
+ /* skip whitespace and commas */
+ while (*p && (isspace(*p) || *p == ',')) p++;
+
+ if (!*p)
+ return NULL;
+
+ /* read the pagerange if we have one */
+ if (prefixcmp(p, "even:") == 0 || prefixcmp(p, "odd:") == 0 || isdigit(*p)) {
+ *pagerange = p;
+ p = strchr(p, ':');
+ if (!p)
+ return NULL;
+ *p = '\0';
+ p++;
+ }
+
+ /* read the key */
+ if (*p == '\'' || *p == '\"') {
+ quotechar = *p;
+ *key = p +1;
+ p = strchr(*key, quotechar);
+ if (!p)
+ return NULL;
+ }
+ else {
+ *key = p;
+ while (*p && *p != ':' && *p != '=' && *p != ' ') p++;
+ }
+
+ if (*p != ':' && *p != '=') { /* no value for this option */
+ if (!*p)
+ return NULL;
+ else if (isspace(*p)) {
+ *p = '\0';
+ return p +1;
+ }
+ return p;
+ }
+
+ *p++ = '\0'; /* remove the separator sign */
+
+ if (*p == '\"' || *p == '\'') {
+ quotechar = *p;
+ *value = p +1;
+ p = strchr(*value, quotechar);
+ if (!p)
+ return NULL;
+ *p = '\0';
+ p++;
+ }
+ else {
+ *value = p;
+ while (*p && *p != ' ' && *p != ',') p++;
+ if (*p == '\0')
+ return NULL;
+ *p = '\0';
+ p++;
+ }
+
+ return *p ? p : NULL;
+}
+
+/* processes job->optstr */
+void process_cmdline_options()
+{
+ char *p, *nextopt, *pagerange, *key, *value;
+ option_t *opt, *opt2;
+ int optset;
+ char tmp [256];
+
+ for (nextopt = extract_next_option(job->optstr->data, &pagerange, &key, &value);
+ key;
+ nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
+ {
+ if (value)
+ _log("Pondering option '%s=%s'\n", key, value);
+ else
+ _log("Pondering option '%s'\n", key);
+
+ /* "docs" option to print help page */
+ if (!strcasecmp(key, "docs")) {
+ do_docs = 1;
+ continue;
+ }
+ /* "profile" option to supply a color correction profile to a CUPS raster driver */
+ if (!strcmp(key, "profile")) {
+ strlcpy(colorprofile, value, 128);
+ continue;
+ }
+ /* Solaris options that have no reason to be */
+ if (!strcmp(key, "nobanner") || !strcmp(key, "dest") || !strcmp(key, "protocol"))
+ continue;
+
+ if (pagerange) {
+ snprintf(tmp, 256, "pages:%s", pagerange);
+ optset = optionset(tmp);
+
+ opt = find_option(key);
+ if (opt && (option_get_section(opt) != SECTION_ANYSETUP &&
+ option_get_section(opt) != SECTION_PAGESETUP)) {
+ _log("This option (%s) is not a \"PageSetup\" or \"AnySetup\" option, so it cannot be restricted to a page range.\n", key);
+ continue;
+ }
+ }
+ else
+ optset = optionset("userval");
+
+ if (value) {
+ /* At first look for the "backend" option to determine the PPR backend to use */
+ if (spooler == SPOOLER_PPR_INT && !strcasecmp(key, "backend")) {
+ /* backend interface name */
+ strlcpy(backend, value, 64);
+ }
+ else if (strcasecmp(key, "media") == 0) {
+ /* Standard arguments?
+ media=x,y,z
+ sides=one|two-sided-long|short-edge
+
+ Rummage around in the media= option for known media, source,
+ etc types.
+ We ought to do something sensible to make the common manual
+ boolean option work when specified as a media= tray thing.
+
+ Note that this fails miserably when the option value is in
+ fact a number; they all look alike. It's unclear how many
+ drivers do that. We may have to standardize the verbose
+ names to make them work as selections, too. */
+
+ p = strtok(value, ",");
+ do {
+ if ((opt = find_option("PageSize")) && option_accepts_value(opt, p))
+ option_set_value(opt, optset, p);
+ else if ((opt = find_option("MediaType")) && option_has_choice(opt, p))
+ option_set_value(opt, optset, p);
+ else if ((opt = find_option("InputSlot")) && option_has_choice(opt, p))
+ option_set_value(opt, optset, p);
+ else if (!strcasecmp(p, "manualfeed")) {
+ /* Special case for our typical boolean manual
+ feeder option if we didn't match an InputSlot above */
+ if ((opt = find_option("ManualFeed")))
+ option_set_value(opt, optset, "1");
+ }
+ else
+ _log("Unknown \"media\" component: \"%s\".\n", p);
+
+ } while ((p = strtok(NULL, ",")));
+ }
+ else if (!strcasecmp(key, "sides")) {
+ /* Handle the standard duplex option, mostly */
+ if (!prefixcasecmp(value, "two-sided")) {
+ if ((opt = find_option("Duplex"))) {
+ /* Default to long-edge binding here, for the case that
+ there is no binding setting */
+ option_set_value(opt, optset, "DuplexNoTumble");
+
+ /* Check the binding: "long edge" or "short edge" */
+ if (strcasestr(value, "long-edge")) {
+ if ((opt2 = find_option("Binding")))
+ option_set_value(opt2, optset, "LongEdge");
+ else
+ option_set_value(opt, optset, "DuplexNoTumble");
+ }
+ else if (strcasestr(value, "short-edge")) {
+ if ((opt2 = find_option("Binding")))
+ option_set_value(opt2, optset, "ShortEdge");
+ else
+ option_set_value(opt, optset, "DuplexNoTumble");
+ }
+ }
+ }
+ else if (!prefixcasecmp(value, "one-sided")) {
+ if ((opt = find_option("Duplex")))
+ option_set_value(opt, optset, "0");
+ }
+
+ /* TODO
+ We should handle the other half of this option - the
+ BindEdge bit. Also, are there well-known ipp/cups options
+ for Collate and StapleLocation? These may be here...
+ */
+ }
+ else {
+ /* Various non-standard printer-specific options */
+ if ((opt = find_option(key))) {
+ if (!option_set_value(opt, optset, value)) {
+ _log(" invalid choice \"%s\", using \"%s\" instead\n",
+ value, option_get_value(opt, optset));
+ }
+ }
+ else if (spooler == SPOOLER_PPR_INT) {
+ /* Unknown option, pass it to PPR's backend interface */
+ if (!backendoptions)
+ backendoptions = create_dstr();
+ dstrcatf(backendoptions, "%s=%s ", key, value);
+ }
+ else
+ _log("Unknown option %s=%s.\n", key, value);
+ }
+ }
+ /* Custom paper size */
+ else if ((opt = find_option("PageSize")) && option_set_value(opt, optset, key)) {
+ /* do nothing, if the value could be set, it has been set */
+ }
+ /* Standard bool args:
+ landscape; what to do here?
+ duplex; we should just handle this one OK now? */
+ else if (!prefixcasecmp(key, "no") && (opt = find_option(&key[2])))
+ option_set_value(opt, optset, "0");
+ else if ((opt = find_option(key)))
+ option_set_value(opt, optset, "1");
+ else
+ _log("Unknown boolean option \"%s\".\n", key);
+ }
+}
+
+/* checks whether a pdq driver declaration file should be build
+ and returns an opened file handle if so */
+FILE * check_pdq_file(list_t *arglist)
+{
+ /* "--appendpdq=<file>" appends the data to the <file>,
+ "--genpdq=<file>" creates/overwrites <file> for the data, and
+ "--genpdq" writes to standard output */
+
+ listitem_t *i;
+ char filename[256];
+ FILE *handle;
+ char *p;
+ int raw, append;
+
+ if ((i = arglist_find_prefix(arglist, "--genpdq"))) {
+ raw = 0;
+ append = 0;
+ }
+ else if ((i = arglist_find_prefix(arglist, "--genrawpdq"))) {
+ raw = 1;
+ append = 0;
+ }
+ else if ((i = arglist_find_prefix(arglist, "--appendpdq"))) {
+ raw = 0;
+ append = 1;
+ }
+ else if ((i = arglist_find_prefix(arglist, "--appendrawpdq"))) {
+ raw = 1;
+ append = 1;
+ }
+
+ if (!i)
+ return NULL;
+
+ p = strchr((char*)i->data, '=');
+ if (p) {
+ strncpy_omit(filename, p +1, 256, omit_shellescapes);
+ handle = fopen(filename, append ? "a" : "w");
+ if (!handle)
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Cannot write PDQ driver declaration file.\n");
+ }
+ else if (!append)
+ handle = stdout;
+ else
+ return NULL;
+
+ /* remove option from args */
+ list_remove(arglist, i);
+
+ /* Do we have a pdq driver declaration for a raw printer */
+ if (raw) {
+ fprintf(handle,
+ "driver \"Raw-Printer-%u\" {\n"
+ " # This PDQ driver declaration file was generated automatically by\n"
+ " # foomatic-rip to allow raw (filter-less) printing.\n"
+ " language_driver all {\n"
+ " # We accept all file types and pass them through without any changes\n"
+ " filetype_regx \"\"\n"
+ " convert_exec {\n"
+ " ln -s $INPUT $OUTPUT\n"
+ " }\n"
+ " }\n"
+ " filter_exec {\n"
+ " ln -s $INPUT $OUTPUT\n"
+ " }\n"
+ "}", (unsigned int)job->time);
+ if (handle != stdout) {
+ fclose(handle);
+ handle = NULL;
+ }
+ exit(EXIT_PRINTED);
+ }
+
+ return handle;
+}
+
+
+/* Build a PDQ driver description file to use the given PPD file
+ together with foomatic-rip with the PDQ printing system
+ and output it into 'pdqfile' */
+void print_pdq_driver(FILE *pdqfile, int optset)
+{
+}
+#if 0
+ option_t *opt;
+ value_t *val;
+ setting_t *setting, *setting_true, *setting_false;
+
+ /* Construct option list */
+ dstr_t *driveropts = create_dstr();
+
+ /* Do we have a "Custom" setting for the page size?
+ Then we have to insert the following into the filter_exec script. */
+ dstr_t *setcustompagesize = create_dstr();
+
+ dstr_t *tmp = create_dstr();
+ dstr_t *cmdline = create_dstr();
+ dstr_t *psfilter = create_dstr();
+
+
+ /* 1, if setting "PageSize=Custom" was found
+ Then we must add options for page width and height */
+ int custompagesize = 0;
+
+ /* Data for a custom page size, to allow a custom size as default */
+ int pagewidth = 612;
+ int pageheight = 792;
+ char pageunit[2] = "pt";
+
+ char def [128];
+
+ def[0] = '\0';
+
+ for (opt = optionlist; opt; opt = opt->next) {
+ if (opt->type == TYPE_ENUM) {
+ /* Option with only one choice, omit it, foomatic-rip will set
+ this choice anyway */
+ if (option_setting_count(opt) <= 1)
+ continue;
+
+ /* Omit "PageRegion" option, it does the same as "PageSize" */
+ if (!strcmp(opt->name, "PageRegion"))
+ continue;
+
+ /* 1, if setting "PageSize=Custom" was found
+ Then we must add options for page width and height */
+ custompagesize = 0;
+
+ if ((val = option_get_value(opt, optset)))
+ strlcpy(def, val->value, 128);
+
+#if 0 TODO not used ?!
+ /* If the default is a custom size we have to set also
+ defaults for the width, height, and units of the page */
+ if (!strcmp(opt->name, "PageSize") && val && value_has_custom_setting(val))
+ strcpy(def, "Custom");
+#endif
+
+ dstrcatf(driveropts,
+ " option {\n"
+ " var = \"%s\"\n"
+ " desc = \"%s\"\n", opt->varname, option_text(opt));
+
+ /* get enumeration values for each enum arg */
+ dstrclear(tmp);
+ for (setting = opt->settinglist; setting; setting = setting->next) {
+ dstrcatf(tmp,
+ " choice \"%s_%s\" {\n"
+ " desc = \"%s\"\n"
+ " value = \" -o %s=%s\"\n"
+ " }\n",
+ opt->name, setting->value,
+ isempty(setting->comment) ? setting->value : setting->comment,
+ opt->name, setting->value);
+
+ if (!strcmp(opt->name, "PageSize") && !strcmp(setting->value, "Custom")) {
+ custompagesize = 1;
+ if (isempty(setcustompagesize->data)) {
+ dstrcatf(setcustompagesize,
+ " # Custom page size settings\n"
+ " # We aren't really checking for legal vals.\n"
+ " if [ \"x${%s}\" = 'x -o %s=%s' ]; then\n"
+ " %s=\"${%s}.${PageWidth}x${PageHeight}${PageSizeUnit}\"\n"
+ " fi\n\n",
+ opt->varname, opt->varname, setting->value, opt->varname, opt->varname);
+ }
+ }
+ }
+
+ dstrcatf(driveropts, " default_choice \"%s_%s\"\n", opt->name, def);
+ dstrcatf(driveropts, tmp->data);
+ dstrcatf(driveropts, " }\n\n");
+
+ if (custompagesize) {
+ /* Add options to set the custom page size */
+ dstrcatf(driveropts,
+ " argument {\n"
+ " var = \"PageWidth\"\n"
+ " desc = \"Page Width (for \\\"Custom\\\" page size)\"\n"
+ " def_value \"%d\"\n" /* pagewidth */
+ " help = \"Minimum value: 0, Maximum value: 100000\"\n"
+ " }\n\n"
+ " argument {\n"
+ " var = \"PageHeight\"\n"
+ " desc = \"Page Height (for \\\"Custom\\\" page size)\"\n"
+ " def_value \"%d\"\n" /* pageheight */
+ " help = \"Minimum value: 0, Maximum value: 100000\"\n"
+ " }\n\n"
+ " option {\n"
+ " var = \"PageSizeUnit\"\n"
+ " desc = \"Unit (for \\\"Custom\\\" page size)\"\n"
+ " default_choice \"PageSizeUnit_%.2s\"\n" /* pageunit */
+ " choice \"PageSizeUnit_pt\" {\n"
+ " desc = \"Points (1/72 inch)\"\n"
+ " value = \"pt\"\n"
+ " }\n"
+ " choice \"PageSizeUnit_in\" {\n"
+ " desc = \"Inches\"\n"
+ " value = \"in\"\n"
+ " }\n"
+ " choice \"PageSizeUnit_cm\" {\n"
+ " desc = \"cm\"\n"
+ " value = \"cm\"\n"
+ " }\n"
+ " choice \"PageSizeUnit_mm\" {\n"
+ " desc = \"mm\"\n"
+ " value = \"mm\"\n"
+ " }\n"
+ " }\n\n",
+ pagewidth, pageheight, pageunit);
+ }
+ }
+ else if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT) {
+ /* Assure that the comment is not emtpy */
+ if (isempty(opt->comment))
+ strcpy(opt->comment, opt->name);
+
+ if ((val = option_get_value(opt, optset)))
+ strlcpy(def, val->value, 128);
+
+ strcpy(opt->varname, opt->name);
+ strrepl(opt->varname, "-/.", '_');
+
+
+ dstrcatf(driveropts,
+ " argument {\n"
+ " var = \"%s\"\n"
+ " desc = \"%s\"\n"
+ " def_value \"%s\"\n"
+ " help = \"Minimum value: %s, Maximum value: %s\"\n"
+ " }\n\n",
+ opt->varname, opt->comment, def, opt->min, opt->max);
+ }
+ else if (opt->type == TYPE_BOOL) {
+ /* Assure that the comment is not emtpy */
+ if (isempty(opt->comment))
+ strcpy(opt->comment, opt->name);
+
+ if ((val = option_get_value(opt, optset)))
+ strlcpy(def, val->value, 128);
+ strcpy(opt->varname, opt->name);
+ strrepl(opt->varname, "-/.", '_');
+ setting_true = option_find_setting(opt, "true");
+ setting_false = option_find_setting(opt, "false");
+
+ dstrcatf(driveropts,
+ " option {\n"
+ " var = \"%s\"\n"
+ " desc = \"%s\"\n", opt->varname, opt->comment);
+
+ if (!isempty(def) && !strcasecmp(def, "true"))
+ dstrcatf(driveropts, " default_choice \"%s\"\n", def);
+ else
+ dstrcatf(driveropts, " default_choice \"no%s\"\n", def);
+
+ dstrcatf(driveropts,
+ " choice \"%s\" {\n"
+ " desc = \"%s\"\n"
+ " value = \" -o %s=True\"\n"
+ " }\n"
+ " choice \"no%s\" {\n"
+ " desc = \"%s\"\n"
+ " value = \" -o %s=False\"\n"
+ " }\n"
+ " }\n\n",
+ opt->name, setting_true->comment, opt->name,
+ opt->name, setting_false->comment, opt->name);
+ }
+ else if (opt->type == TYPE_STRING) {
+ /* Assure that the comment is not emtpy */
+ if (isempty(opt->comment))
+ strcpy(opt->comment, opt->name);
+
+ if ((val = option_get_value(opt, optset)))
+ strlcpy(def, val->value, 128);
+ strcpy(opt->varname, opt->name);
+ strrepl_nodups(opt->varname, "-/.", '_');
+
+ dstrclear(tmp);
+ if (opt->maxlength)
+ dstrcatf(tmp, "Maximum Length: %s characters, ", opt->maxlength);
+
+ dstrcatf(tmp, "Examples/special settings: ");
+ for (setting = opt->settinglist; setting; setting = setting->next) {
+ /* Retrieve the original string from the prototype and the driverval */
+ /* TODO perl code for this part doesn't make sense to me */
+ }
+ }
+ }
+
+ /* Define the "docs" option to print the driver documentation page */
+ dstrcatf(driveropts,
+ " option {\n"
+ " var = \"DRIVERDOCS\"\n"
+ " desc = \"Print driver usage information\"\n"
+ " default_choice \"nodocs\"\n"
+ " choice \"docs\" {\n"
+ " desc = \"Yes\"\n"
+ " value = \" -o docs\"\n"
+ " }\n"
+ " choice \"nodocs\" {\n"
+ " desc = \"No\"\n"
+ " value = \"\"\n"
+ " }\n"
+ " }\n\n");
+
+ /* Build the foomatic-rip command line */
+ dstrcatf(cmdline, "foomatic-rip --pdq");
+ if (!isempty(printer)) {
+ dstrcatf(cmdline, " -P %s", printer);
+ }
+ else {
+ /* Make sure that the PPD file is entered with an absolute path */
+ make_absolute_path(job->ppdfile, 256);
+ dstrcatf(cmdline, " --ppd=%s", job->ppdfile);
+ }
+
+ for (opt = optionlist; opt; opt = opt->next) {
+ if (!isempty(opt->varname))
+ dstrcatf(cmdline, "${%s}", opt->varname);
+ }
+ dstrcatf(cmdline, "${DRIVERDOCS} $INPUT > $OUTPUT");
+
+
+ /* Now we generate code to build the command line snippets for the numerical options */
+ for (opt = optionlist; opt; opt = opt->next) {
+ /* Only numerical and string options need to be treated here */
+ if (opt->type != TYPE_INT &&
+ opt->type != TYPE_FLOAT &&
+ opt->type != TYPE_STRING)
+ continue;
+
+ /* If the option's variable is non-null, put in the
+ argument. Otherwise this option is the empty
+ string. Error checking? */
+ dstrcatf(psfilter, " # %s\n", opt->comment);
+ if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT) {
+ dstrcatf(psfilter,
+ " # We aren't really checking for max/min,\n"
+ " # this is done by foomatic-rip\n"
+ " if [ \"x${%s}\" != 'x' ]; then\n ", opt->varname);
+ }
+
+ dstrcatf(psfilter, " %s=\" -o %s='${%s}'\"\n", opt->varname, opt->name, opt->varname);
+
+ if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT)
+ dstrcatf(psfilter, " fi\n");
+ dstrcatf(psfilter, "\n");
+ }
+
+ /* Command execution */
+ dstrcatf(psfilter,
+ " if ! test -e $INPUT.ok; then\n"
+ " sh -c \"%s\"\n"
+ " if ! test -e $OUTPUT; then \n"
+ " echo 'Error running foomatic-rip; no output!'\n"
+ " exit 1\n"
+ " fi\n"
+ " else\n"
+ " ln -s $INPUT $OUTPUT\n"
+ " fi\n\n", cmdline->data);
+
+
+ dstrclear(tmp);
+ dstrcatf(tmp, "%s", printer_model);
+ strrepl_nodups(tmp->data, " \t\n.,;/()[]{}+*", '-');
+ tmp->len = strlen(tmp->data); /* length could have changed */
+ if (tmp->data[tmp->len -1] == '-') {
+ tmp->data[--tmp->len] = '\0';
+ }
+
+
+ fprintf(pdqfile,
+ "driver \"%s-%u\" {\n\n"
+ " # This PDQ driver declaration file was generated automatically by\n"
+ " # foomatic-rip from information in the file %s.\n" /* ppdfile */
+ " # It allows printing with PDQ on the %s.\n" /* model */
+ "\n"
+ " requires \"foomatic-rip\"\n\n"
+ "%s" /* driveropts */
+ " language_driver all {\n"
+ " # We accept all file types and pass them to foomatic-rip\n"
+ " # (invoked in \"filter_exec {}\" section) without\n"
+ " # pre-filtering\n"
+ " filetype_regx \"\"\n"
+ " convert_exec {\n"
+ " ln -s $INPUT $OUTPUT\n"
+ " }\n"
+ " }\n\n"
+ " filter_exec {\n"
+ "%s" /* setcustompagesize */
+ "%s" /* psfilter */
+ " }\n"
+ "}\n",
+ tmp->data, /* cleaned printer_model */ (unsigned int)job->time, job->ppdfile, printer_model,
+ driveropts->data, setcustompagesize->data, psfilter->data);
+
+
+ free_dstr(setcustompagesize);
+ free_dstr(driveropts);
+ free_dstr(tmp);
+ free_dstr(cmdline);
+ free_dstr(psfilter);
+}
+#endif
+
+/* Functions to let foomatic-rip fork to do several tasks in parallel.
+
+To do the filtering without loading the whole file into memory we work
+on a data stream, we read the data line by line analyse it to decide what
+filters to use and start the filters if we have found out which we need.
+We buffer the data only as long as we didn't determing which filters to
+use for this piece of data and with which options. There are no temporary
+files used.
+
+foomatic-rip splits into up to 6 parallel processes to do the whole
+filtering (listed in the order of the data flow):
+
+ KID0: Generate documentation pages (only jobs with "docs" option)
+ KID2: Put together already read data and current input stream for
+ feeding into the file conversion filter (only non-PostScript
+ and "docs" jobs)
+ KID1: Run the file conversion filter to convert non-PostScript
+ input into PostScript (only non-PostScript and "docs" jobs)
+ MAIN: Prepare the job auto-detecting the spooler, reading the PPD,
+ extracting the options from the command line, and parsing
+ the job data itself. It analyses the job data to check
+ whether it is PostScript and starts KID1/KID2 if not, it
+ also stuffs PostScript code from option settings into the
+ PostScript data stream. It starts the renderer (KID3/KID4)
+ as soon as it knows its command line and restarts it when
+ page-specific option settings need another command line
+ or different JCL commands.
+ KID3: The rendering process. In most cases Ghostscript, "cat"
+ for native PostScript printers with their manufacturer's
+ PPD files.
+ KID4: Put together the JCL commands and the renderer's output
+ and send all that either to STDOUT or pipe it into the
+ command line defined with $postpipe. */
+
+
+
+void write_output(void *data, size_t len)
+{
+ const char *p = (const char *)data;
+ size_t left = len;
+ FILE *postpipe = open_postpipe();
+
+ /* Remove leading whitespace */
+ while (isspace(*p++) && left-- > 0)
+ ;
+
+ fwrite((void *)p, left, 1, postpipe);
+ fflush(postpipe);
+}
+
+enum FileType {
+ UNKNOWN_FILE,
+ PDF_FILE,
+ PS_FILE
+};
+
+int guess_file_type(const char *begin, size_t len, int *startpos)
+{
+ const char * p;
+
+ p = memchr(begin, '%', len);
+ if (!p)
+ return UNKNOWN_FILE;
+ *startpos = p - begin;
+ if (!memcmp(p, "%!", 2))
+ return PS_FILE;
+ else if (!memcmp(p, "%PDF-1.", 7))
+ return PDF_FILE;
+ *startpos = 0;
+ return UNKNOWN_FILE;
+}
+
+/*
+ * Prints 'filename'. If 'convert' is true, the file will be converted if it is
+ * not postscript or pdf
+ */
+int print_file(const char *filename, int convert)
+{
+ FILE *file;
+ char buf[8192];
+ int type;
+ int startpos;
+ size_t n;
+ FILE *fchandle = NULL;
+ int fcpid = 0, ret;
+
+ if (!strcasecmp(filename, "<STDIN>"))
+ file = stdin;
+ else {
+ file = fopen(filename, "r");
+ if (!file) {
+ _log("Could not open \"%s\" for reading\n", filename);
+ return 0;
+ }
+ }
+
+ n = fread(buf, 1, sizeof(buf), file);
+ buf[n] = '\0';
+ type = guess_file_type(buf, n, &startpos);
+ if (startpos > 0) {
+ jobhasjcl = 1;
+ write_output(buf, startpos);
+ }
+ if (file != stdin)
+ rewind(file);
+
+ if (convert) pdfconvertedtops = 0;
+
+ switch (type) {
+ case PDF_FILE:
+ _log("Filetype: PDF\n");
+
+ if (!ppd_supports_pdf())
+ {
+ char pdf2ps_cmd[PATH_MAX];
+ FILE *out, *in;
+ int renderer_pid;
+
+ _log("Driver does not understand PDF input, "
+ "converting to PostScript\n");
+
+ pdfconvertedtops = 1;
+ snprintf(pdf2ps_cmd, PATH_MAX,
+ "gs -q -sstdout=%%stderr -sDEVICE=pswrite -sOutputFile=- "
+ "-dBATCH -dNOPAUSE -dPARANOIDSAFER %s",
+ file == stdin ? "-" : filename);
+
+ renderer_pid = start_system_process("pdf-to-ps", pdf2ps_cmd, &in, &out);
+
+ if (file == stdin)
+ {
+ fwrite(buf, 1, n, in);
+ while ((n = fread(buf, 1, sizeof(buf), file)))
+ fwrite(buf, 1, n, in);
+ fclose(in);
+ }
+
+ if (dup2(fileno(out), fileno(stdin)) < 0)
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
+ "Couldn't dup stdout of pdf-to-ps\n");
+
+ ret = print_file("<STDIN>", 0);
+
+ wait_for_process(renderer_pid);
+ return ret;
+ }
+
+ if (file == stdin)
+ return print_pdf(stdin, buf, n, filename, startpos);
+ else
+ return print_pdf(file, NULL, 0, filename, startpos);
+
+ case PS_FILE:
+ _log("Filetype: PostScript\n");
+ if (file == stdin)
+ return print_ps(stdin, buf, n, filename);
+ else
+ return print_ps(file, NULL, 0, filename);
+
+ case UNKNOWN_FILE:
+ if (spooler == SPOOLER_CUPS) {
+ _log("Cannot process \"%s\": Unknown filetype.\n", filename);
+ return 0;
+ }
+
+ _log("Filetype unknown, trying to convert ...\n");
+ get_fileconverter_handle(buf, &fchandle, &fcpid);
+
+ /* Read further data from the file converter and not from STDIN */
+ if (dup2(fileno(fchandle), fileno(stdin)) < 0)
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't dup fileconverterhandle\n");
+
+ ret = print_file("<STDIN>", 0);
+
+ if (close_fileconverter_handle(fchandle, fcpid) != EXIT_PRINTED)
+ rip_die(ret, "Error closing file converter\n");
+ return ret;
+ }
+
+ fclose(file);
+ return 1;
+}
+
+void signal_terminate(int signal)
+{
+ rip_die(EXIT_PRINTED, "Caught termination signal: Job canceled\n");
+}
+
+jobparams_t * create_job()
+{
+ jobparams_t *job = calloc(1, sizeof(jobparams_t));
+ struct passwd *passwd;
+
+ job->optstr = create_dstr();
+ job->time = time(NULL);
+ strcpy(job->copies, "1");
+ gethostname(job->host, 128);
+ passwd = getpwuid(getuid());
+ if (passwd)
+ strlcpy(job->user, passwd->pw_name, 128);
+ snprintf(job->title, 128, "%s@%s", job->user, job->host);
+
+ return job;
+}
+
+void free_job(jobparams_t *job)
+{
+ free_dstr(job->optstr);
+ free(job);
+}
+
+int main(int argc, char** argv)
+{
+ int i;
+ int verbose = 0, quiet = 0, showdocs = 0;
+ const char* str;
+ char *p, *filename;
+ const char *path;
+ FILE *genpdqfile = NULL;
+ FILE *ppdfh = NULL;
+ char tmp[1024], pstoraster[256];
+ int havefilter, havepstoraster;
+ dstr_t *filelist;
+ list_t * arglist;
+
+ arglist = list_create_from_array(argc -1, (void**)&argv[1]);
+
+ if (argc == 2 && (arglist_find(arglist, "--version") || arglist_find(arglist, "--help") ||
+ arglist_find(arglist, "-v") || arglist_find(arglist, "-h"))) {
+ printf("foomatic rip version "VERSION"\n");
+ printf("\"man foomatic-rip\" for help.\n");
+ list_free(arglist);
+ return 0;
+ }
+
+ filelist = create_dstr();
+ job = create_job();
+
+ jclprepend = NULL;
+ jclappend = create_dstr();
+ postpipe = create_dstr();
+
+ options_init();
+
+ signal(SIGTERM, signal_terminate);
+ signal(SIGINT, signal_terminate);
+
+
+ config_from_file(CONFIG_PATH "/filter.conf");
+
+ /* Command line options for verbosity */
+ if (arglist_remove_flag(arglist, "-v"))
+ verbose = 1;
+ if (arglist_remove_flag(arglist, "-q"))
+ quiet = 1;
+ if (arglist_remove_flag(arglist, "-d"))
+ showdocs = 1;
+ if (arglist_remove_flag(arglist, "--debug"))
+ debug = 1;
+
+ if (debug)
+ logh = fopen(LOG_FILE ".log", "w"); /* insecure, use for debugging only */
+ else if (quiet && !verbose)
+ logh = NULL; /* Quiet mode, do not log */
+ else
+ logh = stderr; /* Default: log to stderr */
+
+ /* Start debug logging */
+ if (debug) {
+ /* If we are not in debug mode, we do this later, as we must find out at
+ first which spooler is used. When printing without spooler we
+ suppress logging because foomatic-rip is called directly on the
+ command line and so we avoid logging onto the console. */
+ _log("foomatic-rip version "VERSION" running...\n");
+
+ /* Print the command line only in debug mode, Mac OS X adds very many
+ options so that CUPS cannot handle the output of the command line
+ in its log files. If CUPS encounters a line with more than 1024
+ characters sent into its log files, it aborts the job with an error. */
+ if (spooler != SPOOLER_CUPS) {
+ _log("called with arguments: ");
+ for (i = 1; i < argc -1; i++)
+ _log("\'%s\', ", argv[i]);
+ _log("\'%s\'\n", argv[i]);
+ }
+ }
+
+ if (getenv("PPD")) {
+ strncpy(job->ppdfile, getenv("PPD"), 256);
+ spooler = SPOOLER_CUPS;
+ }
+
+ if (getenv("SPOOLER_KEY")) {
+ spooler = SPOOLER_SOLARIS;
+ /* set the printer name from the ppd file name */
+ strncpy_omit(job->ppdfile, getenv("PPD"), 256, omit_specialchars);
+ file_basename(job->printer, job->ppdfile, 256);
+ /* TODO read attribute file*/
+ }
+
+ if (getenv("PPR_VERSION"))
+ spooler = SPOOLER_PPR;
+
+ if (getenv("PPR_RIPOPTS")) {
+ /* PPR 1.5 allows the user to specify options for the PPR RIP with the
+ "--ripopts" option on the "ppr" command line. They are provided to
+ the RIP via the "PPR_RIPOPTS" environment variable. */
+ dstrcatf(job->optstr, "%s ", getenv("PPR_RIPOPTS"));
+ spooler = SPOOLER_PPR;
+ }
+
+ if (getenv("LPOPTS")) { /* "LPOPTS": Option settings for some LPD implementations (ex: GNUlpr) */
+ spooler = SPOOLER_GNULPR;
+ dstrcatf(job->optstr, "%s ", getenv("LPOPTS"));
+ }
+
+ /* Check for LPRng first so we do not pick up bogus ppd files by the -ppd option */
+ if (arglist_remove_flag(arglist, "--lprng"))
+ spooler = SPOOLER_LPRNG;
+
+ /* 'PRINTCAP_ENTRY' environment variable is : LPRng
+ the :ppd=/path/to/ppdfile printcap entry should be used */
+ if (getenv("PRINTCAP_ENTRY")) {
+ spooler = SPOOLER_LPRNG;
+ if ((str = strstr(getenv("PRINTCAP_ENTRY"), "ppd=")))
+ str += 4;
+ else if ((str = strstr(getenv("PRINTCAP_ENTRY"), "ppdfile=")));
+ str += 8;
+ if (str) {
+ while (isspace(*str)) str++;
+ p = job->ppdfile;
+ while (*str != '\0' && !isspace(*str) && *str != '\n') {
+ if (isprint(*str) && strchr(shellescapes, *str) == NULL)
+ *p++ = *str;
+ str++;
+ }
+ }
+ }
+
+ /* PPD file name given via the command line
+ allow duplicates, and use the last specified one */
+ if (spooler != SPOOLER_LPRNG) {
+ while ((str = arglist_get_value(arglist, "-p"))) {
+ strncpy(job->ppdfile, str, 256);
+ arglist_remove(arglist, "-p");
+ }
+ }
+ while ((str = arglist_get_value(arglist, "--ppd"))) {
+ strncpy(job->ppdfile, str, 256);
+ arglist_remove(arglist, "--ppd");
+ }
+
+ /* Check for LPD/GNUlpr by typical options which the spooler puts onto
+ the filter's command line (options "-w": text width, "-l": text
+ length, "-i": indent, "-x", "-y": graphics size, "-c": raw printing,
+ "-n": user name, "-h": host name) */
+ if ((str = arglist_get_value(arglist, "-h"))) {
+ if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
+ spooler = SPOOLER_LPD;
+ strncpy(job->host, str, 127);
+ job->host[127] = '\0';
+ arglist_remove(arglist, "-h");
+ }
+ if ((str = arglist_get_value(arglist, "-n"))) {
+ if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
+ spooler = SPOOLER_LPD;
+
+ strncpy(job->user, str, 127);
+ job->user[127] = '\0';
+ arglist_remove(arglist, "-n");
+ }
+ if (arglist_remove(arglist, "-w") ||
+ arglist_remove(arglist, "-l") ||
+ arglist_remove(arglist, "-x") ||
+ arglist_remove(arglist, "-y") ||
+ arglist_remove(arglist, "-i") ||
+ arglist_remove_flag(arglist, "-c")) {
+ if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
+ spooler = SPOOLER_LPD;
+ }
+ /* LPRng delivers the option settings via the "-Z" argument */
+ if ((str = arglist_get_value(arglist, "-Z"))) {
+ spooler = SPOOLER_LPRNG;
+ dstrcatf(job->optstr, "%s ", str);
+ arglist_remove(arglist, "-Z");
+ }
+ /* Job title and options for stock LPD */
+ if ((str = arglist_get_value(arglist, "-j")) || (str = arglist_get_value(arglist, "-J"))) {
+ strncpy_omit(job->title, str, 128, omit_shellescapes);
+ if (spooler == SPOOLER_LPD)
+ dstrcatf(job->optstr, "%s ", job->title);
+ if (!arglist_remove(arglist, "-j"))
+ arglist_remove(arglist, "-J");
+ }
+ /* Check for CPS */
+ if (arglist_remove_flag(arglist, "--cps") > 0)
+ spooler = SPOOLER_CPS;
+
+ /* Options for spooler-less printing, CPS, or PDQ */
+ while ((str = arglist_get_value(arglist, "-o"))) {
+ strncpy_omit(tmp, str, 1024, omit_shellescapes);
+ dstrcatf(job->optstr, "%s ", tmp);
+ arglist_remove(arglist, "-o");
+ /* If we don't print as PPR RIP or as CPS filter, we print
+ without spooler (we check for PDQ later) */
+ if (spooler != SPOOLER_PPR && spooler != SPOOLER_CPS)
+ spooler = SPOOLER_DIRECT;
+ }
+
+ /* Printer for spooler-less printing or PDQ */
+ if ((str = arglist_get_value(arglist, "-d"))) {
+ strncpy_omit(job->printer, str, 256, omit_shellescapes);
+ arglist_remove(arglist, "-d");
+ }
+
+ /* Printer for spooler-less printing, PDQ, or LPRng */
+ if ((str = arglist_get_value(arglist, "-P"))) {
+ strncpy_omit(job->printer, str, 256, omit_shellescapes);
+ arglist_remove(arglist, "-P");
+ }
+
+ /* Were we called from a PDQ wrapper? */
+ if (arglist_remove_flag(arglist, "--pdq"))
+ spooler = SPOOLER_PDQ;
+
+ /* Were we called to build the PDQ driver declaration file? */
+ genpdqfile = check_pdq_file(arglist);
+ if (genpdqfile)
+ spooler = SPOOLER_PDQ;
+
+ /* spooler specific initialization */
+ switch (spooler) {
+ case SPOOLER_PPR:
+ init_ppr(arglist, job);
+ break;
+
+ case SPOOLER_CUPS:
+ init_cups(arglist, filelist, job);
+ break;
+
+ case SPOOLER_LPD:
+ case SPOOLER_LPRNG:
+ case SPOOLER_GNULPR:
+ /* Get PPD file name as the last command line argument */
+ if (arglist->last)
+ strncpy(job->ppdfile, (char*)arglist->last->data, 256);
+ break;
+
+ case SPOOLER_DIRECT:
+ case SPOOLER_CPS:
+ case SPOOLER_PDQ:
+ init_direct_cps_pdq(arglist, filelist, job);
+ break;
+ }
+
+ /* Files to be printed (can be more than one for spooler-less printing) */
+ /* Empty file list -> print STDIN */
+ dstrtrim(filelist);
+ if (filelist->len == 0)
+ dstrcpyf(filelist, "<STDIN>");
+
+ /* Check filelist */
+ p = strtok(filelist->data, " ");
+ while (p) {
+ if (strcmp(p, "<STDIN>") != 0) {
+ if (p[0] == '-')
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Invalid argument: %s", p);
+ else if (access(p, R_OK) != 0) {
+ _log("File %s does not exist/is not readable\n", p);
+ strclr(p);
+ }
+ }
+ p = strtok(NULL, " ");
+ }
+
+ /* When we print without spooler or with CPS do not log onto STDERR unless
+ the "-v" ('Verbose') is set or the debug mode is used */
+ if ((spooler == SPOOLER_DIRECT || spooler == SPOOLER_CPS || genpdqfile) && !verbose && !debug) {
+ if (logh && logh != stderr)
+ fclose(logh);
+ logh = NULL;
+ }
+
+ /* If we are in debug mode, we do this earlier. */
+ if (!debug) {
+ _log("foomatic-rip version " VERSION " running...\n");
+ /* Print the command line only in debug mode, Mac OS X adds very many
+ options so that CUPS cannot handle the output of the command line
+ in its log files. If CUPS encounters a line with more than 1024
+ characters sent into its log files, it aborts the job with an error. */
+ if (spooler != SPOOLER_CUPS) {
+ _log("called with arguments: ");
+ for (i = 1; i < argc -1; i++)
+ _log("\'%s\', ", argv[i]);
+ _log("\'%s\'\n", argv[i]);
+ }
+ }
+
+ /* PPD File */
+ /* Load the PPD file and build a data structure for the renderer's
+ command line and the options */
+ if (!(ppdfh = fopen(job->ppdfile, "r")))
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Unable to open PPD file %s\n", job->ppdfile);
+
+ read_ppd_file(job->ppdfile);
+
+ /* We do not need to parse the PostScript job when we don't have
+ any options. If we have options, we must check whether the
+ default settings from the PPD file are valid and correct them
+ if nexessary. */
+ if (option_count() == 0) {
+ /* We don't have any options, so we do not need to parse the
+ PostScript data */
+ dontparse = 1;
+ }
+
+ /* Is our PPD for a CUPS raster driver */
+ if (!isempty(cupsfilter)) {
+ /* Search the filter in cupsfilterpath
+ The %Y is a placeholder for the option settings */
+ havefilter = 0;
+ path = cupsfilterpath;
+ while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
+ strlcat(tmp, "/", 1024);
+ strlcat(tmp, cupsfilter, 1024);
+ if (access(tmp, X_OK) == 0) {
+ havefilter = 1;
+ strlcpy(cupsfilter, tmp, 256);
+ strlcat(cupsfilter, " 0 '' '' 0 '%Y%X'", 256);
+ break;
+ }
+ }
+
+ if (!havefilter) {
+ /* We do not have the required filter, so we assume that
+ rendering this job is supposed to be done on a remote
+ server. So we do not define a renderer command line and
+ embed only the option settings (as we had a PostScript
+ printer). This way the settings are taken into account
+ when the job is rendered on the server.*/
+ _log("CUPS filter for this PPD file not found - assuming that job will "
+ "be rendered on a remote server. Only the PostScript of the options"
+ "will be inserted into the PostScript data stream.\n");
+ }
+ else {
+ /* use pstoraster script if available, otherwise run Ghostscript directly */
+ havepstoraster = 0;
+ path = cupsfilterpath;
+ while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
+ strlcat(tmp, "/pstoraster", 1024);
+ if (access(tmp, X_OK) == 0) {
+ havepstoraster = 1;
+ strlcpy(pstoraster, tmp, 256);
+ strlcat(pstoraster, " 0 '' '' 0 '%X'", 256);
+ break;
+ }
+ }
+ if (!havepstoraster) {
+ strcpy(pstoraster, "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOMEDIAATTRS -sDEVICE=cups -sOutputFile=-%W -");
+ }
+
+ /* build Ghostscript/CUPS driver command line */
+ snprintf(cmd, 1024, "%s | %s", pstoraster, cupsfilter);
+
+ /* Set environment variables */
+ setenv("PPD", job->ppdfile, 1);
+ }
+ }
+
+ /* Was the RIP command line defined in the PPD file? If not, we assume a PostScript printer
+ and do not render/translate the input data */
+ if (isempty(cmd)) {
+ strcpy(cmd, "cat%A%B%C%D%E%F%G%H%I%J%K%L%M%Z");
+ if (dontparse) {
+ /* No command line, no options, we have a raw queue, don't check
+ whether the input is PostScript and ignore the "docs" option,
+ simply pass the input data to the backend.*/
+ dontparse = 2;
+ strcpy(printer_model, "Raw queue");
+ }
+ }
+
+ /* Summary for debugging */
+ _log("\nParameter Summary\n"
+ "-----------------\n\n"
+ "Spooler: %s\n"
+ "Printer: %s\n"
+ "Shell: %s\n"
+ "PPD file: %s\n"
+ "ATTR file: %s\n"
+ "Printer model: %s\n",
+ spooler_name(spooler), job->printer, get_modern_shell(), job->ppdfile, attrpath, printer_model);
+ /* Print the options string only in debug mode, Mac OS X adds very many
+ options so that CUPS cannot handle the output of the option string
+ in its log files. If CUPS encounters a line with more than 1024 characters
+ sent into its log files, it aborts the job with an error.*/
+ if (debug || spooler != SPOOLER_CUPS)
+ _log("Options: %s\n", job->optstr->data);
+ _log("Job title: %s\n", job->title);
+ _log("File(s) to be printed:\n");
+ _log("%s\n\n", filelist->data);
+ if (getenv("GS_LIB"))
+ _log("Ghostscript extra search path ('GS_LIB'): %s\n", getenv("GS_LIB"));
+
+ /* Process options from command line,
+ but save the defaults for printing documentation pages first */
+ optionset_copy_values(optionset("default"), optionset("userval"));
+ process_cmdline_options();
+
+ /* Were we called to build the PDQ driver declaration file? */
+ if (genpdqfile) {
+ print_pdq_driver(genpdqfile, optionset("userval"));
+ fclose(genpdqfile);
+ exit(EXIT_PRINTED);
+ }
+
+ if (spooler == SPOOLER_PPR_INT) {
+ snprintf(tmp, 1024, "interfaces/%s", backend);
+ if (access(tmp, X_OK) != 0)
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "The backend interface "
+ "/interfaces/%s does not exist/ is not executable!\n", backend);
+
+ /* foomatic-rip cannot use foomatic-rip as backend */
+ if (!strcmp(backend, "foomatic-rip"))
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "\"foomatic-rip\" cannot "
+ "use itself as backend interface!\n");
+
+ /* Put the backend interface into the postpipe */
+ /* TODO
+ $postpipe = "| ( interfaces/$backend \"$ppr_printer\" ".
+ "\"$ppr_address\" \"" . join(" ",@backendoptions) .
+ "\" \"$ppr_jobbreak\" \"$ppr_feedback\" " .
+ "\"$ppr_codes\" \"$ppr_jobname\" \"$ppr_routing\" " .
+ "\"$ppr_for\" \"\" )";
+ */
+ }
+
+ /* no postpipe for CUPS or PDQ, even if one is defined in the PPD file */
+ if (spooler == SPOOLER_CUPS || spooler == SPOOLER_PDQ)
+ dstrclear(postpipe);
+
+ /* CPS always needs a postpipe, set the default one for local printing if none is set */
+ if (spooler == SPOOLER_CPS && !postpipe->len)
+ dstrcpy(postpipe, "| cat - > $LPDDEV");
+
+ if (postpipe->len)
+ _log("Ouput will be redirected to:\n%s\n", postpipe);
+
+
+ /* Print documentation page when asked for */
+ if (do_docs) {
+ /* Don't print the supplied files, STDIN will be redirected to the
+ documentation page generator */
+ dstrcpyf(filelist, "<STDIN>");
+
+ /* Start the documentation page generator */
+ /* TODO tbd */
+ }
+
+ /* In debug mode save the data supposed to be fed into the
+ renderer also into a file, reset the file here */
+ if (debug)
+ run_system_process("reset-file", "> " LOG_FILE ".ps");
+
+ filename = strtok_r(filelist->data, " ", &p);
+ while (filename) {
+ _log("\n================================================\n\n"
+ "File: %s\n\n"
+ "================================================\n\n", filename);
+
+ /* Do we have a raw queue? */
+ if (dontparse == 2) {
+ /* Raw queue, simply pass the input into the postpipe (or to STDOUT
+ when there is no postpipe) */
+ _log("Raw printing, executing \"cat %s\"\n\n");
+ snprintf(tmp, 1024, "cat %s", postpipe->data);
+ run_system_process("raw-printer", tmp);
+ continue;
+ }
+
+ /* First, for arguments with a default, stick the default in as
+ the initial value for the "header" option set, this option set
+ consists of the PPD defaults, the options specified on the
+ command line, and the options set in the header part of the
+ PostScript file (all before the first page begins). */
+ optionset_copy_values(optionset("userval"), optionset("header"));
+
+ print_file(filename, 1);
+ filename = strtok_r(NULL, " ", &p);
+ }
+
+ /* Close documentation page generator */
+ /* if (docgenerator_pid) {
+ retval = close_docgenerator_handle(dogenerator_handle, docgenerator_pid);
+ if (!retval != EXIT_PRINTED) {
+ _log("Error closing documentation page generator\n");
+ exit(retval);
+ }
+ docgenerator_pid = 0;
+ } */
+
+ /* Close the last input file */
+ fclose(stdin);
+
+ /* TODO dump everything in $dat when debug is turned on (necessary?) */
+
+ _log("\nClosing foomatic-rip.\n");
+
+
+ /* Cleanup */
+ free_job(job);
+ if (genpdqfile && genpdqfile != stdout)
+ fclose(genpdqfile);
+ free_dstr(filelist);
+ options_free();
+ close_log();
+
+ argv_free(jclprepend);
+ free_dstr(jclappend);
+ if (backendoptions)
+ free_dstr(backendoptions);
+
+ list_free(arglist);
+
+ return 0;
+}
+