1 /* foomaticrip.c
2  *
3  * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4  * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de>
5  *
6  * This file is part of foomatic-rip.
7  *
8  * Foomatic-rip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Foomatic-rip is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #include "foomaticrip.h"
25 #include "util.h"
26 #include "options.h"
27 #include "pdf.h"
28 #include "postscript.h"
29 #include "process.h"
30 #include "spooler.h"
31 #include "renderer.h"
32 #include "fileconverter.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <errno.h>
38 #include <memory.h>
39 #include <ctype.h>
40 #include <stdarg.h>
41 #include <assert.h>
42 #include <unistd.h>
43 #include <sys/wait.h>
44 #include <math.h>
45 #include <signal.h>
46 #include <pwd.h>
47 
48 #ifdef HAVE_DBUS
49   #include "colord.h"
50 #endif
51 
52 /* Logging */
53 FILE* logh = NULL;
54 
_logv(const char * msg,va_list ap)55 void _logv(const char *msg, va_list ap)
56 {
57     if (!logh)
58         return;
59     vfprintf(logh, msg, ap);
60     fflush(logh);
61 }
62 
_log(const char * msg,...)63 void _log(const char* msg, ...)
64 {
65     va_list ap;
66     va_start(ap, msg);
67     _logv(msg, ap);
68     va_end(ap);
69 }
70 
close_log()71 void close_log()
72 {
73     if (logh && logh != stderr)
74         fclose(logh);
75 }
76 
redirect_log_to_stderr()77 int redirect_log_to_stderr()
78 {
79     if (dup2(fileno(logh), fileno(stderr)) < 0) {
80         _log("Could not dup logh to stderr\n");
81         return 0;
82     }
83     return 1;
84 }
85 
rip_die(int status,const char * msg,...)86 void rip_die(int status, const char *msg, ...)
87 {
88     va_list ap;
89 
90     _log("Process is dying with \"");
91     va_start(ap, msg);
92     _logv(msg, ap);
93     va_end(ap);
94     _log("\", exit stat %d\n", status);
95 
96     _log("Cleaning up...\n");
97     kill_all_processes();
98 
99     exit(status);
100 }
101 
102 
103 jobparams_t  *job = NULL;
104 
get_current_job()105 jobparams_t * get_current_job()
106 {
107     assert(job);
108     return job;
109 }
110 
111 
112 dstr_t *postpipe = NULL;  /* command into which the output of this filter should be piped */
113 FILE *postpipe_fh = NULL;
114 
open_postpipe()115 FILE * open_postpipe()
116 {
117     const char *p;
118 
119     if (postpipe_fh)
120         return postpipe_fh;
121 
122     if (isempty(postpipe->data))
123         return stdout;
124 
125     /* Delete possible '|' symbol in the beginning */
126     p = skip_whitespace(postpipe->data);
127     if (*p && *p == '|')
128         p += 1;
129 
130     if (start_system_process("postpipe", p, &postpipe_fh, NULL) < 0)
131         rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
132                 "Cannot execute postpipe %s\n", postpipe->data);
133 
134     return postpipe_fh;
135 }
136 
137 
138 char printer_model[256] = "";
139 const char *accounting_prolog = NULL;
140 char attrpath[256] = "";
141 
142 
143 int spooler = SPOOLER_DIRECT;
144 int do_docs = 0;
145 int dontparse = 0;
146 int jobhasjcl;
147 int pdfconvertedtops;
148 
149 /* Variable for PPR's backend interface name (parallel, tcpip, atalk, ...) */
150 char backend [64];
151 
152 /* Array to collect unknown options so that they can get passed to the
153 backend interface of PPR. For other spoolers we ignore them. */
154 dstr_t *backendoptions = NULL;
155 
156 /* These variables were in 'dat' before */
157 char colorprofile [128];
158 char cupsfilter[256];
159 char **jclprepend = NULL;
160 dstr_t *jclappend;
161 
162 /* Set debug to 1 to enable the debug logfile for this filter; it will appear
163  * as defined by LOG_FILE. It will contain status from this filter, plus the
164  * renderer's stderr output. You can also add a line "debug: 1" to your
165  * /etc/foomatic/filter.conf to get all your Foomatic filters into debug mode.
166  * WARNING: This logfile is a security hole; do not use in production. */
167 int debug = 0;
168 
169 /* Path to the GhostScript which foomatic-rip shall use */
170 char gspath[PATH_MAX] = "gs";
171 
172 /* What 'echo' program to use.  It needs -e and -n.  Linux's builtin
173 and regular echo work fine; non-GNU platforms may need to install
174 gnu echo and put gecho here or something. */
175 char echopath[PATH_MAX] = "echo";
176 
177 /* CUPS raster drivers are searched here */
178 char cupsfilterpath[PATH_MAX] = "/usr/local/lib/cups/filter:"
179                                 "/usr/local/libexec/cups/filter:"
180                                 "/opt/cups/filter:"
181                                 "/usr/lib/cups/filter";
182 
183 char modern_shell[64] = "/bin/sh";
184 
config_set_option(const char * key,const char * value)185 void config_set_option(const char *key, const char *value)
186 {
187     if (strcmp(key, "debug") == 0)
188         debug = atoi(value);
189 
190     /* What path to use for filter programs and such. Your printer driver must be
191      * in the path, as must be the renderer, $enscriptcommand, and possibly other
192      * stuff. The default path is often fine on Linux, but may not be on other
193      * systems. */
194     else if (strcmp(key, "execpath") == 0 && !isempty(value))
195         setenv("PATH", value, 1);
196 
197     else if (strcmp(key, "cupsfilterpath") == 0)
198         strlcpy(cupsfilterpath, value, PATH_MAX);
199     else if (strcmp(key, "preferred_shell") == 0)
200         strlcpy(modern_shell, value, 32);
201     else if (strcmp(key, "textfilter") == 0)
202         set_fileconverter(value);
203     else if (strcmp(key, "gspath") == 0)
204         strlcpy(gspath, value, PATH_MAX);
205     else if (strcmp(key, "echo") == 0)
206         strlcpy(echopath, value, PATH_MAX);
207 }
208 
config_from_file(const char * filename)209 void config_from_file(const char *filename)
210 {
211     FILE *fh;
212     char line[256];
213     char *key, *value;
214 
215     fh = fopen(filename, "r");
216     if (fh == NULL)
217         return; /* no error here, only read config file if it is present */
218 
219     while (fgets(line, 256, fh) != NULL)
220     {
221         key = strtok(line, " :\t\r\n");
222         if (key == NULL || key[0] == '#')
223             continue;
224         value = strtok(NULL, " \t\r\n#");
225         config_set_option(key, value);
226     }
227     fclose(fh);
228 }
229 
get_modern_shell()230 const char * get_modern_shell()
231 {
232     return modern_shell;
233 }
234 
235 /* returns position in 'str' after the option */
extract_next_option(char * str,char ** pagerange,char ** key,char ** value)236 char * extract_next_option(char *str, char **pagerange, char **key, char **value)
237 {
238     char *p = str;
239     char quotechar;
240 
241     *pagerange = NULL;
242     *key = NULL;
243     *value = NULL;
244 
245     if (!str)
246         return NULL;
247 
248     /* skip whitespace and commas */
249     while (*p && (isspace(*p) || *p == ',')) p++;
250 
251     if (!*p)
252         return NULL;
253 
254     /* read the pagerange if we have one */
255     if (prefixcmp(p, "even:") == 0 || prefixcmp(p, "odd:") == 0 || isdigit(*p)) {
256         *pagerange = p;
257         p = strchr(p, ':');
258         if (!p)
259             return NULL;
260         *p = '\0';
261         p++;
262     }
263 
264     /* read the key */
265     if (*p == '\'' || *p == '\"') {
266         quotechar = *p;
267         *key = p +1;
268         p = strchr(*key, quotechar);
269         if (!p)
270             return NULL;
271     }
272     else {
273         *key = p;
274         while (*p && *p != ':' && *p != '=' && *p != ' ') p++;
275     }
276 
277     if (*p != ':' && *p != '=') { /* no value for this option */
278         if (!*p)
279             return NULL;
280         else if (isspace(*p)) {
281             *p = '\0';
282             return p +1;
283         }
284         return p;
285     }
286 
287     *p++ = '\0'; /* remove the separator sign */
288 
289     if (*p == '\"' || *p == '\'') {
290         quotechar = *p;
291         *value = p +1;
292         p = strchr(*value, quotechar);
293         if (!p)
294             return NULL;
295         *p = '\0';
296         p++;
297     }
298     else {
299         *value = p;
300         while (*p && *p != ' ' && *p != ',') p++;
301         if (*p == '\0')
302             return NULL;
303         *p = '\0';
304         p++;
305     }
306 
307     return *p ? p : NULL;
308 }
309 
310 /* processes job->optstr */
process_cmdline_options()311 void process_cmdline_options()
312 {
313     char *p, *cmdlineopts, *nextopt, *pagerange, *key, *value;
314     option_t *opt, *opt2;
315     int optset;
316     char tmp [256];
317 
318     _log("Printing system options:\n");
319     cmdlineopts = strdup(job->optstr->data);
320     for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
321         key;
322         nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
323     {
324         /* Consider only options which are not in the PPD file here */
325         if ((opt = find_option(key)) != NULL) continue;
326         if (value)
327             _log("Pondering option '%s=%s'\n", key, value);
328         else
329             _log("Pondering option '%s'\n", key);
330 
331         /* "docs" option to print help page */
332         if (!strcasecmp(key, "docs")) {
333             do_docs = 1;
334             continue;
335         }
336         /* "profile" option to supply a color correction profile to a CUPS raster driver */
337         if (!strcmp(key, "profile")) {
338             strlcpy(colorprofile, value, 128);
339             continue;
340         }
341         /* Solaris options that have no reason to be */
342         if (!strcmp(key, "nobanner") || !strcmp(key, "dest") || !strcmp(key, "protocol"))
343             continue;
344 
345         if (pagerange) {
346             snprintf(tmp, 256, "pages:%s", pagerange);
347             optset = optionset(tmp);
348         }
349         else
350             optset = optionset("userval");
351 
352         if (value) {
353             /* At first look for the "backend" option to determine the PPR backend to use */
354             if (spooler == SPOOLER_PPR_INT && !strcasecmp(key, "backend")) {
355                 /* backend interface name */
356                 strlcpy(backend, value, 64);
357             }
358             else if (strcasecmp(key, "media") == 0) {
359                 /*  Standard arguments?
360                     media=x,y,z
361                     sides=one|two-sided-long|short-edge
362 
363                     Rummage around in the media= option for known media, source,
364                     etc types.
365                     We ought to do something sensible to make the common manual
366                     boolean option work when specified as a media= tray thing.
367 
368                     Note that this fails miserably when the option value is in
369                     fact a number; they all look alike.  It's unclear how many
370                     drivers do that.  We may have to standardize the verbose
371                     names to make them work as selections, too. */
372 
373                 p = strtok(value, ",");
374                 do {
375                     if ((opt = find_option("PageSize")) && option_accepts_value(opt, p))
376                         option_set_value(opt, optset, p);
377                     else if ((opt = find_option("MediaType")) && option_has_choice(opt, p))
378                         option_set_value(opt, optset, p);
379                     else if ((opt = find_option("InputSlot")) && option_has_choice(opt, p))
380                         option_set_value(opt, optset, p);
381                     else if (!strcasecmp(p, "manualfeed")) {
382                         /* Special case for our typical boolean manual
383                            feeder option if we didn't match an InputSlot above */
384                         if ((opt = find_option("ManualFeed")))
385                             option_set_value(opt, optset, "1");
386                     }
387                     else
388                         _log("Unknown \"media\" component: \"%s\".\n", p);
389 
390                 } while ((p = strtok(NULL, ",")));
391             }
392             else if (!strcasecmp(key, "sides")) {
393                 /* Handle the standard duplex option, mostly */
394                 if (!prefixcasecmp(value, "two-sided")) {
395                     if ((opt = find_option("Duplex"))) {
396                         /* Default to long-edge binding here, for the case that
397                            there is no binding setting */
398                         option_set_value(opt, optset, "DuplexNoTumble");
399 
400                         /* Check the binding: "long edge" or "short edge" */
401                         if (strcasestr(value, "long-edge")) {
402                             if ((opt2 = find_option("Binding")))
403                                 option_set_value(opt2, optset, "LongEdge");
404                             else
405                                 option_set_value(opt, optset, "DuplexNoTumble");
406                         }
407                         else if (strcasestr(value, "short-edge")) {
408                             if ((opt2 = find_option("Binding")))
409                                 option_set_value(opt2, optset, "ShortEdge");
410                             else
411                                 option_set_value(opt, optset, "DuplexNoTumble");
412                         }
413                     }
414                 }
415                 else if (!prefixcasecmp(value, "one-sided")) {
416                     if ((opt = find_option("Duplex")))
417                         option_set_value(opt, optset, "0");
418                 }
419 
420                 /*  TODO
421                     We should handle the other half of this option - the
422                     BindEdge bit.  Also, are there well-known ipp/cups options
423                     for Collate and StapleLocation?  These may be here...
424                 */
425             }
426 	    else if (spooler == SPOOLER_PPR_INT) {
427 	        /* Unknown option, pass it to PPR's backend interface */
428 	        if (!backendoptions)
429 		    backendoptions = create_dstr();
430 		dstrcatf(backendoptions, "%s=%s ", key, value);
431 	    }
432 	    else
433 	        _log("Unknown option %s=%s.\n", key, value);
434         }
435         /* Custom paper size */
436         else if ((opt = find_option("PageSize")) && option_set_value(opt, optset, key)) {
437             /* do nothing, if the value could be set, it has been set */
438         }
439         else
440             _log("Unknown boolean option \"%s\".\n", key);
441     }
442     free(cmdlineopts);
443 
444     _log("Options from the PPD file:\n");
445     cmdlineopts = strdup(job->optstr->data);
446     for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
447         key;
448         nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
449     {
450         /* Consider only PPD file options here */
451         if ((opt = find_option(key)) == NULL) continue;
452         if (value)
453             _log("Pondering option '%s=%s'\n", key, value);
454         else
455             _log("Pondering option '%s'\n", key);
456 
457         if (pagerange) {
458             snprintf(tmp, 256, "pages:%s", pagerange);
459             optset = optionset(tmp);
460 
461             if (opt && (option_get_section(opt) != SECTION_ANYSETUP &&
462                         option_get_section(opt) != SECTION_PAGESETUP)) {
463                 _log("This option (%s) is not a \"PageSetup\" or \"AnySetup\" option, so it cannot be restricted to a page range.\n", key);
464                 continue;
465             }
466         }
467         else
468             optset = optionset("userval");
469 
470         if (value) {
471 	    /* Various non-standard printer-specific options */
472 	    if (!option_set_value(opt, optset, value)) {
473 	        _log("  invalid choice \"%s\", using \"%s\" instead\n",
474 		     value, option_get_value(opt, optset));
475 	    }
476         }
477         /* Standard bool args:
478            landscape; what to do here?
479            duplex; we should just handle this one OK now? */
480         else if (!prefixcasecmp(key, "no"))
481             option_set_value(opt, optset, "0");
482         else
483             option_set_value(opt, optset, "1");
484     }
485     free(cmdlineopts);
486 }
487 
488 /* checks whether a pdq driver declaration file should be build
489    and returns an opened file handle if so */
check_pdq_file(list_t * arglist)490 FILE * check_pdq_file(list_t *arglist)
491 {
492     /* "--appendpdq=<file>" appends the data to the <file>,
493        "--genpdq=<file>" creates/overwrites <file> for the data, and
494        "--genpdq" writes to standard output */
495 
496     listitem_t *i;
497     char filename[256];
498     FILE *handle;
499     char *p;
500     int raw, append;
501 
502     if ((i = arglist_find_prefix(arglist, "--genpdq"))) {
503         raw = 0;
504         append = 0;
505     }
506     else if ((i = arglist_find_prefix(arglist, "--genrawpdq"))) {
507         raw = 1;
508         append = 0;
509     }
510     else if ((i = arglist_find_prefix(arglist, "--appendpdq"))) {
511         raw = 0;
512         append = 1;
513     }
514     else if ((i = arglist_find_prefix(arglist, "--appendrawpdq"))) {
515         raw = 1;
516         append = 1;
517     }
518 
519     if (!i)
520         return NULL;
521 
522     p = strchr((char*)i->data, '=');
523     if (p) {
524         strncpy_omit(filename, p +1, 256, omit_shellescapes);
525         handle = fopen(filename, append ? "a" : "w");
526         if (!handle)
527             rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Cannot write PDQ driver declaration file.\n");
528     }
529     else if (!append)
530         handle = stdout;
531     else
532         return NULL;
533 
534     /* remove option from args */
535     list_remove(arglist, i);
536 
537     /* Do we have a pdq driver declaration for a raw printer */
538     if (raw) {
539         fprintf(handle,
540                 "driver \"Raw-Printer-%u\" {\n"
541                 "  # This PDQ driver declaration file was generated automatically by\n"
542                 "  # foomatic-rip to allow raw (filter-less) printing.\n"
543                 "  language_driver all {\n"
544                 "    # We accept all file types and pass them through without any changes\n"
545                 "    filetype_regx \"\"\n"
546                 "    convert_exec {\n"
547                 "      ln -s $INPUT $OUTPUT\n"
548                 "    }\n"
549                 "  }\n"
550                 "  filter_exec {\n"
551                 "    ln -s $INPUT $OUTPUT\n"
552                 "  }\n"
553                 "}", (unsigned int)job->time);
554         if (handle != stdout) {
555             fclose(handle);
556             handle = NULL;
557         }
558         exit(EXIT_PRINTED);
559     }
560 
561     return handle;
562 }
563 
564 
565 /* Build a PDQ driver description file to use the given PPD file
566    together with foomatic-rip with the PDQ printing system
567    and output it into 'pdqfile' */
print_pdq_driver(FILE * pdqfile,int optset)568 void print_pdq_driver(FILE *pdqfile, int optset)
569 {
570 }
571 #if 0
572     option_t *opt;
573     value_t *val;
574     setting_t *setting, *setting_true, *setting_false;
575 
576     /* Construct option list */
577     dstr_t *driveropts = create_dstr();
578 
579     /* Do we have a "Custom" setting for the page size?
580        Then we have to insert the following into the filter_exec script. */
581     dstr_t *setcustompagesize = create_dstr();
582 
583     dstr_t *tmp = create_dstr();
584     dstr_t *cmdline = create_dstr();
585     dstr_t *psfilter = create_dstr();
586 
587 
588     /* 1, if setting "PageSize=Custom" was found
589        Then we must add options for page width and height */
590     int custompagesize = 0;
591 
592     /* Data for a custom page size, to allow a custom size as default */
593     int pagewidth = 612;
594     int pageheight = 792;
595     char pageunit[2] = "pt";
596 
597     char def [128];
598 
599     def[0] = '\0';
600 
601     for (opt = optionlist; opt; opt = opt->next) {
602         if (opt->type == TYPE_ENUM) {
603             /* Option with only one choice, omit it, foomatic-rip will set
604                this choice anyway */
605             if (option_setting_count(opt) <= 1)
606                 continue;
607 
608             /* Omit "PageRegion" option, it does the same as "PageSize" */
609             if (!strcmp(opt->name, "PageRegion"))
610                 continue;
611 
612             /* 1, if setting "PageSize=Custom" was found
613                Then we must add options for page width and height */
614             custompagesize = 0;
615 
616             if ((val = option_get_value(opt, optset)))
617                 strlcpy(def, val->value, 128);
618 
619 #if 0 TODO not used ?!
620             /* If the default is a custom size we have to set also
621                defaults for the width, height, and units of the page */
622             if (!strcmp(opt->name, "PageSize") && val && value_has_custom_setting(val))
623                 strcpy(def, "Custom");
624 #endif
625 
626             dstrcatf(driveropts,
627                     "  option {\n"
628                     "    var = \"%s\"\n"
629                     "    desc = \"%s\"\n", opt->varname, option_text(opt));
630 
631             /* get enumeration values for each enum arg */
632             dstrclear(tmp);
633             for (setting = opt->settinglist; setting; setting = setting->next)  {
634                 dstrcatf(tmp,
635                     "    choice \"%s_%s\" {\n"
636                     "      desc = \"%s\"\n"
637                     "      value = \" -o %s=%s\"\n"
638                     "    }\n",
639                      opt->name, setting->value,
640                       isempty(setting->comment) ? setting->value : setting->comment,
641                     opt->name, setting->value);
642 
643                 if (!strcmp(opt->name, "PageSize") && !strcmp(setting->value, "Custom")) {
644                     custompagesize = 1;
645                     if (isempty(setcustompagesize->data)) {
646                         dstrcatf(setcustompagesize,
647                             "      # Custom page size settings\n"
648                             "      # We aren't really checking for legal vals.\n"
649                             "      if [ \"x${%s}\" = 'x -o %s=%s' ]; then\n"
650                             "        %s=\"${%s}.${PageWidth}x${PageHeight}${PageSizeUnit}\"\n"
651                             "      fi\n\n",
652                             opt->varname, opt->varname, setting->value, opt->varname, opt->varname);
653                     }
654                 }
655             }
656 
657             dstrcatf(driveropts, "    default_choice \"%s_%s\"\n", opt->name, def);
658             dstrcatf(driveropts, tmp->data);
659             dstrcatf(driveropts, "  }\n\n");
660 
661             if (custompagesize) {
662                 /* Add options to set the custom page size */
663                 dstrcatf(driveropts,
664                     "  argument {\n"
665                     "    var = \"PageWidth\"\n"
666                     "    desc = \"Page Width (for \\\"Custom\\\" page size)\"\n"
667                     "    def_value \"%d\"\n"                      /* pagewidth */
668                     "    help = \"Minimum value: 0, Maximum value: 100000\"\n"
669                     "  }\n\n"
670                     "  argument {\n"
671                     "    var = \"PageHeight\"\n"
672                     "    desc = \"Page Height (for \\\"Custom\\\" page size)\"\n"
673                     "    def_value \"%d\"\n"                      /* pageheight */
674                     "    help = \"Minimum value: 0, Maximum value: 100000\"\n"
675                     "  }\n\n"
676                     "  option {\n"
677                     "    var = \"PageSizeUnit\"\n"
678                     "    desc = \"Unit (for \\\"Custom\\\" page size)\"\n"
679                     "    default_choice \"PageSizeUnit_%.2s\"\n"  /* pageunit */
680                     "    choice \"PageSizeUnit_pt\" {\n"
681                     "      desc = \"Points (1/72 inch)\"\n"
682                     "      value = \"pt\"\n"
683                     "    }\n"
684                     "    choice \"PageSizeUnit_in\" {\n"
685                     "      desc = \"Inches\"\n"
686                     "      value = \"in\"\n"
687                     "    }\n"
688                     "    choice \"PageSizeUnit_cm\" {\n"
689                     "      desc = \"cm\"\n"
690                     "      value = \"cm\"\n"
691                     "    }\n"
692                     "    choice \"PageSizeUnit_mm\" {\n"
693                     "      desc = \"mm\"\n"
694                     "      value = \"mm\"\n"
695                     "    }\n"
696                     "  }\n\n",
697                     pagewidth, pageheight, pageunit);
698             }
699         }
700         else if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT) {
701             /* Assure that the comment is not emtpy */
702             if (isempty(opt->comment))
703                 strcpy(opt->comment, opt->name);
704 
705             if ((val = option_get_value(opt, optset)))
706                 strlcpy(def, val->value, 128);
707 
708             strcpy(opt->varname, opt->name);
709             strrepl(opt->varname, "-/.", '_');
710 
711 
712             dstrcatf(driveropts,
713                 "  argument {\n"
714                 "    var = \"%s\"\n"
715                 "    desc = \"%s\"\n"
716                 "    def_value \"%s\"\n"
717                 "    help = \"Minimum value: %s, Maximum value: %s\"\n"
718                 "  }\n\n",
719                 opt->varname, opt->comment, def, opt->min, opt->max);
720         }
721         else if (opt->type == TYPE_BOOL) {
722             /* Assure that the comment is not emtpy */
723             if (isempty(opt->comment))
724                 strcpy(opt->comment, opt->name);
725 
726             if ((val = option_get_value(opt, optset)))
727                 strlcpy(def, val->value, 128);
728             strcpy(opt->varname, opt->name);
729             strrepl(opt->varname, "-/.", '_');
730             setting_true = option_find_setting(opt, "true");
731             setting_false = option_find_setting(opt, "false");
732 
733             dstrcatf(driveropts,
734                 "  option {\n"
735                 "    var = \"%s\"\n"
736                 "    desc = \"%s\"\n", opt->varname, opt->comment);
737 
738             if (!isempty(def) && !strcasecmp(def, "true"))
739                 dstrcatf(driveropts, "    default_choice \"%s\"\n", def);
740             else
741                 dstrcatf(driveropts, "    default_choice \"no%s\"\n", def);
742 
743             dstrcatf(driveropts,
744                 "    choice \"%s\" {\n"
745                 "      desc = \"%s\"\n"
746                 "      value = \" -o %s=True\"\n"
747                 "    }\n"
748                 "    choice \"no%s\" {\n"
749                 "      desc = \"%s\"\n"
750                 "      value = \" -o %s=False\"\n"
751                 "    }\n"
752                 "  }\n\n",
753                 opt->name, setting_true->comment, opt->name,
754                 opt->name, setting_false->comment, opt->name);
755         }
756         else if (opt->type == TYPE_STRING) {
757             /* Assure that the comment is not emtpy */
758             if (isempty(opt->comment))
759                 strcpy(opt->comment, opt->name);
760 
761             if ((val = option_get_value(opt, optset)))
762                 strlcpy(def, val->value, 128);
763             strcpy(opt->varname, opt->name);
764             strrepl_nodups(opt->varname, "-/.", '_');
765 
766             dstrclear(tmp);
767             if (opt->maxlength)
768                 dstrcatf(tmp, "Maximum Length: %s characters, ", opt->maxlength);
769 
770             dstrcatf(tmp, "Examples/special settings: ");
771             for (setting = opt->settinglist; setting; setting = setting->next)  {
772                 /* Retrieve the original string from the prototype and the driverval */
773                 /* TODO perl code for this part doesn't make sense to me */
774             }
775         }
776     }
777 
778     /* Define the "docs" option to print the driver documentation page */
779     dstrcatf(driveropts,
780         "  option {\n"
781         "    var = \"DRIVERDOCS\"\n"
782         "    desc = \"Print driver usage information\"\n"
783         "    default_choice \"nodocs\"\n"
784         "    choice \"docs\" {\n"
785         "      desc = \"Yes\"\n"
786         "      value = \" -o docs\"\n"
787         "    }\n"
788         "    choice \"nodocs\" {\n"
789         "      desc = \"No\"\n"
790         "      value = \"\"\n"
791         "    }\n"
792         "  }\n\n");
793 
794     /* Build the foomatic-rip command line */
795     dstrcatf(cmdline, "foomatic-rip --pdq");
796     if (!isempty(printer)) {
797         dstrcatf(cmdline, " -P %s", printer);
798     }
799     else {
800         /* Make sure that the PPD file is entered with an absolute path */
801         make_absolute_path(job->ppdfile, 256);
802         dstrcatf(cmdline, " --ppd=%s", job->ppdfile);
803     }
804 
805     for (opt = optionlist; opt; opt = opt->next) {
806         if (!isempty(opt->varname))
807             dstrcatf(cmdline, "${%s}", opt->varname);
808     }
809     dstrcatf(cmdline, "${DRIVERDOCS} $INPUT > $OUTPUT");
810 
811 
812     /* Now we generate code to build the command line snippets for the numerical options */
813     for (opt = optionlist; opt; opt = opt->next) {
814         /* Only numerical and string options need to be treated here */
815         if (opt->type != TYPE_INT &&
816             opt->type != TYPE_FLOAT &&
817             opt->type != TYPE_STRING)
818             continue;
819 
820         /* If the option's variable is non-null, put in the
821            argument.  Otherwise this option is the empty
822            string.  Error checking? */
823         dstrcatf(psfilter, "      # %s\n", opt->comment);
824         if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT) {
825             dstrcatf(psfilter,
826                 "      # We aren't really checking for max/min,\n"
827                 "      # this is done by foomatic-rip\n"
828                 "      if [ \"x${%s}\" != 'x' ]; then\n  ", opt->varname);
829         }
830 
831         dstrcatf(psfilter, "      %s=\" -o %s='${%s}'\"\n", opt->varname, opt->name, opt->varname);
832 
833         if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT)
834             dstrcatf(psfilter, "      fi\n");
835         dstrcatf(psfilter, "\n");
836     }
837 
838     /* Command execution */
839     dstrcatf(psfilter,
840         "      if ! test -e $INPUT.ok; then\n"
841         "        sh -c \"%s\"\n"
842         "        if ! test -e $OUTPUT; then \n"
843         "          echo 'Error running foomatic-rip; no output!'\n"
844         "          exit 1\n"
845         "        fi\n"
846         "      else\n"
847         "        ln -s $INPUT $OUTPUT\n"
848         "      fi\n\n", cmdline->data);
849 
850 
851     dstrclear(tmp);
852     dstrcatf(tmp, "%s", printer_model);
853     strrepl_nodups(tmp->data, " \t\n.,;/()[]{}+*", '-');
854     tmp->len = strlen(tmp->data); /* length could have changed */
855     if (tmp->data[tmp->len -1] == '-') {
856         tmp->data[--tmp->len] = '\0';
857     }
858 
859 
860     fprintf(pdqfile,
861         "driver \"%s-%u\" {\n\n"
862         "  # This PDQ driver declaration file was generated automatically by\n"
863         "  # foomatic-rip from information in the file %s.\n" /* ppdfile */
864         "  # It allows printing with PDQ on the %s.\n"        /* model */
865         "\n"
866         "  requires \"foomatic-rip\"\n\n"
867         "%s" /* driveropts */
868         "  language_driver all {\n"
869         "    # We accept all file types and pass them to foomatic-rip\n"
870         "    # (invoked in \"filter_exec {}\" section) without\n"
871         "    # pre-filtering\n"
872         "    filetype_regx \"\"\n"
873         "    convert_exec {\n"
874         "      ln -s $INPUT $OUTPUT\n"
875         "    }\n"
876         "  }\n\n"
877         "  filter_exec {\n"
878         "%s" /* setcustompagesize */
879         "%s" /* psfilter */
880         "  }\n"
881         "}\n",
882         tmp->data, /* cleaned printer_model */ (unsigned int)job->time, job->ppdfile, printer_model,
883         driveropts->data, setcustompagesize->data, psfilter->data);
884 
885 
886     free_dstr(setcustompagesize);
887     free_dstr(driveropts);
888     free_dstr(tmp);
889     free_dstr(cmdline);
890     free_dstr(psfilter);
891 }
892 #endif
893 
894 /*  Functions to let foomatic-rip fork to do several tasks in parallel.
895 
896 To do the filtering without loading the whole file into memory we work
897 on a data stream, we read the data line by line analyse it to decide what
898 filters to use and start the filters if we have found out which we need.
899 We buffer the data only as long as we didn't determing which filters to
900 use for this piece of data and with which options. There are no temporary
901 files used.
902 
903 foomatic-rip splits into up to 6 parallel processes to do the whole
904 filtering (listed in the order of the data flow):
905 
906    KID0: Generate documentation pages (only jobs with "docs" option)
907    KID2: Put together already read data and current input stream for
908          feeding into the file conversion filter (only non-PostScript
909          and "docs" jobs)
910    KID1: Run the file conversion filter to convert non-PostScript
911          input into PostScript (only non-PostScript and "docs" jobs)
912    MAIN: Prepare the job auto-detecting the spooler, reading the PPD,
913          extracting the options from the command line, and parsing
914          the job data itself. It analyses the job data to check
915          whether it is PostScript and starts KID1/KID2 if not, it
916          also stuffs PostScript code from option settings into the
917          PostScript data stream. It starts the renderer (KID3/KID4)
918          as soon as it knows its command line and restarts it when
919          page-specific option settings need another command line
920          or different JCL commands.
921    KID3: The rendering process. In most cases Ghostscript, "cat"
922          for native PostScript printers with their manufacturer's
923          PPD files.
924    KID4: Put together the JCL commands and the renderer's output
925          and send all that either to STDOUT or pipe it into the
926          command line defined with $postpipe. */
927 
928 
929 
write_output(void * data,size_t len)930 void write_output(void *data, size_t len)
931 {
932     const char *p = (const char *)data;
933     size_t left = len;
934     FILE *postpipe = open_postpipe();
935 
936     /* Remove leading whitespace */
937     while (isspace(*p++) && left-- > 0)
938         ;
939 
940     fwrite((void *)p, left, 1, postpipe);
941     fflush(postpipe);
942 }
943 
944 enum FileType {
945     UNKNOWN_FILE,
946     PDF_FILE,
947     PS_FILE
948 };
949 
guess_file_type(const char * begin,size_t len,int * startpos)950 int guess_file_type(const char *begin, size_t len, int *startpos)
951 {
952     const char * p, * end;
953     p = begin;
954     end = begin + len;
955 
956     while (p < end)
957     {
958         p = memchr(p, '%', end - p);
959 	if (!p)
960 	    return UNKNOWN_FILE;
961 	*startpos = p - begin;
962 	if ((end - p) > 2 && !memcmp(p, "%!", 2))
963 	    return PS_FILE;
964 	else if ((end - p) > 7 && !memcmp(p, "%PDF-1.", 7))
965 	    return PDF_FILE;
966 	++ p;
967     }
968     *startpos = 0;
969     return UNKNOWN_FILE;
970 }
971 
972 /*
973  * Prints 'filename'. If 'convert' is true, the file will be converted if it is
974  * not postscript or pdf
975  */
print_file(const char * filename,int convert)976 int print_file(const char *filename, int convert)
977 {
978     FILE *file;
979     char buf[8192];
980     int type;
981     int startpos;
982     size_t n;
983     FILE *fchandle = NULL;
984     int fcpid = 0, ret;
985 
986     if (!strcasecmp(filename, "<STDIN>"))
987         file = stdin;
988     else {
989         file = fopen(filename, "r");
990         if (!file) {
991             _log("Could not open \"%s\" for reading\n", filename);
992             return 0;
993         }
994     }
995 
996     n = fread(buf, 1, sizeof(buf) - 1, file);
997     buf[n] = '\0';
998     type = guess_file_type(buf, n, &startpos);
999     /* We do not use any JCL preceeded to the inputr data, as it is simply
1000        the PJL commands from the PPD file, and these commands we can also
1001        generate, end we even merge them with PJl from the driver */
1002     /*if (startpos > 0) {
1003         jobhasjcl = 1;
1004         write_output(buf, startpos);
1005     }*/
1006     if (file != stdin)
1007         rewind(file);
1008 
1009     if (convert) pdfconvertedtops = 0;
1010 
1011     switch (type) {
1012         case PDF_FILE:
1013             _log("Filetype: PDF\n");
1014 
1015             if (!ppd_supports_pdf())
1016             {
1017                 char pdf2ps_cmd[PATH_MAX];
1018                 FILE *out, *in;
1019                 int renderer_pid;
1020 		char tmpfilename[PATH_MAX] = "";
1021 
1022                 _log("Driver does not understand PDF input, "
1023                      "converting to PostScript\n");
1024 
1025 		pdfconvertedtops = 1;
1026 
1027 		/* If reading from stdin, write everything into a temporary file */
1028 		if (file == stdin)
1029                 {
1030 		    int fd;
1031 		    FILE *tmpfile;
1032 
1033 		    snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
1034 		    fd = mkstemp(tmpfilename);
1035 		    if (fd < 0) {
1036 		        _log("Could not create temporary file: %s\n", strerror(errno));
1037 		        return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
1038 		    }
1039 		    tmpfile = fdopen(fd, "r+");
1040 		    copy_file(tmpfile, stdin, buf, n);
1041 		    fclose(tmpfile);
1042 
1043 		    filename = tmpfilename;
1044 		}
1045 
1046 		/* If the spooler is CUPS we use the pdftops filter of CUPS,
1047 		   to have always the same PDF->PostScript conversion method
1048 		   in the whole printing environment, including incompatibility
1049 		   workarounds in the CUPS filter (so this way we also have to
1050 		   maintain all these quirks only once).
1051 
1052 		   The "-dNOINTERPOLATE" makes Ghostscript rendering
1053 		   significantly faster.
1054 
1055 		   Note that Ghostscript's "pswrite" output device turns text
1056 		   into bitmaps and therefore produces huge PostScript files.
1057 		   In addition, this output device is deprecated. Therefore
1058 		   we use "ps2write".
1059 
1060 		   We give priority to Ghostscript here and use Poppler if
1061 		   Ghostscript is not available. */
1062 		if (spooler == SPOOLER_CUPS)
1063 		  snprintf(pdf2ps_cmd, PATH_MAX,
1064 			   "/usr/local/libexec/cups/filter/pdftops '%s' '%s' '%s' '%s' '%s' '%s'",
1065 			   job->id, job->user, job->title, "1", job->optstr->data,
1066 			   filename);
1067 		else
1068 		  snprintf(pdf2ps_cmd, PATH_MAX,
1069 			   "gs -q -sstdout=%%stderr -sDEVICE=ps2write -sOutputFile=- "
1070 			   "-dBATCH -dNOPAUSE -dPARANOIDSAFER -dNOINTERPOLATE %s 2>/dev/null || "
1071 			   "pdftops -level2 -origpagesizes %s - 2>/dev/null",
1072 			   filename, filename);
1073 
1074                 renderer_pid = start_system_process("pdf-to-ps", pdf2ps_cmd, &in, &out);
1075 
1076                 if (dup2(fileno(out), fileno(stdin)) < 0)
1077                     rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
1078                             "Couldn't dup stdout of pdf-to-ps\n");
1079 
1080                 ret = print_file("<STDIN>", 0);
1081 
1082                 wait_for_process(renderer_pid);
1083                 return ret;
1084             }
1085 
1086             if (file == stdin)
1087                 return print_pdf(stdin, buf, n, filename, startpos);
1088             else
1089                 return print_pdf(file, NULL, 0, filename, startpos);
1090 
1091         case PS_FILE:
1092             _log("Filetype: PostScript\n");
1093             if (file == stdin)
1094                 return print_ps(stdin, buf, n, filename);
1095             else
1096                 return print_ps(file, NULL, 0, filename);
1097 
1098         case UNKNOWN_FILE:
1099             if (spooler == SPOOLER_CUPS) {
1100                 _log("Cannot process \"%s\": Unknown filetype.\n", filename);
1101                 return 0;
1102             }
1103 
1104             _log("Filetype unknown, trying to convert ...\n");
1105             get_fileconverter_handle(buf, &fchandle, &fcpid);
1106 
1107             /* Read further data from the file converter and not from STDIN */
1108             if (dup2(fileno(fchandle), fileno(stdin)) < 0)
1109                 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't dup fileconverterhandle\n");
1110 
1111             ret = print_file("<STDIN>", 0);
1112 
1113             if (close_fileconverter_handle(fchandle, fcpid) != EXIT_PRINTED)
1114                 rip_die(ret, "Error closing file converter\n");
1115             return ret;
1116     }
1117 
1118     fclose(file);
1119     return 1;
1120 }
1121 
signal_terminate(int signal)1122 void signal_terminate(int signal)
1123 {
1124     rip_die(EXIT_PRINTED, "Caught termination signal: Job canceled\n");
1125 }
1126 
create_job()1127 jobparams_t * create_job()
1128 {
1129     jobparams_t *job = calloc(1, sizeof(jobparams_t));
1130     struct passwd *passwd;
1131 
1132     job->optstr = create_dstr();
1133     job->time = time(NULL);
1134     strcpy(job->copies, "1");
1135     gethostname(job->host, 128);
1136     passwd = getpwuid(getuid());
1137     if (passwd)
1138         strlcpy(job->user, passwd->pw_name, 128);
1139     snprintf(job->title, 128, "%s@%s", job->user, job->host);
1140 
1141     return job;
1142 }
1143 
free_job(jobparams_t * job)1144 void free_job(jobparams_t *job)
1145 {
1146     free_dstr(job->optstr);
1147     free(job);
1148 }
1149 
main(int argc,char ** argv)1150 int main(int argc, char** argv)
1151 {
1152     int i;
1153     int verbose = 0, quiet = 0, showdocs = 0;
1154     const char* str;
1155     char *p, *filename;
1156     const char *path;
1157     FILE *genpdqfile = NULL;
1158     FILE *ppdfh = NULL;
1159     char tmp[1024], pstoraster[256];
1160     int havefilter, havepstoraster;
1161     dstr_t *filelist;
1162     list_t * arglist;
1163 
1164     arglist = list_create_from_array(argc -1, (void**)&argv[1]);
1165 
1166     if (argc == 2 && (arglist_find(arglist, "--version") || arglist_find(arglist, "--help") ||
1167                 arglist_find(arglist, "-v") || arglist_find(arglist, "-h"))) {
1168         printf("foomatic rip version "VERSION"\n");
1169         printf("\"man foomatic-rip\" for help.\n");
1170         list_free(arglist);
1171         return 0;
1172     }
1173 
1174     filelist = create_dstr();
1175     job = create_job();
1176 
1177     jclprepend = NULL;
1178     jclappend = create_dstr();
1179     postpipe = create_dstr();
1180 
1181     options_init();
1182 
1183     signal(SIGTERM, signal_terminate);
1184     signal(SIGINT, signal_terminate);
1185 
1186 
1187     config_from_file(CONFIG_PATH "/filter.conf");
1188 
1189     /* Command line options for verbosity */
1190     if (arglist_remove_flag(arglist, "-v"))
1191         verbose = 1;
1192     if (arglist_remove_flag(arglist, "-q"))
1193         quiet = 1;
1194     if (arglist_remove_flag(arglist, "-d"))
1195         showdocs = 1;
1196     if (arglist_remove_flag(arglist, "--debug"))
1197         debug = 1;
1198 
1199     if (debug) {
1200 	sprintf(tmp, "%s-XXXXXX.log", LOG_FILE);
1201 	int fd = mkstemps (tmp, 4);
1202 	if (fd != -1)
1203 	    logh = fdopen(fd, "w");
1204 	else
1205 	    logh = stderr;
1206     } else if (quiet && !verbose)
1207         logh = NULL; /* Quiet mode, do not log */
1208     else
1209         logh = stderr; /* Default: log to stderr */
1210 
1211     /* Start debug logging */
1212     if (debug) {
1213         /* If we are not in debug mode, we do this later, as we must find out at
1214         first which spooler is used. When printing without spooler we
1215         suppress logging because foomatic-rip is called directly on the
1216         command line and so we avoid logging onto the console. */
1217         _log("foomatic-rip version "VERSION" running...\n");
1218 
1219         /* Print the command line only in debug mode, Mac OS X adds very many
1220         options so that CUPS cannot handle the output of the command line
1221         in its log files. If CUPS encounters a line with more than 1024
1222         characters sent into its log files, it aborts the job with an error. */
1223         if (spooler != SPOOLER_CUPS) {
1224             _log("called with arguments: ");
1225             for (i = 1; i < argc -1; i++)
1226                 _log("\'%s\', ", argv[i]);
1227             _log("\'%s\'\n", argv[i]);
1228         }
1229     }
1230 
1231     if (getenv("PPD")) {
1232         strncpy(job->ppdfile, getenv("PPD"), 256);
1233         spooler = SPOOLER_CUPS;
1234     }
1235 
1236     if (getenv("SPOOLER_KEY")) {
1237         spooler = SPOOLER_SOLARIS;
1238         /* set the printer name from the ppd file name */
1239         strncpy_omit(job->ppdfile, getenv("PPD"), 256, omit_specialchars);
1240         file_basename(job->printer, job->ppdfile, 256);
1241         /* TODO read attribute file*/
1242     }
1243 
1244     if (getenv("PPR_VERSION"))
1245         spooler = SPOOLER_PPR;
1246 
1247     if (getenv("PPR_RIPOPTS")) {
1248         /* PPR 1.5 allows the user to specify options for the PPR RIP with the
1249            "--ripopts" option on the "ppr" command line. They are provided to
1250            the RIP via the "PPR_RIPOPTS" environment variable. */
1251         dstrcatf(job->optstr, "%s ", getenv("PPR_RIPOPTS"));
1252         spooler = SPOOLER_PPR;
1253     }
1254 
1255     if (getenv("LPOPTS")) { /* "LPOPTS": Option settings for some LPD implementations (ex: GNUlpr) */
1256         spooler = SPOOLER_GNULPR;
1257         dstrcatf(job->optstr, "%s ", getenv("LPOPTS"));
1258     }
1259 
1260     /* Check for LPRng first so we do not pick up bogus ppd files by the -ppd option */
1261     if (spooler != SPOOLER_CUPS && spooler != SPOOLER_PPR &&
1262 	spooler != SPOOLER_PPR_INT) {
1263         if (arglist_remove_flag(arglist, "--lprng"))
1264             spooler = SPOOLER_LPRNG;
1265     }
1266 
1267     /* 'PRINTCAP_ENTRY' environment variable is : LPRng
1268        the :ppd=/path/to/ppdfile printcap entry should be used */
1269     if (getenv("PRINTCAP_ENTRY")) {
1270         spooler = SPOOLER_LPRNG;
1271         if ((str = strstr(getenv("PRINTCAP_ENTRY"), "ppd=")))
1272             str += 4;
1273         else if ((str = strstr(getenv("PRINTCAP_ENTRY"), "ppdfile=")))
1274 	    str += 8;
1275         if (str) {
1276             while (isspace(*str)) str++;
1277             p = job->ppdfile;
1278             while (*str != '\0' && !isspace(*str) && *str != '\n' &&
1279 		   *str != ':') {
1280                 if (isprint(*str) && strchr(shellescapes, *str) == NULL)
1281                     *p++ = *str;
1282                 str++;
1283             }
1284         }
1285     }
1286 
1287     /* CUPS calls foomatic-rip only with 5 or 6 positional parameters,
1288        not with named options, like for example "-p <string>". Also PPR
1289        does not used named options. */
1290     if (spooler != SPOOLER_CUPS && spooler != SPOOLER_PPR &&
1291 	spooler != SPOOLER_PPR_INT) {
1292         /* Check for LPD/GNUlpr by typical options which the spooler puts onto
1293            the filter's command line (options "-w": text width, "-l": text
1294            length, "-i": indent, "-x", "-y": graphics size, "-c": raw printing,
1295            "-n": user name, "-h": host name) */
1296         if ((str = arglist_get_value(arglist, "-h"))) {
1297             if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
1298                 spooler = SPOOLER_LPD;
1299             strncpy(job->host, str, 127);
1300             job->host[127] = '\0';
1301             arglist_remove(arglist, "-h");
1302         }
1303         if ((str = arglist_get_value(arglist, "-n"))) {
1304             if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
1305                 spooler = SPOOLER_LPD;
1306 
1307             strncpy(job->user, str, 127);
1308             job->user[127] = '\0';
1309             arglist_remove(arglist, "-n");
1310         }
1311         if (arglist_remove(arglist, "-w") ||
1312             arglist_remove(arglist, "-l") ||
1313             arglist_remove(arglist, "-x") ||
1314             arglist_remove(arglist, "-y") ||
1315             arglist_remove(arglist, "-i") ||
1316             arglist_remove_flag(arglist, "-c")) {
1317                 if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
1318                     spooler = SPOOLER_LPD;
1319         }
1320         /* LPRng delivers the option settings via the "-Z" argument */
1321         if ((str = arglist_get_value(arglist, "-Z"))) {
1322             spooler = SPOOLER_LPRNG;
1323             dstrcatf(job->optstr, "%s ", str);
1324             arglist_remove(arglist, "-Z");
1325         }
1326         /* Job title and options for stock LPD */
1327         if ((str = arglist_get_value(arglist, "-j")) || (str = arglist_get_value(arglist, "-J"))) {
1328             strncpy_omit(job->title, str, 128, omit_shellescapes);
1329             if (spooler == SPOOLER_LPD)
1330                  dstrcatf(job->optstr, "%s ", job->title);
1331              if (!arglist_remove(arglist, "-j"))
1332                 arglist_remove(arglist, "-J");
1333         }
1334 
1335         /* Check for CPS */
1336         if (arglist_remove_flag(arglist, "--cps") > 0)
1337             spooler = SPOOLER_CPS;
1338 
1339         /* PPD file name given via the command line
1340            allow duplicates, and use the last specified one */
1341         if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG &&
1342 	    spooler != SPOOLER_LPD) {
1343             while ((str = arglist_get_value(arglist, "-p"))) {
1344                 strncpy(job->ppdfile, str, 256);
1345                 arglist_remove(arglist, "-p");
1346             }
1347 	    while ((str = arglist_get_value(arglist, "--ppd"))) {
1348 	        strncpy(job->ppdfile, str, 256);
1349 	        arglist_remove(arglist, "--ppd");
1350 	    }
1351         }
1352 
1353         /* Options for spooler-less printing, CPS, or PDQ */
1354         while ((str = arglist_get_value(arglist, "-o"))) {
1355             strncpy_omit(tmp, str, 1024, omit_shellescapes);
1356             dstrcatf(job->optstr, "%s ", tmp);
1357             arglist_remove(arglist, "-o");
1358             /* If we don't print as PPR RIP or as CPS filter, we print
1359                without spooler (we check for PDQ later) */
1360             if (spooler != SPOOLER_PPR && spooler != SPOOLER_CPS)
1361                 spooler = SPOOLER_DIRECT;
1362         }
1363 
1364         /* Printer for spooler-less printing or PDQ */
1365         if ((str = arglist_get_value(arglist, "-d"))) {
1366             strncpy_omit(job->printer, str, 256, omit_shellescapes);
1367             arglist_remove(arglist, "-d");
1368         }
1369 
1370         /* Printer for spooler-less printing, PDQ, or LPRng */
1371         if ((str = arglist_get_value(arglist, "-P"))) {
1372             strncpy_omit(job->printer, str, 256, omit_shellescapes);
1373             arglist_remove(arglist, "-P");
1374         }
1375 
1376         /* Were we called from a PDQ wrapper? */
1377         if (arglist_remove_flag(arglist, "--pdq"))
1378             spooler = SPOOLER_PDQ;
1379 
1380         /* Were we called to build the PDQ driver declaration file? */
1381         genpdqfile = check_pdq_file(arglist);
1382         if (genpdqfile)
1383             spooler = SPOOLER_PDQ;
1384     }
1385 
1386     /* spooler specific initialization */
1387     switch (spooler) {
1388         case SPOOLER_PPR:
1389             init_ppr(arglist, job);
1390             break;
1391 
1392         case SPOOLER_CUPS:
1393             init_cups(arglist, filelist, job);
1394             break;
1395 
1396         case SPOOLER_LPRNG:
1397 	    if (job->ppdfile[0] != '\0') break;
1398         case SPOOLER_LPD:
1399         case SPOOLER_GNULPR:
1400             /* Get PPD file name as the last command line argument */
1401             if (arglist->last)
1402                 strncpy(job->ppdfile, (char*)arglist->last->data, 256);
1403             break;
1404 
1405         case SPOOLER_DIRECT:
1406         case SPOOLER_CPS:
1407         case SPOOLER_PDQ:
1408             init_direct_cps_pdq(arglist, filelist, job);
1409             break;
1410     }
1411 
1412     /* Files to be printed (can be more than one for spooler-less printing) */
1413     /* Empty file list -> print STDIN */
1414     dstrtrim(filelist);
1415     if (filelist->len == 0)
1416         dstrcpyf(filelist, "<STDIN>");
1417 
1418     /* Check filelist */
1419     p = strtok(strdup(filelist->data), " ");
1420     while (p) {
1421         if (strcmp(p, "<STDIN>") != 0) {
1422             if (p[0] == '-')
1423                 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Invalid argument: %s", p);
1424             else if (access(p, R_OK) != 0) {
1425                 _log("File %s does not exist/is not readable\n", p);
1426             strclr(p);
1427             }
1428         }
1429         p = strtok(NULL, " ");
1430     }
1431 
1432     /* When we print without spooler or with CPS do not log onto STDERR unless
1433        the "-v" ('Verbose') is set or the debug mode is used */
1434     if ((spooler == SPOOLER_DIRECT || spooler == SPOOLER_CPS || genpdqfile) && !verbose && !debug) {
1435         if (logh && logh != stderr)
1436             fclose(logh);
1437         logh = NULL;
1438     }
1439 
1440     /* If we are in debug mode, we do this earlier. */
1441     if (!debug) {
1442         _log("foomatic-rip version " VERSION " running...\n");
1443         /* Print the command line only in debug mode, Mac OS X adds very many
1444         options so that CUPS cannot handle the output of the command line
1445         in its log files. If CUPS encounters a line with more than 1024
1446         characters sent into its log files, it aborts the job with an error. */
1447         if (spooler != SPOOLER_CUPS) {
1448             _log("called with arguments: ");
1449             for (i = 1; i < argc -1; i++)
1450                 _log("\'%s\', ", argv[i]);
1451             _log("\'%s\'\n", argv[i]);
1452         }
1453     }
1454 
1455     /* PPD File */
1456     /* Load the PPD file and build a data structure for the renderer's
1457        command line and the options */
1458     if (!(ppdfh = fopen(job->ppdfile, "r")))
1459         rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Unable to open PPD file %s\n", job->ppdfile);
1460 
1461     read_ppd_file(job->ppdfile);
1462 
1463     /* We do not need to parse the PostScript job when we don't have
1464        any options. If we have options, we must check whether the
1465        default settings from the PPD file are valid and correct them
1466        if nexessary. */
1467     if (option_count() == 0) {
1468         /* We don't have any options, so we do not need to parse the
1469            PostScript data */
1470         dontparse = 1;
1471     }
1472 
1473     /* Is our PPD for a CUPS raster driver */
1474     if (!isempty(cupsfilter)) {
1475         /* Search the filter in cupsfilterpath
1476            The %Y is a placeholder for the option settings */
1477         havefilter = 0;
1478         path = cupsfilterpath;
1479         while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
1480             strlcat(tmp, "/", 1024);
1481             strlcat(tmp, cupsfilter, 1024);
1482             if (access(tmp, X_OK) == 0) {
1483                 havefilter = 1;
1484                 strlcpy(cupsfilter, tmp, 256);
1485                 strlcat(cupsfilter, " 0 '' '' 0 '%Y%X'", 256);
1486                 break;
1487             }
1488         }
1489 
1490         if (!havefilter) {
1491             /* We do not have the required filter, so we assume that
1492                rendering this job is supposed to be done on a remote
1493                server. So we do not define a renderer command line and
1494                embed only the option settings (as we had a PostScript
1495                printer). This way the settings are taken into account
1496                when the job is rendered on the server.*/
1497             _log("CUPS filter for this PPD file not found - assuming that job will "
1498                  "be rendered on a remote server. Only the PostScript of the options"
1499                  "will be inserted into the PostScript data stream.\n");
1500         }
1501         else {
1502             /* use pstoraster script if available, otherwise run Ghostscript directly */
1503             havepstoraster = 0;
1504             path = cupsfilterpath;
1505             while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
1506                 strlcat(tmp, "/pstoraster", 1024);
1507                 if (access(tmp, X_OK) == 0) {
1508                     havepstoraster = 1;
1509                     strlcpy(pstoraster, tmp, 256);
1510                     strlcat(pstoraster, " 0 '' '' 0 '%X'", 256);
1511                     break;
1512                 }
1513                 /* gstoraster is the new name for pstoraster */
1514                 strlcat(tmp, "/gstoraster", 1024);
1515                 if (access(tmp, X_OK) == 0) {
1516                     havepstoraster = 1;
1517                     strlcpy(pstoraster, tmp, 256);
1518                     strlcat(pstoraster, " 0 '' '' 0 '%X'", 256);
1519                     break;
1520                 }
1521             }
1522             if (!havepstoraster) {
1523                 const char **qualifier = NULL;
1524                 const char *icc_profile = NULL;
1525 
1526                 qualifier = get_ppd_qualifier();
1527                 _log("INFO: Using qualifer: '%s.%s.%s'\n",
1528                       qualifier[0], qualifier[1], qualifier[2]);
1529 
1530 #ifdef HAVE_DBUS
1531                 /* ask colord for the profile */
1532                 icc_profile = colord_get_profile_for_device_id ((const char *) getenv("PRINTER"),
1533                                                                 qualifier);
1534 #endif
1535 
1536                 /* fall back to PPD */
1537                 if (icc_profile == NULL) {
1538                   _log("INFO: need to look in PPD for matching qualifer\n");
1539                   icc_profile = get_icc_profile_for_qualifier(qualifier);
1540                 }
1541 
1542                 if (icc_profile != NULL)
1543                   snprintf(cmd, sizeof(cmd),
1544                            "-sOutputICCProfile='%s'", icc_profile);
1545                 else
1546                   cmd[0] = '\0';
1547 
1548                 snprintf(pstoraster, sizeof(pstoraster), "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOINTERPOLATE -dNOMEDIAATTRS -sDEVICE=cups %s -sOutputFile=- -", cmd);
1549             }
1550 
1551             /* build Ghostscript/CUPS driver command line */
1552             snprintf(cmd, 1024, "%s | %s", pstoraster, cupsfilter);
1553             _log("INFO: Using command line: %s\n", cmd);
1554 
1555             /* Set environment variables */
1556             setenv("PPD", job->ppdfile, 1);
1557         }
1558     }
1559 
1560     /* Was the RIP command line defined in the PPD file? If not, we assume a PostScript printer
1561        and do not render/translate the input data */
1562     if (isempty(cmd)) {
1563         strcpy(cmd, "cat%A%B%C%D%E%F%G%H%I%J%K%L%M%Z");
1564         if (dontparse) {
1565             /* No command line, no options, we have a raw queue, don't check
1566                whether the input is PostScript and ignore the "docs" option,
1567                simply pass the input data to the backend.*/
1568             dontparse = 2;
1569             strcpy(printer_model, "Raw queue");
1570         }
1571     }
1572 
1573     /* Summary for debugging */
1574     _log("\nParameter Summary\n"
1575          "-----------------\n\n"
1576          "Spooler: %s\n"
1577          "Printer: %s\n"
1578          "Shell: %s\n"
1579          "PPD file: %s\n"
1580          "ATTR file: %s\n"
1581          "Printer model: %s\n",
1582         spooler_name(spooler), job->printer, get_modern_shell(), job->ppdfile, attrpath, printer_model);
1583     /* Print the options string only in debug mode, Mac OS X adds very many
1584        options so that CUPS cannot handle the output of the option string
1585        in its log files. If CUPS encounters a line with more than 1024 characters
1586        sent into its log files, it aborts the job with an error.*/
1587     if (debug || spooler != SPOOLER_CUPS)
1588         _log("Options: %s\n", job->optstr->data);
1589     _log("Job title: %s\n", job->title);
1590     _log("File(s) to be printed:\n");
1591     _log("%s\n\n", filelist->data);
1592     if (getenv("GS_LIB"))
1593         _log("Ghostscript extra search path ('GS_LIB'): %s\n", getenv("GS_LIB"));
1594 
1595     /* Process options from command line,
1596        but save the defaults for printing documentation pages first */
1597     optionset_copy_values(optionset("default"), optionset("userval"));
1598     process_cmdline_options();
1599 
1600     /* Were we called to build the PDQ driver declaration file? */
1601     if (genpdqfile) {
1602         print_pdq_driver(genpdqfile, optionset("userval"));
1603         fclose(genpdqfile);
1604         exit(EXIT_PRINTED);
1605     }
1606 
1607     if (spooler == SPOOLER_PPR_INT) {
1608         snprintf(tmp, 1024, "interfaces/%s", backend);
1609         if (access(tmp, X_OK) != 0)
1610             rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "The backend interface "
1611                     "/interfaces/%s does not exist/ is not executable!\n", backend);
1612 
1613         /* foomatic-rip cannot use foomatic-rip as backend */
1614         if (!strcmp(backend, "foomatic-rip"))
1615             rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "\"foomatic-rip\" cannot "
1616                     "use itself as backend interface!\n");
1617 
1618         /* Put the backend interface into the postpipe */
1619         /* TODO
1620             $postpipe = "| ( interfaces/$backend \"$ppr_printer\" ".
1621             "\"$ppr_address\" \"" . join(" ",@backendoptions) .
1622             "\" \"$ppr_jobbreak\" \"$ppr_feedback\" " .
1623             "\"$ppr_codes\" \"$ppr_jobname\" \"$ppr_routing\" " .
1624             "\"$ppr_for\" \"\" )";
1625         */
1626     }
1627 
1628     /* no postpipe for CUPS or PDQ, even if one is defined in the PPD file */
1629     if (spooler == SPOOLER_CUPS || spooler == SPOOLER_PDQ)
1630         dstrclear(postpipe);
1631 
1632     /* CPS always needs a postpipe, set the default one for local printing if none is set */
1633     if (spooler == SPOOLER_CPS && !postpipe->len)
1634         dstrcpy(postpipe, "| cat - > $LPDDEV");
1635 
1636     if (postpipe->len)
1637         _log("Ouput will be redirected to:\n%s\n", postpipe);
1638 
1639 
1640     /* Print documentation page when asked for */
1641     if (do_docs) {
1642         /* Don't print the supplied files, STDIN will be redirected to the
1643            documentation page generator */
1644         dstrcpyf(filelist, "<STDIN>");
1645 
1646         /* Start the documentation page generator */
1647         /* TODO tbd */
1648     }
1649 
1650     filename = strtok_r(filelist->data, " ", &p);
1651     while (filename) {
1652         _log("\n================================================\n\n"
1653              "File: %s\n\n"
1654              "================================================\n\n", filename);
1655 
1656         /* Do we have a raw queue? */
1657         if (dontparse == 2) {
1658             /* Raw queue, simply pass the input into the postpipe (or to STDOUT
1659                when there is no postpipe) */
1660             _log("Raw printing, executing \"cat %s\"\n\n");
1661             snprintf(tmp, 1024, "cat %s", postpipe->data);
1662             run_system_process("raw-printer", tmp);
1663             continue;
1664         }
1665 
1666         /* First, for arguments with a default, stick the default in as
1667            the initial value for the "header" option set, this option set
1668            consists of the PPD defaults, the options specified on the
1669            command line, and the options set in the header part of the
1670            PostScript file (all before the first page begins). */
1671         optionset_copy_values(optionset("userval"), optionset("header"));
1672 
1673         if (!print_file(filename, 1))
1674 	    rip_die(EXIT_PRNERR_NORETRY, "Could not print file %s\n", filename);
1675         filename = strtok_r(NULL, " ", &p);
1676     }
1677 
1678     /* Close documentation page generator */
1679     /* if (docgenerator_pid) {
1680         retval = close_docgenerator_handle(dogenerator_handle, docgenerator_pid);
1681         if (!retval != EXIT_PRINTED) {
1682             _log("Error closing documentation page generator\n");
1683             exit(retval);
1684         }
1685         docgenerator_pid = 0;
1686     } */
1687 
1688     /* Close the last input file */
1689     fclose(stdin);
1690 
1691     /* TODO dump everything in $dat when debug is turned on (necessary?) */
1692 
1693     _log("\nClosing foomatic-rip.\n");
1694 
1695 
1696     /* Cleanup */
1697     free_job(job);
1698     if (genpdqfile && genpdqfile != stdout)
1699         fclose(genpdqfile);
1700     free_dstr(filelist);
1701     options_free();
1702     close_log();
1703 
1704     argv_free(jclprepend);
1705     free_dstr(jclappend);
1706     if (backendoptions)
1707         free_dstr(backendoptions);
1708 
1709     list_free(arglist);
1710 
1711     return EXIT_PRINTED;
1712 }
1713 
1714