3
* Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4
* Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de>
6
* This file is part of foomatic-rip.
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.
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.
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.
24
#include "foomaticrip.h"
28
#include "postscript.h"
32
#include "fileconverter.h"
52
void _logv(const char *msg, va_list ap)
56
vfprintf(logh, msg, ap);
60
void _log(const char* msg, ...)
70
if (logh && logh != stderr)
74
int redirect_log_to_stderr()
76
if (dup2(fileno(logh), fileno(stderr)) < 0) {
77
_log("Could not dup logh to stderr\n");
83
void rip_die(int status, const char *msg, ...)
87
_log("Process is dying with \"");
91
_log("\", exit stat %d\n", status);
93
_log("Cleaning up...\n");
100
jobparams_t *job = NULL;
102
jobparams_t * get_current_job()
109
dstr_t *postpipe; /* command into which the output of this filter should be piped */
110
FILE *postpipe_fh = NULL;
112
FILE * open_postpipe()
119
if (isempty(postpipe->data))
122
/* Delete possible '|' symbol in the beginning */
123
p = skip_whitespace(postpipe->data);
127
if (start_system_process("postpipe", p, &postpipe_fh, NULL) < 0)
128
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
129
"Cannot execute postpipe %s\n", postpipe->data);
135
char printer_model[256] = "";
136
const char *accounting_prolog = NULL;
137
char attrpath[256] = "";
140
int spooler = SPOOLER_DIRECT;
144
int pdfconvertedtops;
146
/* Variable for PPR's backend interface name (parallel, tcpip, atalk, ...) */
149
/* Array to collect unknown options so that they can get passed to the
150
backend interface of PPR. For other spoolers we ignore them. */
151
dstr_t *backendoptions = NULL;
153
/* These variables were in 'dat' before */
154
char colorprofile [128];
155
char cupsfilter[256];
156
char **jclprepend = NULL;
159
/* Set debug to 1 to enable the debug logfile for this filter; it will appear
160
* as defined by LOG_FILE. It will contain status from this filter, plus the
161
* renderer's stderr output. You can also add a line "debug: 1" to your
162
* /etc/foomatic/filter.conf to get all your Foomatic filters into debug mode.
163
* WARNING: This logfile is a security hole; do not use in production. */
166
/* Path to the GhostScript which foomatic-rip shall use */
167
char gspath[PATH_MAX] = "gs";
169
/* What 'echo' program to use. It needs -e and -n. Linux's builtin
170
and regular echo work fine; non-GNU platforms may need to install
171
gnu echo and put gecho here or something. */
172
char echopath[PATH_MAX] = "echo";
174
/* CUPS raster drivers are searched here */
175
char cupsfilterpath[PATH_MAX] = "/usr/local/lib/cups/filter:"
176
"/usr/local/libexec/cups/filter:"
178
"/usr/lib/cups/filter";
180
char modern_shell[64] = "/bin/bash";
182
void config_set_option(const char *key, const char *value)
184
if (strcmp(key, "debug") == 0)
187
/* What path to use for filter programs and such. Your printer driver must be
188
* in the path, as must be the renderer, $enscriptcommand, and possibly other
189
* stuff. The default path is often fine on Linux, but may not be on other
191
else if (strcmp(key, "execpath") == 0 && !isempty(value))
192
setenv("PATH", value, 1);
194
else if (strcmp(key, "cupsfilterpath") == 0)
195
strlcpy(cupsfilterpath, value, PATH_MAX);
196
else if (strcmp(key, "preferred_shell") == 0)
197
strlcpy(modern_shell, value, 32);
198
else if (strcmp(key, "textfilter") == 0)
199
set_fileconverter(value);
200
else if (strcmp(key, "gspath") == 0)
201
strlcpy(gspath, value, PATH_MAX);
202
else if (strcmp(key, "echo") == 0)
203
strlcpy(echopath, value, PATH_MAX);
206
void config_from_file(const char *filename)
212
fh = fopen(filename, "r");
214
return; /* no error here, only read config file if it is present */
216
while (fgets(line, 256, fh) != NULL)
218
key = strtok(line, " :\t\r\n");
219
if (key == NULL || key[0] == '#')
221
value = strtok(NULL, " \t\r\n#");
222
config_set_option(key, value);
227
const char * get_modern_shell()
232
/* returns position in 'str' after the option */
233
char * extract_next_option(char *str, char **pagerange, char **key, char **value)
245
/* skip whitespace and commas */
246
while (*p && (isspace(*p) || *p == ',')) p++;
251
/* read the pagerange if we have one */
252
if (prefixcmp(p, "even:") == 0 || prefixcmp(p, "odd:") == 0 || isdigit(*p)) {
262
if (*p == '\'' || *p == '\"') {
265
p = strchr(*key, quotechar);
271
while (*p && *p != ':' && *p != '=' && *p != ' ') p++;
274
if (*p != ':' && *p != '=') { /* no value for this option */
277
else if (isspace(*p)) {
284
*p++ = '\0'; /* remove the separator sign */
286
if (*p == '\"' || *p == '\'') {
289
p = strchr(*value, quotechar);
297
while (*p && *p != ' ' && *p != ',') p++;
304
return *p ? p : NULL;
307
/* processes job->optstr */
308
void process_cmdline_options()
310
char *p, *cmdlineopts, *nextopt, *pagerange, *key, *value;
311
option_t *opt, *opt2;
315
_log("Printing system options:\n");
316
cmdlineopts = strdup(job->optstr->data);
317
for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
319
nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
321
/* Consider only options which are not in the PPD file here */
322
if ((opt = find_option(key)) != NULL) continue;
324
_log("Pondering option '%s=%s'\n", key, value);
326
_log("Pondering option '%s'\n", key);
328
/* "docs" option to print help page */
329
if (!strcasecmp(key, "docs")) {
333
/* "profile" option to supply a color correction profile to a CUPS raster driver */
334
if (!strcmp(key, "profile")) {
335
strlcpy(colorprofile, value, 128);
338
/* Solaris options that have no reason to be */
339
if (!strcmp(key, "nobanner") || !strcmp(key, "dest") || !strcmp(key, "protocol"))
343
snprintf(tmp, 256, "pages:%s", pagerange);
344
optset = optionset(tmp);
347
optset = optionset("userval");
350
/* At first look for the "backend" option to determine the PPR backend to use */
351
if (spooler == SPOOLER_PPR_INT && !strcasecmp(key, "backend")) {
352
/* backend interface name */
353
strlcpy(backend, value, 64);
355
else if (strcasecmp(key, "media") == 0) {
356
/* Standard arguments?
358
sides=one|two-sided-long|short-edge
360
Rummage around in the media= option for known media, source,
362
We ought to do something sensible to make the common manual
363
boolean option work when specified as a media= tray thing.
365
Note that this fails miserably when the option value is in
366
fact a number; they all look alike. It's unclear how many
367
drivers do that. We may have to standardize the verbose
368
names to make them work as selections, too. */
370
p = strtok(value, ",");
372
if ((opt = find_option("PageSize")) && option_accepts_value(opt, p))
373
option_set_value(opt, optset, p);
374
else if ((opt = find_option("MediaType")) && option_has_choice(opt, p))
375
option_set_value(opt, optset, p);
376
else if ((opt = find_option("InputSlot")) && option_has_choice(opt, p))
377
option_set_value(opt, optset, p);
378
else if (!strcasecmp(p, "manualfeed")) {
379
/* Special case for our typical boolean manual
380
feeder option if we didn't match an InputSlot above */
381
if ((opt = find_option("ManualFeed")))
382
option_set_value(opt, optset, "1");
385
_log("Unknown \"media\" component: \"%s\".\n", p);
387
} while ((p = strtok(NULL, ",")));
389
else if (!strcasecmp(key, "sides")) {
390
/* Handle the standard duplex option, mostly */
391
if (!prefixcasecmp(value, "two-sided")) {
392
if ((opt = find_option("Duplex"))) {
393
/* Default to long-edge binding here, for the case that
394
there is no binding setting */
395
option_set_value(opt, optset, "DuplexNoTumble");
397
/* Check the binding: "long edge" or "short edge" */
398
if (strcasestr(value, "long-edge")) {
399
if ((opt2 = find_option("Binding")))
400
option_set_value(opt2, optset, "LongEdge");
402
option_set_value(opt, optset, "DuplexNoTumble");
404
else if (strcasestr(value, "short-edge")) {
405
if ((opt2 = find_option("Binding")))
406
option_set_value(opt2, optset, "ShortEdge");
408
option_set_value(opt, optset, "DuplexNoTumble");
412
else if (!prefixcasecmp(value, "one-sided")) {
413
if ((opt = find_option("Duplex")))
414
option_set_value(opt, optset, "0");
418
We should handle the other half of this option - the
419
BindEdge bit. Also, are there well-known ipp/cups options
420
for Collate and StapleLocation? These may be here...
423
else if (spooler == SPOOLER_PPR_INT) {
424
/* Unknown option, pass it to PPR's backend interface */
426
backendoptions = create_dstr();
427
dstrcatf(backendoptions, "%s=%s ", key, value);
430
_log("Unknown option %s=%s.\n", key, value);
432
/* Custom paper size */
433
else if ((opt = find_option("PageSize")) && option_set_value(opt, optset, key)) {
434
/* do nothing, if the value could be set, it has been set */
437
_log("Unknown boolean option \"%s\".\n", key);
441
_log("Options from the PPD file:\n");
442
cmdlineopts = strdup(job->optstr->data);
443
for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
445
nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
447
/* Consider only PPD file options here */
448
if ((opt = find_option(key)) == NULL) continue;
450
_log("Pondering option '%s=%s'\n", key, value);
452
_log("Pondering option '%s'\n", key);
455
snprintf(tmp, 256, "pages:%s", pagerange);
456
optset = optionset(tmp);
458
if (opt && (option_get_section(opt) != SECTION_ANYSETUP &&
459
option_get_section(opt) != SECTION_PAGESETUP)) {
460
_log("This option (%s) is not a \"PageSetup\" or \"AnySetup\" option, so it cannot be restricted to a page range.\n", key);
465
optset = optionset("userval");
468
/* Various non-standard printer-specific options */
469
if (!option_set_value(opt, optset, value)) {
470
_log(" invalid choice \"%s\", using \"%s\" instead\n",
471
value, option_get_value(opt, optset));
474
/* Standard bool args:
475
landscape; what to do here?
476
duplex; we should just handle this one OK now? */
477
else if (!prefixcasecmp(key, "no"))
478
option_set_value(opt, optset, "0");
480
option_set_value(opt, optset, "1");
485
/* checks whether a pdq driver declaration file should be build
486
and returns an opened file handle if so */
487
FILE * check_pdq_file(list_t *arglist)
489
/* "--appendpdq=<file>" appends the data to the <file>,
490
"--genpdq=<file>" creates/overwrites <file> for the data, and
491
"--genpdq" writes to standard output */
499
if ((i = arglist_find_prefix(arglist, "--genpdq"))) {
503
else if ((i = arglist_find_prefix(arglist, "--genrawpdq"))) {
507
else if ((i = arglist_find_prefix(arglist, "--appendpdq"))) {
511
else if ((i = arglist_find_prefix(arglist, "--appendrawpdq"))) {
519
p = strchr((char*)i->data, '=');
521
strncpy_omit(filename, p +1, 256, omit_shellescapes);
522
handle = fopen(filename, append ? "a" : "w");
524
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Cannot write PDQ driver declaration file.\n");
531
/* remove option from args */
532
list_remove(arglist, i);
534
/* Do we have a pdq driver declaration for a raw printer */
537
"driver \"Raw-Printer-%u\" {\n"
538
" # This PDQ driver declaration file was generated automatically by\n"
539
" # foomatic-rip to allow raw (filter-less) printing.\n"
540
" language_driver all {\n"
541
" # We accept all file types and pass them through without any changes\n"
542
" filetype_regx \"\"\n"
544
" ln -s $INPUT $OUTPUT\n"
548
" ln -s $INPUT $OUTPUT\n"
550
"}", (unsigned int)job->time);
551
if (handle != stdout) {
562
/* Build a PDQ driver description file to use the given PPD file
563
together with foomatic-rip with the PDQ printing system
564
and output it into 'pdqfile' */
565
void print_pdq_driver(FILE *pdqfile, int optset)
571
setting_t *setting, *setting_true, *setting_false;
573
/* Construct option list */
574
dstr_t *driveropts = create_dstr();
576
/* Do we have a "Custom" setting for the page size?
577
Then we have to insert the following into the filter_exec script. */
578
dstr_t *setcustompagesize = create_dstr();
580
dstr_t *tmp = create_dstr();
581
dstr_t *cmdline = create_dstr();
582
dstr_t *psfilter = create_dstr();
585
/* 1, if setting "PageSize=Custom" was found
586
Then we must add options for page width and height */
587
int custompagesize = 0;
589
/* Data for a custom page size, to allow a custom size as default */
591
int pageheight = 792;
592
char pageunit[2] = "pt";
598
for (opt = optionlist; opt; opt = opt->next) {
599
if (opt->type == TYPE_ENUM) {
600
/* Option with only one choice, omit it, foomatic-rip will set
601
this choice anyway */
602
if (option_setting_count(opt) <= 1)
605
/* Omit "PageRegion" option, it does the same as "PageSize" */
606
if (!strcmp(opt->name, "PageRegion"))
609
/* 1, if setting "PageSize=Custom" was found
610
Then we must add options for page width and height */
613
if ((val = option_get_value(opt, optset)))
614
strlcpy(def, val->value, 128);
616
#if 0 TODO not used ?!
617
/* If the default is a custom size we have to set also
618
defaults for the width, height, and units of the page */
619
if (!strcmp(opt->name, "PageSize") && val && value_has_custom_setting(val))
620
strcpy(def, "Custom");
626
" desc = \"%s\"\n", opt->varname, option_text(opt));
628
/* get enumeration values for each enum arg */
630
for (setting = opt->settinglist; setting; setting = setting->next) {
632
" choice \"%s_%s\" {\n"
634
" value = \" -o %s=%s\"\n"
636
opt->name, setting->value,
637
isempty(setting->comment) ? setting->value : setting->comment,
638
opt->name, setting->value);
640
if (!strcmp(opt->name, "PageSize") && !strcmp(setting->value, "Custom")) {
642
if (isempty(setcustompagesize->data)) {
643
dstrcatf(setcustompagesize,
644
" # Custom page size settings\n"
645
" # We aren't really checking for legal vals.\n"
646
" if [ \"x${%s}\" = 'x -o %s=%s' ]; then\n"
647
" %s=\"${%s}.${PageWidth}x${PageHeight}${PageSizeUnit}\"\n"
649
opt->varname, opt->varname, setting->value, opt->varname, opt->varname);
654
dstrcatf(driveropts, " default_choice \"%s_%s\"\n", opt->name, def);
655
dstrcatf(driveropts, tmp->data);
656
dstrcatf(driveropts, " }\n\n");
658
if (custompagesize) {
659
/* Add options to set the custom page size */
662
" var = \"PageWidth\"\n"
663
" desc = \"Page Width (for \\\"Custom\\\" page size)\"\n"
664
" def_value \"%d\"\n" /* pagewidth */
665
" help = \"Minimum value: 0, Maximum value: 100000\"\n"
668
" var = \"PageHeight\"\n"
669
" desc = \"Page Height (for \\\"Custom\\\" page size)\"\n"
670
" def_value \"%d\"\n" /* pageheight */
671
" help = \"Minimum value: 0, Maximum value: 100000\"\n"
674
" var = \"PageSizeUnit\"\n"
675
" desc = \"Unit (for \\\"Custom\\\" page size)\"\n"
676
" default_choice \"PageSizeUnit_%.2s\"\n" /* pageunit */
677
" choice \"PageSizeUnit_pt\" {\n"
678
" desc = \"Points (1/72 inch)\"\n"
681
" choice \"PageSizeUnit_in\" {\n"
682
" desc = \"Inches\"\n"
685
" choice \"PageSizeUnit_cm\" {\n"
689
" choice \"PageSizeUnit_mm\" {\n"
694
pagewidth, pageheight, pageunit);
697
else if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT) {
698
/* Assure that the comment is not emtpy */
699
if (isempty(opt->comment))
700
strcpy(opt->comment, opt->name);
702
if ((val = option_get_value(opt, optset)))
703
strlcpy(def, val->value, 128);
705
strcpy(opt->varname, opt->name);
706
strrepl(opt->varname, "-/.", '_');
713
" def_value \"%s\"\n"
714
" help = \"Minimum value: %s, Maximum value: %s\"\n"
716
opt->varname, opt->comment, def, opt->min, opt->max);
718
else if (opt->type == TYPE_BOOL) {
719
/* Assure that the comment is not emtpy */
720
if (isempty(opt->comment))
721
strcpy(opt->comment, opt->name);
723
if ((val = option_get_value(opt, optset)))
724
strlcpy(def, val->value, 128);
725
strcpy(opt->varname, opt->name);
726
strrepl(opt->varname, "-/.", '_');
727
setting_true = option_find_setting(opt, "true");
728
setting_false = option_find_setting(opt, "false");
733
" desc = \"%s\"\n", opt->varname, opt->comment);
735
if (!isempty(def) && !strcasecmp(def, "true"))
736
dstrcatf(driveropts, " default_choice \"%s\"\n", def);
738
dstrcatf(driveropts, " default_choice \"no%s\"\n", def);
743
" value = \" -o %s=True\"\n"
745
" choice \"no%s\" {\n"
747
" value = \" -o %s=False\"\n"
750
opt->name, setting_true->comment, opt->name,
751
opt->name, setting_false->comment, opt->name);
753
else if (opt->type == TYPE_STRING) {
754
/* Assure that the comment is not emtpy */
755
if (isempty(opt->comment))
756
strcpy(opt->comment, opt->name);
758
if ((val = option_get_value(opt, optset)))
759
strlcpy(def, val->value, 128);
760
strcpy(opt->varname, opt->name);
761
strrepl_nodups(opt->varname, "-/.", '_');
765
dstrcatf(tmp, "Maximum Length: %s characters, ", opt->maxlength);
767
dstrcatf(tmp, "Examples/special settings: ");
768
for (setting = opt->settinglist; setting; setting = setting->next) {
769
/* Retrieve the original string from the prototype and the driverval */
770
/* TODO perl code for this part doesn't make sense to me */
775
/* Define the "docs" option to print the driver documentation page */
778
" var = \"DRIVERDOCS\"\n"
779
" desc = \"Print driver usage information\"\n"
780
" default_choice \"nodocs\"\n"
781
" choice \"docs\" {\n"
783
" value = \" -o docs\"\n"
785
" choice \"nodocs\" {\n"
791
/* Build the foomatic-rip command line */
792
dstrcatf(cmdline, "foomatic-rip --pdq");
793
if (!isempty(printer)) {
794
dstrcatf(cmdline, " -P %s", printer);
797
/* Make sure that the PPD file is entered with an absolute path */
798
make_absolute_path(job->ppdfile, 256);
799
dstrcatf(cmdline, " --ppd=%s", job->ppdfile);
802
for (opt = optionlist; opt; opt = opt->next) {
803
if (!isempty(opt->varname))
804
dstrcatf(cmdline, "${%s}", opt->varname);
806
dstrcatf(cmdline, "${DRIVERDOCS} $INPUT > $OUTPUT");
809
/* Now we generate code to build the command line snippets for the numerical options */
810
for (opt = optionlist; opt; opt = opt->next) {
811
/* Only numerical and string options need to be treated here */
812
if (opt->type != TYPE_INT &&
813
opt->type != TYPE_FLOAT &&
814
opt->type != TYPE_STRING)
817
/* If the option's variable is non-null, put in the
818
argument. Otherwise this option is the empty
819
string. Error checking? */
820
dstrcatf(psfilter, " # %s\n", opt->comment);
821
if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT) {
823
" # We aren't really checking for max/min,\n"
824
" # this is done by foomatic-rip\n"
825
" if [ \"x${%s}\" != 'x' ]; then\n ", opt->varname);
828
dstrcatf(psfilter, " %s=\" -o %s='${%s}'\"\n", opt->varname, opt->name, opt->varname);
830
if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT)
831
dstrcatf(psfilter, " fi\n");
832
dstrcatf(psfilter, "\n");
835
/* Command execution */
837
" if ! test -e $INPUT.ok; then\n"
839
" if ! test -e $OUTPUT; then \n"
840
" echo 'Error running foomatic-rip; no output!'\n"
844
" ln -s $INPUT $OUTPUT\n"
845
" fi\n\n", cmdline->data);
849
dstrcatf(tmp, "%s", printer_model);
850
strrepl_nodups(tmp->data, " \t\n.,;/()[]{}+*", '-');
851
tmp->len = strlen(tmp->data); /* length could have changed */
852
if (tmp->data[tmp->len -1] == '-') {
853
tmp->data[--tmp->len] = '\0';
858
"driver \"%s-%u\" {\n\n"
859
" # This PDQ driver declaration file was generated automatically by\n"
860
" # foomatic-rip from information in the file %s.\n" /* ppdfile */
861
" # It allows printing with PDQ on the %s.\n" /* model */
863
" requires \"foomatic-rip\"\n\n"
864
"%s" /* driveropts */
865
" language_driver all {\n"
866
" # We accept all file types and pass them to foomatic-rip\n"
867
" # (invoked in \"filter_exec {}\" section) without\n"
869
" filetype_regx \"\"\n"
871
" ln -s $INPUT $OUTPUT\n"
875
"%s" /* setcustompagesize */
879
tmp->data, /* cleaned printer_model */ (unsigned int)job->time, job->ppdfile, printer_model,
880
driveropts->data, setcustompagesize->data, psfilter->data);
883
free_dstr(setcustompagesize);
884
free_dstr(driveropts);
891
/* Functions to let foomatic-rip fork to do several tasks in parallel.
893
To do the filtering without loading the whole file into memory we work
894
on a data stream, we read the data line by line analyse it to decide what
895
filters to use and start the filters if we have found out which we need.
896
We buffer the data only as long as we didn't determing which filters to
897
use for this piece of data and with which options. There are no temporary
900
foomatic-rip splits into up to 6 parallel processes to do the whole
901
filtering (listed in the order of the data flow):
903
KID0: Generate documentation pages (only jobs with "docs" option)
904
KID2: Put together already read data and current input stream for
905
feeding into the file conversion filter (only non-PostScript
907
KID1: Run the file conversion filter to convert non-PostScript
908
input into PostScript (only non-PostScript and "docs" jobs)
909
MAIN: Prepare the job auto-detecting the spooler, reading the PPD,
910
extracting the options from the command line, and parsing
911
the job data itself. It analyses the job data to check
912
whether it is PostScript and starts KID1/KID2 if not, it
913
also stuffs PostScript code from option settings into the
914
PostScript data stream. It starts the renderer (KID3/KID4)
915
as soon as it knows its command line and restarts it when
916
page-specific option settings need another command line
917
or different JCL commands.
918
KID3: The rendering process. In most cases Ghostscript, "cat"
919
for native PostScript printers with their manufacturer's
921
KID4: Put together the JCL commands and the renderer's output
922
and send all that either to STDOUT or pipe it into the
923
command line defined with $postpipe. */
927
void write_output(void *data, size_t len)
929
const char *p = (const char *)data;
931
FILE *postpipe = open_postpipe();
933
/* Remove leading whitespace */
934
while (isspace(*p++) && left-- > 0)
937
fwrite((void *)p, left, 1, postpipe);
947
int guess_file_type(const char *begin, size_t len, int *startpos)
949
const char * p, * end;
955
p = memchr(p, '%', end - p);
958
*startpos = p - begin;
959
if ((end - p) > 2 && !memcmp(p, "%!", 2))
961
else if ((end - p) > 7 && !memcmp(p, "%PDF-1.", 7))
970
* Prints 'filename'. If 'convert' is true, the file will be converted if it is
971
* not postscript or pdf
973
int print_file(const char *filename, int convert)
980
FILE *fchandle = NULL;
983
if (!strcasecmp(filename, "<STDIN>"))
986
file = fopen(filename, "r");
988
_log("Could not open \"%s\" for reading\n", filename);
993
n = fread(buf, 1, sizeof(buf) - 1, file);
995
type = guess_file_type(buf, n, &startpos);
996
/* We do not use any JCL preceeded to the inputr data, as it is simply
997
the PJL commands from the PPD file, and these commands we can also
998
generate, end we even merge them with PJl from the driver */
999
/*if (startpos > 0) {
1001
write_output(buf, startpos);
1006
if (convert) pdfconvertedtops = 0;
1010
_log("Filetype: PDF\n");
1012
if (!ppd_supports_pdf())
1014
char pdf2ps_cmd[PATH_MAX];
1017
char tmpfilename[PATH_MAX] = "";
1019
_log("Driver does not understand PDF input, "
1020
"converting to PostScript\n");
1022
pdfconvertedtops = 1;
1024
/* If reading from stdin, write everything into a temporary file */
1030
snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
1031
fd = mkstemp(tmpfilename);
1033
_log("Could not create temporary file: %s\n", strerror(errno));
1034
return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
1036
tmpfile = fdopen(fd, "r+");
1037
copy_file(tmpfile, stdin, buf, n);
1040
filename = tmpfilename;
1043
/* If the spooler is CUPS we remove the /usr/lib/cups/filter
1044
(CUPS filter directory, can be different, but ends with
1045
"/cups/filter") which CUPS adds to the beginning of $PATH,
1046
so that Poppler's/XPDF's pdftops filter is called and not
1047
the one of CUPS, as the one of CUPS has a different command
1048
line and does undesired page management operations */
1049
snprintf(pdf2ps_cmd, PATH_MAX,
1050
"%spdftops -level2 -origpagesizes %s - 2>/dev/null || "
1051
"gs -q -sstdout=%%stderr -sDEVICE=pswrite -sOutputFile=- "
1052
"-dBATCH -dNOPAUSE -dPARANOIDSAFER %s 2>/dev/null",
1053
(spooler == SPOOLER_CUPS ?
1054
"PATH=${PATH#*/cups/filter:} " : ""),
1055
filename, filename);
1057
renderer_pid = start_system_process("pdf-to-ps", pdf2ps_cmd, &in, &out);
1059
if (dup2(fileno(out), fileno(stdin)) < 0)
1060
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
1061
"Couldn't dup stdout of pdf-to-ps\n");
1063
ret = print_file("<STDIN>", 0);
1065
wait_for_process(renderer_pid);
1070
return print_pdf(stdin, buf, n, filename, startpos);
1072
return print_pdf(file, NULL, 0, filename, startpos);
1075
_log("Filetype: PostScript\n");
1077
return print_ps(stdin, buf, n, filename);
1079
return print_ps(file, NULL, 0, filename);
1082
if (spooler == SPOOLER_CUPS) {
1083
_log("Cannot process \"%s\": Unknown filetype.\n", filename);
1087
_log("Filetype unknown, trying to convert ...\n");
1088
get_fileconverter_handle(buf, &fchandle, &fcpid);
1090
/* Read further data from the file converter and not from STDIN */
1091
if (dup2(fileno(fchandle), fileno(stdin)) < 0)
1092
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't dup fileconverterhandle\n");
1094
ret = print_file("<STDIN>", 0);
1096
if (close_fileconverter_handle(fchandle, fcpid) != EXIT_PRINTED)
1097
rip_die(ret, "Error closing file converter\n");
1105
void signal_terminate(int signal)
1107
rip_die(EXIT_PRINTED, "Caught termination signal: Job canceled\n");
1110
jobparams_t * create_job()
1112
jobparams_t *job = calloc(1, sizeof(jobparams_t));
1113
struct passwd *passwd;
1115
job->optstr = create_dstr();
1116
job->time = time(NULL);
1117
strcpy(job->copies, "1");
1118
gethostname(job->host, 128);
1119
passwd = getpwuid(getuid());
1121
strlcpy(job->user, passwd->pw_name, 128);
1122
snprintf(job->title, 128, "%s@%s", job->user, job->host);
1127
void free_job(jobparams_t *job)
1129
free_dstr(job->optstr);
1133
int main(int argc, char** argv)
1136
int verbose = 0, quiet = 0, showdocs = 0;
1140
FILE *genpdqfile = NULL;
1142
char tmp[1024], pstoraster[256];
1143
int havefilter, havepstoraster;
1147
arglist = list_create_from_array(argc -1, (void**)&argv[1]);
1149
if (argc == 2 && (arglist_find(arglist, "--version") || arglist_find(arglist, "--help") ||
1150
arglist_find(arglist, "-v") || arglist_find(arglist, "-h"))) {
1151
printf("foomatic rip version "VERSION"\n");
1152
printf("\"man foomatic-rip\" for help.\n");
1157
filelist = create_dstr();
1161
jclappend = create_dstr();
1162
postpipe = create_dstr();
1166
signal(SIGTERM, signal_terminate);
1167
signal(SIGINT, signal_terminate);
1170
config_from_file(CONFIG_PATH "/filter.conf");
1172
/* Command line options for verbosity */
1173
if (arglist_remove_flag(arglist, "-v"))
1175
if (arglist_remove_flag(arglist, "-q"))
1177
if (arglist_remove_flag(arglist, "-d"))
1179
if (arglist_remove_flag(arglist, "--debug"))
1183
logh = fopen(LOG_FILE ".log", "w"); /* insecure, use for debugging only */
1184
else if (quiet && !verbose)
1185
logh = NULL; /* Quiet mode, do not log */
1187
logh = stderr; /* Default: log to stderr */
1189
/* Start debug logging */
1191
/* If we are not in debug mode, we do this later, as we must find out at
1192
first which spooler is used. When printing without spooler we
1193
suppress logging because foomatic-rip is called directly on the
1194
command line and so we avoid logging onto the console. */
1195
_log("foomatic-rip version "VERSION" running...\n");
1197
/* Print the command line only in debug mode, Mac OS X adds very many
1198
options so that CUPS cannot handle the output of the command line
1199
in its log files. If CUPS encounters a line with more than 1024
1200
characters sent into its log files, it aborts the job with an error. */
1201
if (spooler != SPOOLER_CUPS) {
1202
_log("called with arguments: ");
1203
for (i = 1; i < argc -1; i++)
1204
_log("\'%s\', ", argv[i]);
1205
_log("\'%s\'\n", argv[i]);
1209
if (getenv("PPD")) {
1210
strncpy(job->ppdfile, getenv("PPD"), 256);
1211
spooler = SPOOLER_CUPS;
1214
if (getenv("SPOOLER_KEY")) {
1215
spooler = SPOOLER_SOLARIS;
1216
/* set the printer name from the ppd file name */
1217
strncpy_omit(job->ppdfile, getenv("PPD"), 256, omit_specialchars);
1218
file_basename(job->printer, job->ppdfile, 256);
1219
/* TODO read attribute file*/
1222
if (getenv("PPR_VERSION"))
1223
spooler = SPOOLER_PPR;
1225
if (getenv("PPR_RIPOPTS")) {
1226
/* PPR 1.5 allows the user to specify options for the PPR RIP with the
1227
"--ripopts" option on the "ppr" command line. They are provided to
1228
the RIP via the "PPR_RIPOPTS" environment variable. */
1229
dstrcatf(job->optstr, "%s ", getenv("PPR_RIPOPTS"));
1230
spooler = SPOOLER_PPR;
1233
if (getenv("LPOPTS")) { /* "LPOPTS": Option settings for some LPD implementations (ex: GNUlpr) */
1234
spooler = SPOOLER_GNULPR;
1235
dstrcatf(job->optstr, "%s ", getenv("LPOPTS"));
1238
/* Check for LPRng first so we do not pick up bogus ppd files by the -ppd option */
1239
if (arglist_remove_flag(arglist, "--lprng"))
1240
spooler = SPOOLER_LPRNG;
1242
/* 'PRINTCAP_ENTRY' environment variable is : LPRng
1243
the :ppd=/path/to/ppdfile printcap entry should be used */
1244
if (getenv("PRINTCAP_ENTRY")) {
1245
spooler = SPOOLER_LPRNG;
1246
if ((str = strstr(getenv("PRINTCAP_ENTRY"), "ppd=")))
1248
else if ((str = strstr(getenv("PRINTCAP_ENTRY"), "ppdfile=")))
1251
while (isspace(*str)) str++;
1253
while (*str != '\0' && !isspace(*str) && *str != '\n' &&
1255
if (isprint(*str) && strchr(shellescapes, *str) == NULL)
1262
/* PPD file name given via the command line
1263
allow duplicates, and use the last specified one */
1264
if (spooler != SPOOLER_LPRNG) {
1265
while ((str = arglist_get_value(arglist, "-p"))) {
1266
strncpy(job->ppdfile, str, 256);
1267
arglist_remove(arglist, "-p");
1270
while ((str = arglist_get_value(arglist, "--ppd"))) {
1271
strncpy(job->ppdfile, str, 256);
1272
arglist_remove(arglist, "--ppd");
1275
/* Check for LPD/GNUlpr by typical options which the spooler puts onto
1276
the filter's command line (options "-w": text width, "-l": text
1277
length, "-i": indent, "-x", "-y": graphics size, "-c": raw printing,
1278
"-n": user name, "-h": host name) */
1279
if ((str = arglist_get_value(arglist, "-h"))) {
1280
if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
1281
spooler = SPOOLER_LPD;
1282
strncpy(job->host, str, 127);
1283
job->host[127] = '\0';
1284
arglist_remove(arglist, "-h");
1286
if ((str = arglist_get_value(arglist, "-n"))) {
1287
if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
1288
spooler = SPOOLER_LPD;
1290
strncpy(job->user, str, 127);
1291
job->user[127] = '\0';
1292
arglist_remove(arglist, "-n");
1294
if (arglist_remove(arglist, "-w") ||
1295
arglist_remove(arglist, "-l") ||
1296
arglist_remove(arglist, "-x") ||
1297
arglist_remove(arglist, "-y") ||
1298
arglist_remove(arglist, "-i") ||
1299
arglist_remove_flag(arglist, "-c")) {
1300
if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
1301
spooler = SPOOLER_LPD;
1303
/* LPRng delivers the option settings via the "-Z" argument */
1304
if ((str = arglist_get_value(arglist, "-Z"))) {
1305
spooler = SPOOLER_LPRNG;
1306
dstrcatf(job->optstr, "%s ", str);
1307
arglist_remove(arglist, "-Z");
1309
/* Job title and options for stock LPD */
1310
if ((str = arglist_get_value(arglist, "-j")) || (str = arglist_get_value(arglist, "-J"))) {
1311
strncpy_omit(job->title, str, 128, omit_shellescapes);
1312
if (spooler == SPOOLER_LPD)
1313
dstrcatf(job->optstr, "%s ", job->title);
1314
if (!arglist_remove(arglist, "-j"))
1315
arglist_remove(arglist, "-J");
1318
if (arglist_remove_flag(arglist, "--cps") > 0)
1319
spooler = SPOOLER_CPS;
1321
/* Options for spooler-less printing, CPS, or PDQ */
1322
while ((str = arglist_get_value(arglist, "-o"))) {
1323
strncpy_omit(tmp, str, 1024, omit_shellescapes);
1324
dstrcatf(job->optstr, "%s ", tmp);
1325
arglist_remove(arglist, "-o");
1326
/* If we don't print as PPR RIP or as CPS filter, we print
1327
without spooler (we check for PDQ later) */
1328
if (spooler != SPOOLER_PPR && spooler != SPOOLER_CPS)
1329
spooler = SPOOLER_DIRECT;
1332
/* Printer for spooler-less printing or PDQ */
1333
if ((str = arglist_get_value(arglist, "-d"))) {
1334
strncpy_omit(job->printer, str, 256, omit_shellescapes);
1335
arglist_remove(arglist, "-d");
1338
/* Printer for spooler-less printing, PDQ, or LPRng */
1339
if ((str = arglist_get_value(arglist, "-P"))) {
1340
strncpy_omit(job->printer, str, 256, omit_shellescapes);
1341
arglist_remove(arglist, "-P");
1344
/* Were we called from a PDQ wrapper? */
1345
if (arglist_remove_flag(arglist, "--pdq"))
1346
spooler = SPOOLER_PDQ;
1348
/* Were we called to build the PDQ driver declaration file? */
1349
genpdqfile = check_pdq_file(arglist);
1351
spooler = SPOOLER_PDQ;
1353
/* spooler specific initialization */
1356
init_ppr(arglist, job);
1360
init_cups(arglist, filelist, job);
1364
if (job->ppdfile[0] != '\0') break;
1366
case SPOOLER_GNULPR:
1367
/* Get PPD file name as the last command line argument */
1369
strncpy(job->ppdfile, (char*)arglist->last->data, 256);
1372
case SPOOLER_DIRECT:
1375
init_direct_cps_pdq(arglist, filelist, job);
1379
/* Files to be printed (can be more than one for spooler-less printing) */
1380
/* Empty file list -> print STDIN */
1382
if (filelist->len == 0)
1383
dstrcpyf(filelist, "<STDIN>");
1385
/* Check filelist */
1386
p = strtok(filelist->data, " ");
1388
if (strcmp(p, "<STDIN>") != 0) {
1390
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Invalid argument: %s", p);
1391
else if (access(p, R_OK) != 0) {
1392
_log("File %s does not exist/is not readable\n", p);
1396
p = strtok(NULL, " ");
1399
/* When we print without spooler or with CPS do not log onto STDERR unless
1400
the "-v" ('Verbose') is set or the debug mode is used */
1401
if ((spooler == SPOOLER_DIRECT || spooler == SPOOLER_CPS || genpdqfile) && !verbose && !debug) {
1402
if (logh && logh != stderr)
1407
/* If we are in debug mode, we do this earlier. */
1409
_log("foomatic-rip version " VERSION " running...\n");
1410
/* Print the command line only in debug mode, Mac OS X adds very many
1411
options so that CUPS cannot handle the output of the command line
1412
in its log files. If CUPS encounters a line with more than 1024
1413
characters sent into its log files, it aborts the job with an error. */
1414
if (spooler != SPOOLER_CUPS) {
1415
_log("called with arguments: ");
1416
for (i = 1; i < argc -1; i++)
1417
_log("\'%s\', ", argv[i]);
1418
_log("\'%s\'\n", argv[i]);
1423
/* Load the PPD file and build a data structure for the renderer's
1424
command line and the options */
1425
if (!(ppdfh = fopen(job->ppdfile, "r")))
1426
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Unable to open PPD file %s\n", job->ppdfile);
1428
read_ppd_file(job->ppdfile);
1430
/* We do not need to parse the PostScript job when we don't have
1431
any options. If we have options, we must check whether the
1432
default settings from the PPD file are valid and correct them
1434
if (option_count() == 0) {
1435
/* We don't have any options, so we do not need to parse the
1440
/* Is our PPD for a CUPS raster driver */
1441
if (!isempty(cupsfilter)) {
1442
/* Search the filter in cupsfilterpath
1443
The %Y is a placeholder for the option settings */
1445
path = cupsfilterpath;
1446
while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
1447
strlcat(tmp, "/", 1024);
1448
strlcat(tmp, cupsfilter, 1024);
1449
if (access(tmp, X_OK) == 0) {
1451
strlcpy(cupsfilter, tmp, 256);
1452
strlcat(cupsfilter, " 0 '' '' 0 '%Y%X'", 256);
1458
/* We do not have the required filter, so we assume that
1459
rendering this job is supposed to be done on a remote
1460
server. So we do not define a renderer command line and
1461
embed only the option settings (as we had a PostScript
1462
printer). This way the settings are taken into account
1463
when the job is rendered on the server.*/
1464
_log("CUPS filter for this PPD file not found - assuming that job will "
1465
"be rendered on a remote server. Only the PostScript of the options"
1466
"will be inserted into the PostScript data stream.\n");
1469
/* use pstoraster script if available, otherwise run Ghostscript directly */
1471
path = cupsfilterpath;
1472
while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
1473
strlcat(tmp, "/pstoraster", 1024);
1474
if (access(tmp, X_OK) == 0) {
1476
strlcpy(pstoraster, tmp, 256);
1477
strlcat(pstoraster, " 0 '' '' 0 '%X'", 256);
1481
if (!havepstoraster) {
1482
strcpy(pstoraster, "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOMEDIAATTRS -sDEVICE=cups -sOutputFile=-%W -");
1485
/* build Ghostscript/CUPS driver command line */
1486
snprintf(cmd, 1024, "%s | %s", pstoraster, cupsfilter);
1488
/* Set environment variables */
1489
setenv("PPD", job->ppdfile, 1);
1493
/* Was the RIP command line defined in the PPD file? If not, we assume a PostScript printer
1494
and do not render/translate the input data */
1496
strcpy(cmd, "cat%A%B%C%D%E%F%G%H%I%J%K%L%M%Z");
1498
/* No command line, no options, we have a raw queue, don't check
1499
whether the input is PostScript and ignore the "docs" option,
1500
simply pass the input data to the backend.*/
1502
strcpy(printer_model, "Raw queue");
1506
/* Summary for debugging */
1507
_log("\nParameter Summary\n"
1508
"-----------------\n\n"
1514
"Printer model: %s\n",
1515
spooler_name(spooler), job->printer, get_modern_shell(), job->ppdfile, attrpath, printer_model);
1516
/* Print the options string only in debug mode, Mac OS X adds very many
1517
options so that CUPS cannot handle the output of the option string
1518
in its log files. If CUPS encounters a line with more than 1024 characters
1519
sent into its log files, it aborts the job with an error.*/
1520
if (debug || spooler != SPOOLER_CUPS)
1521
_log("Options: %s\n", job->optstr->data);
1522
_log("Job title: %s\n", job->title);
1523
_log("File(s) to be printed:\n");
1524
_log("%s\n\n", filelist->data);
1525
if (getenv("GS_LIB"))
1526
_log("Ghostscript extra search path ('GS_LIB'): %s\n", getenv("GS_LIB"));
1528
/* Process options from command line,
1529
but save the defaults for printing documentation pages first */
1530
optionset_copy_values(optionset("default"), optionset("userval"));
1531
process_cmdline_options();
1533
/* Were we called to build the PDQ driver declaration file? */
1535
print_pdq_driver(genpdqfile, optionset("userval"));
1540
if (spooler == SPOOLER_PPR_INT) {
1541
snprintf(tmp, 1024, "interfaces/%s", backend);
1542
if (access(tmp, X_OK) != 0)
1543
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "The backend interface "
1544
"/interfaces/%s does not exist/ is not executable!\n", backend);
1546
/* foomatic-rip cannot use foomatic-rip as backend */
1547
if (!strcmp(backend, "foomatic-rip"))
1548
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "\"foomatic-rip\" cannot "
1549
"use itself as backend interface!\n");
1551
/* Put the backend interface into the postpipe */
1553
$postpipe = "| ( interfaces/$backend \"$ppr_printer\" ".
1554
"\"$ppr_address\" \"" . join(" ",@backendoptions) .
1555
"\" \"$ppr_jobbreak\" \"$ppr_feedback\" " .
1556
"\"$ppr_codes\" \"$ppr_jobname\" \"$ppr_routing\" " .
1557
"\"$ppr_for\" \"\" )";
1561
/* no postpipe for CUPS or PDQ, even if one is defined in the PPD file */
1562
if (spooler == SPOOLER_CUPS || spooler == SPOOLER_PDQ)
1563
dstrclear(postpipe);
1565
/* CPS always needs a postpipe, set the default one for local printing if none is set */
1566
if (spooler == SPOOLER_CPS && !postpipe->len)
1567
dstrcpy(postpipe, "| cat - > $LPDDEV");
1570
_log("Ouput will be redirected to:\n%s\n", postpipe);
1573
/* Print documentation page when asked for */
1575
/* Don't print the supplied files, STDIN will be redirected to the
1576
documentation page generator */
1577
dstrcpyf(filelist, "<STDIN>");
1579
/* Start the documentation page generator */
1583
/* In debug mode save the data supposed to be fed into the
1584
renderer also into a file, reset the file here */
1586
run_system_process("reset-file", "> " LOG_FILE ".ps");
1588
filename = strtok_r(filelist->data, " ", &p);
1590
_log("\n================================================\n\n"
1592
"================================================\n\n", filename);
1594
/* Do we have a raw queue? */
1595
if (dontparse == 2) {
1596
/* Raw queue, simply pass the input into the postpipe (or to STDOUT
1597
when there is no postpipe) */
1598
_log("Raw printing, executing \"cat %s\"\n\n");
1599
snprintf(tmp, 1024, "cat %s", postpipe->data);
1600
run_system_process("raw-printer", tmp);
1604
/* First, for arguments with a default, stick the default in as
1605
the initial value for the "header" option set, this option set
1606
consists of the PPD defaults, the options specified on the
1607
command line, and the options set in the header part of the
1608
PostScript file (all before the first page begins). */
1609
optionset_copy_values(optionset("userval"), optionset("header"));
1611
if (!print_file(filename, 1))
1612
rip_die(EXIT_PRNERR_NORETRY, "Could not print file %s\n", filename);
1613
filename = strtok_r(NULL, " ", &p);
1616
/* Close documentation page generator */
1617
/* if (docgenerator_pid) {
1618
retval = close_docgenerator_handle(dogenerator_handle, docgenerator_pid);
1619
if (!retval != EXIT_PRINTED) {
1620
_log("Error closing documentation page generator\n");
1623
docgenerator_pid = 0;
1626
/* Close the last input file */
1629
/* TODO dump everything in $dat when debug is turned on (necessary?) */
1631
_log("\nClosing foomatic-rip.\n");
1636
if (genpdqfile && genpdqfile != stdout)
1638
free_dstr(filelist);
1642
argv_free(jclprepend);
1643
free_dstr(jclappend);
1645
free_dstr(backendoptions);
1649
return EXIT_PRINTED;