~ubuntu-branches/ubuntu/precise/foomatic-filters/precise-proposed

« back to all changes in this revision

Viewing changes to .pc/foomatic-rip-read-multiple-input-files.patch/foomaticrip.c

  • Committer: Bazaar Package Importer
  • Author(s): Didier Raboud, Translation updates
  • Date: 2011-02-09 15:38:14 UTC
  • mfrom: (1.2.4 upstream) (6.1.2 experimental)
  • Revision ID: james.westby@ubuntu.com-20110209153814-noiljpseb0kzdlzs
Tags: 4.0.6-1
* Upload to unstable.

[ Translation updates ]
* Japanese: Indent correctly.

* Explicitly depend on bash (Closes: #600179)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
49
/* Logging */
 
50
FILE* logh = NULL;
 
51
 
 
52
void _logv(const char *msg, va_list ap)
 
53
{
 
54
    if (!logh)
 
55
        return;
 
56
    vfprintf(logh, msg, ap);
 
57
    fflush(logh);
 
58
}
 
59
 
 
60
void _log(const char* msg, ...)
 
61
{
 
62
    va_list ap;
 
63
    va_start(ap, msg);
 
64
    _logv(msg, ap);
 
65
    va_end(ap);
 
66
}
 
67
 
 
68
void close_log()
 
69
{
 
70
    if (logh && logh != stderr)
 
71
        fclose(logh);
 
72
}
 
73
 
 
74
int redirect_log_to_stderr()
 
75
{
 
76
    if (dup2(fileno(logh), fileno(stderr)) < 0) {
 
77
        _log("Could not dup logh to stderr\n");
 
78
        return 0;
 
79
    }
 
80
    return 1;
 
81
}
 
82
 
 
83
void rip_die(int status, const char *msg, ...)
 
84
{
 
85
    va_list ap;
 
86
 
 
87
    _log("Process is dying with \"");
 
88
    va_start(ap, msg);
 
89
    _logv(msg, ap);
 
90
    va_end(ap);
 
91
    _log("\", exit stat %d\n", status);
 
92
 
 
93
    _log("Cleaning up...\n");
 
94
    kill_all_processes();
 
95
 
 
96
    exit(status);
 
97
}
 
98
 
 
99
 
 
100
jobparams_t  *job = NULL;
 
101
 
 
102
jobparams_t * get_current_job()
 
103
{
 
104
    assert(job);
 
105
    return job;
 
106
}
 
107
 
 
108
 
 
109
dstr_t *postpipe;  /* command into which the output of this filter should be piped */
 
110
FILE *postpipe_fh = NULL;
 
111
 
 
112
FILE * open_postpipe()
 
113
{
 
114
    const char *p;
 
115
 
 
116
    if (postpipe_fh)
 
117
        return postpipe_fh;
 
118
 
 
119
    if (isempty(postpipe->data))
 
120
        return stdout;
 
121
 
 
122
    /* Delete possible '|' symbol in the beginning */
 
123
    p = skip_whitespace(postpipe->data);
 
124
    if (*p && *p == '|')
 
125
        p += 1;
 
126
 
 
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);
 
130
 
 
131
    return postpipe_fh;
 
132
}
 
133
 
 
134
 
 
135
char printer_model[256] = "";
 
136
const char *accounting_prolog = NULL;
 
137
char attrpath[256] = "";
 
138
 
 
139
 
 
140
int spooler = SPOOLER_DIRECT;
 
141
int do_docs = 0;
 
142
int dontparse = 0;
 
143
int jobhasjcl;
 
144
int pdfconvertedtops;
 
145
 
 
146
/* Variable for PPR's backend interface name (parallel, tcpip, atalk, ...) */
 
147
char backend [64];
 
148
 
 
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;
 
152
 
 
153
/* These variables were in 'dat' before */
 
154
char colorprofile [128];
 
155
char cupsfilter[256];
 
156
char **jclprepend = NULL;
 
157
dstr_t *jclappend;
 
158
 
 
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. */
 
164
int debug = 0;
 
165
 
 
166
/* Path to the GhostScript which foomatic-rip shall use */
 
167
char gspath[PATH_MAX] = "gs";
 
168
 
 
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";
 
173
 
 
174
/* CUPS raster drivers are searched here */
 
175
char cupsfilterpath[PATH_MAX] = "/usr/local/lib/cups/filter:"
 
176
                                "/usr/local/libexec/cups/filter:"
 
177
                                "/opt/cups/filter:"
 
178
                                "/usr/lib/cups/filter";
 
179
 
 
180
char modern_shell[64] = "/bin/bash";
 
181
 
 
182
void config_set_option(const char *key, const char *value)
 
183
{
 
184
    if (strcmp(key, "debug") == 0)
 
185
        debug = atoi(value);
 
186
 
 
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
 
190
     * systems. */
 
191
    else if (strcmp(key, "execpath") == 0 && !isempty(value))
 
192
        setenv("PATH", value, 1);
 
193
 
 
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);
 
204
}
 
205
 
 
206
void config_from_file(const char *filename)
 
207
{
 
208
    FILE *fh;
 
209
    char line[256];
 
210
    char *key, *value;
 
211
 
 
212
    fh = fopen(filename, "r");
 
213
    if (fh == NULL)
 
214
        return; /* no error here, only read config file if it is present */
 
215
 
 
216
    while (fgets(line, 256, fh) != NULL)
 
217
    {
 
218
        key = strtok(line, " :\t\r\n");
 
219
        if (key == NULL || key[0] == '#')
 
220
            continue;
 
221
        value = strtok(NULL, " \t\r\n#");
 
222
        config_set_option(key, value);
 
223
    }
 
224
    fclose(fh);
 
225
}
 
226
 
 
227
const char * get_modern_shell()
 
228
{
 
229
    return modern_shell;
 
230
}
 
231
 
 
232
/* returns position in 'str' after the option */
 
233
char * extract_next_option(char *str, char **pagerange, char **key, char **value)
 
234
{
 
235
    char *p = str;
 
236
    char quotechar;
 
237
 
 
238
    *pagerange = NULL;
 
239
    *key = NULL;
 
240
    *value = NULL;
 
241
 
 
242
    if (!str)
 
243
        return NULL;
 
244
 
 
245
    /* skip whitespace and commas */
 
246
    while (*p && (isspace(*p) || *p == ',')) p++;
 
247
 
 
248
    if (!*p)
 
249
        return NULL;
 
250
 
 
251
    /* read the pagerange if we have one */
 
252
    if (prefixcmp(p, "even:") == 0 || prefixcmp(p, "odd:") == 0 || isdigit(*p)) {
 
253
        *pagerange = p;
 
254
        p = strchr(p, ':');
 
255
        if (!p)
 
256
            return NULL;
 
257
        *p = '\0';
 
258
        p++;
 
259
    }
 
260
 
 
261
    /* read the key */
 
262
    if (*p == '\'' || *p == '\"') {
 
263
        quotechar = *p;
 
264
        *key = p +1;
 
265
        p = strchr(*key, quotechar);
 
266
        if (!p)
 
267
            return NULL;
 
268
    }
 
269
    else {
 
270
        *key = p;
 
271
        while (*p && *p != ':' && *p != '=' && *p != ' ') p++;
 
272
    }
 
273
 
 
274
    if (*p != ':' && *p != '=') { /* no value for this option */
 
275
        if (!*p)
 
276
            return NULL;
 
277
        else if (isspace(*p)) {
 
278
            *p = '\0';
 
279
            return p +1;
 
280
        }
 
281
        return p;
 
282
    }
 
283
 
 
284
    *p++ = '\0'; /* remove the separator sign */
 
285
 
 
286
    if (*p == '\"' || *p == '\'') {
 
287
        quotechar = *p;
 
288
        *value = p +1;
 
289
        p = strchr(*value, quotechar);
 
290
        if (!p)
 
291
            return NULL;
 
292
        *p = '\0';
 
293
        p++;
 
294
    }
 
295
    else {
 
296
        *value = p;
 
297
        while (*p && *p != ' ' && *p != ',') p++;
 
298
        if (*p == '\0')
 
299
            return NULL;
 
300
        *p = '\0';
 
301
        p++;
 
302
    }
 
303
 
 
304
    return *p ? p : NULL;
 
305
}
 
306
 
 
307
/* processes job->optstr */
 
308
void process_cmdline_options()
 
309
{
 
310
    char *p, *cmdlineopts, *nextopt, *pagerange, *key, *value;
 
311
    option_t *opt, *opt2;
 
312
    int optset;
 
313
    char tmp [256];
 
314
 
 
315
    _log("Printing system options:\n");
 
316
    cmdlineopts = strdup(job->optstr->data);
 
317
    for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
 
318
        key;
 
319
        nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
 
320
    {
 
321
        /* Consider only options which are not in the PPD file here */
 
322
        if ((opt = find_option(key)) != NULL) continue;
 
323
        if (value)
 
324
            _log("Pondering option '%s=%s'\n", key, value);
 
325
        else
 
326
            _log("Pondering option '%s'\n", key);
 
327
 
 
328
        /* "docs" option to print help page */
 
329
        if (!strcasecmp(key, "docs")) {
 
330
            do_docs = 1;
 
331
            continue;
 
332
        }
 
333
        /* "profile" option to supply a color correction profile to a CUPS raster driver */
 
334
        if (!strcmp(key, "profile")) {
 
335
            strlcpy(colorprofile, value, 128);
 
336
            continue;
 
337
        }
 
338
        /* Solaris options that have no reason to be */
 
339
        if (!strcmp(key, "nobanner") || !strcmp(key, "dest") || !strcmp(key, "protocol"))
 
340
            continue;
 
341
 
 
342
        if (pagerange) {
 
343
            snprintf(tmp, 256, "pages:%s", pagerange);
 
344
            optset = optionset(tmp);
 
345
        }
 
346
        else
 
347
            optset = optionset("userval");
 
348
 
 
349
        if (value) {
 
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);
 
354
            }
 
355
            else if (strcasecmp(key, "media") == 0) {
 
356
                /*  Standard arguments?
 
357
                    media=x,y,z
 
358
                    sides=one|two-sided-long|short-edge
 
359
 
 
360
                    Rummage around in the media= option for known media, source,
 
361
                    etc types.
 
362
                    We ought to do something sensible to make the common manual
 
363
                    boolean option work when specified as a media= tray thing.
 
364
 
 
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. */
 
369
 
 
370
                p = strtok(value, ",");
 
371
                do {
 
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");
 
383
                    }
 
384
                    else
 
385
                        _log("Unknown \"media\" component: \"%s\".\n", p);
 
386
 
 
387
                } while ((p = strtok(NULL, ",")));
 
388
            }
 
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");
 
396
 
 
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");
 
401
                            else
 
402
                                option_set_value(opt, optset, "DuplexNoTumble");
 
403
                        }
 
404
                        else if (strcasestr(value, "short-edge")) {
 
405
                            if ((opt2 = find_option("Binding")))
 
406
                                option_set_value(opt2, optset, "ShortEdge");
 
407
                            else
 
408
                                option_set_value(opt, optset, "DuplexNoTumble");
 
409
                        }
 
410
                    }
 
411
                }
 
412
                else if (!prefixcasecmp(value, "one-sided")) {
 
413
                    if ((opt = find_option("Duplex")))
 
414
                        option_set_value(opt, optset, "0");
 
415
                }
 
416
 
 
417
                /*  TODO
 
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...
 
421
                */
 
422
            }
 
423
            else if (spooler == SPOOLER_PPR_INT) {
 
424
                /* Unknown option, pass it to PPR's backend interface */
 
425
                if (!backendoptions)
 
426
                    backendoptions = create_dstr();
 
427
                dstrcatf(backendoptions, "%s=%s ", key, value);
 
428
            }
 
429
            else
 
430
                _log("Unknown option %s=%s.\n", key, value);
 
431
        }
 
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 */
 
435
        }
 
436
        else
 
437
            _log("Unknown boolean option \"%s\".\n", key);
 
438
    }
 
439
    free(cmdlineopts);
 
440
 
 
441
    _log("Options from the PPD file:\n");
 
442
    cmdlineopts = strdup(job->optstr->data);
 
443
    for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
 
444
        key;
 
445
        nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
 
446
    {
 
447
        /* Consider only PPD file options here */
 
448
        if ((opt = find_option(key)) == NULL) continue; 
 
449
        if (value)
 
450
            _log("Pondering option '%s=%s'\n", key, value);
 
451
        else
 
452
            _log("Pondering option '%s'\n", key);
 
453
 
 
454
        if (pagerange) {
 
455
            snprintf(tmp, 256, "pages:%s", pagerange);
 
456
            optset = optionset(tmp);
 
457
 
 
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);
 
461
                continue;
 
462
            }
 
463
        }
 
464
        else
 
465
            optset = optionset("userval");
 
466
 
 
467
        if (value) {
 
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));
 
472
            }
 
473
        }
 
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");
 
479
        else
 
480
            option_set_value(opt, optset, "1");
 
481
    }
 
482
    free(cmdlineopts);
 
483
}
 
484
 
 
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)
 
488
{
 
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 */
 
492
 
 
493
    listitem_t *i;
 
494
    char filename[256];
 
495
    FILE *handle;
 
496
    char *p;
 
497
    int raw, append;
 
498
 
 
499
    if ((i = arglist_find_prefix(arglist, "--genpdq"))) {
 
500
        raw = 0;
 
501
        append = 0;
 
502
    }
 
503
    else if ((i = arglist_find_prefix(arglist, "--genrawpdq"))) {
 
504
        raw = 1;
 
505
        append = 0;
 
506
    }
 
507
    else if ((i = arglist_find_prefix(arglist, "--appendpdq"))) {
 
508
        raw = 0;
 
509
        append = 1;
 
510
    }
 
511
    else if ((i = arglist_find_prefix(arglist, "--appendrawpdq"))) {
 
512
        raw = 1;
 
513
        append = 1;
 
514
    }
 
515
 
 
516
    if (!i)
 
517
        return NULL;
 
518
 
 
519
    p = strchr((char*)i->data, '=');
 
520
    if (p) {
 
521
        strncpy_omit(filename, p +1, 256, omit_shellescapes);
 
522
        handle = fopen(filename, append ? "a" : "w");
 
523
        if (!handle)
 
524
            rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Cannot write PDQ driver declaration file.\n");
 
525
    }
 
526
    else if (!append)
 
527
        handle = stdout;
 
528
    else
 
529
        return NULL;
 
530
 
 
531
    /* remove option from args */
 
532
    list_remove(arglist, i);
 
533
 
 
534
    /* Do we have a pdq driver declaration for a raw printer */
 
535
    if (raw) {
 
536
        fprintf(handle,
 
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"
 
543
                "    convert_exec {\n"
 
544
                "      ln -s $INPUT $OUTPUT\n"
 
545
                "    }\n"
 
546
                "  }\n"
 
547
                "  filter_exec {\n"
 
548
                "    ln -s $INPUT $OUTPUT\n"
 
549
                "  }\n"
 
550
                "}", (unsigned int)job->time);
 
551
        if (handle != stdout) {
 
552
            fclose(handle);
 
553
            handle = NULL;
 
554
        }
 
555
        exit(EXIT_PRINTED);
 
556
    }
 
557
 
 
558
    return handle;
 
559
}
 
560
 
 
561
 
 
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)
 
566
{
 
567
}
 
568
#if 0
 
569
    option_t *opt;
 
570
    value_t *val;
 
571
    setting_t *setting, *setting_true, *setting_false;
 
572
 
 
573
    /* Construct option list */
 
574
    dstr_t *driveropts = create_dstr();
 
575
 
 
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();
 
579
 
 
580
    dstr_t *tmp = create_dstr();
 
581
    dstr_t *cmdline = create_dstr();
 
582
    dstr_t *psfilter = create_dstr();
 
583
 
 
584
 
 
585
    /* 1, if setting "PageSize=Custom" was found
 
586
       Then we must add options for page width and height */
 
587
    int custompagesize = 0;
 
588
 
 
589
    /* Data for a custom page size, to allow a custom size as default */
 
590
    int pagewidth = 612;
 
591
    int pageheight = 792;
 
592
    char pageunit[2] = "pt";
 
593
 
 
594
    char def [128];
 
595
 
 
596
    def[0] = '\0';
 
597
 
 
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)
 
603
                continue;
 
604
 
 
605
            /* Omit "PageRegion" option, it does the same as "PageSize" */
 
606
            if (!strcmp(opt->name, "PageRegion"))
 
607
                continue;
 
608
 
 
609
            /* 1, if setting "PageSize=Custom" was found
 
610
               Then we must add options for page width and height */
 
611
            custompagesize = 0;
 
612
 
 
613
            if ((val = option_get_value(opt, optset)))
 
614
                strlcpy(def, val->value, 128);
 
615
 
 
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");
 
621
#endif
 
622
 
 
623
            dstrcatf(driveropts,
 
624
                    "  option {\n"
 
625
                    "    var = \"%s\"\n"
 
626
                    "    desc = \"%s\"\n", opt->varname, option_text(opt));
 
627
 
 
628
            /* get enumeration values for each enum arg */
 
629
            dstrclear(tmp);
 
630
            for (setting = opt->settinglist; setting; setting = setting->next)  {
 
631
                dstrcatf(tmp,
 
632
                    "    choice \"%s_%s\" {\n"
 
633
                    "      desc = \"%s\"\n"
 
634
                    "      value = \" -o %s=%s\"\n"
 
635
                    "    }\n",
 
636
                     opt->name, setting->value,
 
637
                      isempty(setting->comment) ? setting->value : setting->comment,
 
638
                    opt->name, setting->value);
 
639
 
 
640
                if (!strcmp(opt->name, "PageSize") && !strcmp(setting->value, "Custom")) {
 
641
                    custompagesize = 1;
 
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"
 
648
                            "      fi\n\n",
 
649
                            opt->varname, opt->varname, setting->value, opt->varname, opt->varname);
 
650
                    }
 
651
                }
 
652
            }
 
653
 
 
654
            dstrcatf(driveropts, "    default_choice \"%s_%s\"\n", opt->name, def);
 
655
            dstrcatf(driveropts, tmp->data);
 
656
            dstrcatf(driveropts, "  }\n\n");
 
657
 
 
658
            if (custompagesize) {
 
659
                /* Add options to set the custom page size */
 
660
                dstrcatf(driveropts,
 
661
                    "  argument {\n"
 
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"
 
666
                    "  }\n\n"
 
667
                    "  argument {\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"
 
672
                    "  }\n\n"
 
673
                    "  option {\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"
 
679
                    "      value = \"pt\"\n"
 
680
                    "    }\n"
 
681
                    "    choice \"PageSizeUnit_in\" {\n"
 
682
                    "      desc = \"Inches\"\n"
 
683
                    "      value = \"in\"\n"
 
684
                    "    }\n"
 
685
                    "    choice \"PageSizeUnit_cm\" {\n"
 
686
                    "      desc = \"cm\"\n"
 
687
                    "      value = \"cm\"\n"
 
688
                    "    }\n"
 
689
                    "    choice \"PageSizeUnit_mm\" {\n"
 
690
                    "      desc = \"mm\"\n"
 
691
                    "      value = \"mm\"\n"
 
692
                    "    }\n"
 
693
                    "  }\n\n",
 
694
                    pagewidth, pageheight, pageunit);
 
695
            }
 
696
        }
 
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);
 
701
 
 
702
            if ((val = option_get_value(opt, optset)))
 
703
                strlcpy(def, val->value, 128);
 
704
 
 
705
            strcpy(opt->varname, opt->name);
 
706
            strrepl(opt->varname, "-/.", '_');
 
707
 
 
708
 
 
709
            dstrcatf(driveropts,
 
710
                "  argument {\n"
 
711
                "    var = \"%s\"\n"
 
712
                "    desc = \"%s\"\n"
 
713
                "    def_value \"%s\"\n"
 
714
                "    help = \"Minimum value: %s, Maximum value: %s\"\n"
 
715
                "  }\n\n",
 
716
                opt->varname, opt->comment, def, opt->min, opt->max);
 
717
        }
 
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);
 
722
 
 
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");
 
729
 
 
730
            dstrcatf(driveropts,
 
731
                "  option {\n"
 
732
                "    var = \"%s\"\n"
 
733
                "    desc = \"%s\"\n", opt->varname, opt->comment);
 
734
 
 
735
            if (!isempty(def) && !strcasecmp(def, "true"))
 
736
                dstrcatf(driveropts, "    default_choice \"%s\"\n", def);
 
737
            else
 
738
                dstrcatf(driveropts, "    default_choice \"no%s\"\n", def);
 
739
 
 
740
            dstrcatf(driveropts,
 
741
                "    choice \"%s\" {\n"
 
742
                "      desc = \"%s\"\n"
 
743
                "      value = \" -o %s=True\"\n"
 
744
                "    }\n"
 
745
                "    choice \"no%s\" {\n"
 
746
                "      desc = \"%s\"\n"
 
747
                "      value = \" -o %s=False\"\n"
 
748
                "    }\n"
 
749
                "  }\n\n",
 
750
                opt->name, setting_true->comment, opt->name,
 
751
                opt->name, setting_false->comment, opt->name);
 
752
        }
 
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);
 
757
 
 
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, "-/.", '_');
 
762
 
 
763
            dstrclear(tmp);
 
764
            if (opt->maxlength)
 
765
                dstrcatf(tmp, "Maximum Length: %s characters, ", opt->maxlength);
 
766
 
 
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 */
 
771
            }
 
772
        }
 
773
    }
 
774
 
 
775
    /* Define the "docs" option to print the driver documentation page */
 
776
    dstrcatf(driveropts,
 
777
        "  option {\n"
 
778
        "    var = \"DRIVERDOCS\"\n"
 
779
        "    desc = \"Print driver usage information\"\n"
 
780
        "    default_choice \"nodocs\"\n"
 
781
        "    choice \"docs\" {\n"
 
782
        "      desc = \"Yes\"\n"
 
783
        "      value = \" -o docs\"\n"
 
784
        "    }\n"
 
785
        "    choice \"nodocs\" {\n"
 
786
        "      desc = \"No\"\n"
 
787
        "      value = \"\"\n"
 
788
        "    }\n"
 
789
        "  }\n\n");
 
790
 
 
791
    /* Build the foomatic-rip command line */
 
792
    dstrcatf(cmdline, "foomatic-rip --pdq");
 
793
    if (!isempty(printer)) {
 
794
        dstrcatf(cmdline, " -P %s", printer);
 
795
    }
 
796
    else {
 
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);
 
800
    }
 
801
 
 
802
    for (opt = optionlist; opt; opt = opt->next) {
 
803
        if (!isempty(opt->varname))
 
804
            dstrcatf(cmdline, "${%s}", opt->varname);
 
805
    }
 
806
    dstrcatf(cmdline, "${DRIVERDOCS} $INPUT > $OUTPUT");
 
807
 
 
808
 
 
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)
 
815
            continue;
 
816
 
 
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) {
 
822
            dstrcatf(psfilter,
 
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);
 
826
        }
 
827
 
 
828
        dstrcatf(psfilter, "      %s=\" -o %s='${%s}'\"\n", opt->varname, opt->name, opt->varname);
 
829
 
 
830
        if (opt->type == TYPE_INT || opt->type == TYPE_FLOAT)
 
831
            dstrcatf(psfilter, "      fi\n");
 
832
        dstrcatf(psfilter, "\n");
 
833
    }
 
834
 
 
835
    /* Command execution */
 
836
    dstrcatf(psfilter,
 
837
        "      if ! test -e $INPUT.ok; then\n"
 
838
        "        sh -c \"%s\"\n"
 
839
        "        if ! test -e $OUTPUT; then \n"
 
840
        "          echo 'Error running foomatic-rip; no output!'\n"
 
841
        "          exit 1\n"
 
842
        "        fi\n"
 
843
        "      else\n"
 
844
        "        ln -s $INPUT $OUTPUT\n"
 
845
        "      fi\n\n", cmdline->data);
 
846
 
 
847
 
 
848
    dstrclear(tmp);
 
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';
 
854
    }
 
855
 
 
856
 
 
857
    fprintf(pdqfile,
 
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 */
 
862
        "\n"
 
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"
 
868
        "    # pre-filtering\n"
 
869
        "    filetype_regx \"\"\n"
 
870
        "    convert_exec {\n"
 
871
        "      ln -s $INPUT $OUTPUT\n"
 
872
        "    }\n"
 
873
        "  }\n\n"
 
874
        "  filter_exec {\n"
 
875
        "%s" /* setcustompagesize */
 
876
        "%s" /* psfilter */
 
877
        "  }\n"
 
878
        "}\n",
 
879
        tmp->data, /* cleaned printer_model */ (unsigned int)job->time, job->ppdfile, printer_model,
 
880
        driveropts->data, setcustompagesize->data, psfilter->data);
 
881
 
 
882
 
 
883
    free_dstr(setcustompagesize);
 
884
    free_dstr(driveropts);
 
885
    free_dstr(tmp);
 
886
    free_dstr(cmdline);
 
887
    free_dstr(psfilter);
 
888
}
 
889
#endif
 
890
 
 
891
/*  Functions to let foomatic-rip fork to do several tasks in parallel.
 
892
 
 
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
 
898
files used.
 
899
 
 
900
foomatic-rip splits into up to 6 parallel processes to do the whole
 
901
filtering (listed in the order of the data flow):
 
902
 
 
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
 
906
         and "docs" jobs)
 
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
 
920
         PPD files.
 
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. */
 
924
 
 
925
 
 
926
 
 
927
void write_output(void *data, size_t len)
 
928
{
 
929
    const char *p = (const char *)data;
 
930
    size_t left = len;
 
931
    FILE *postpipe = open_postpipe();
 
932
 
 
933
    /* Remove leading whitespace */
 
934
    while (isspace(*p++) && left-- > 0)
 
935
        ;
 
936
 
 
937
    fwrite((void *)p, left, 1, postpipe);
 
938
    fflush(postpipe);
 
939
}
 
940
 
 
941
enum FileType {
 
942
    UNKNOWN_FILE,
 
943
    PDF_FILE,
 
944
    PS_FILE
 
945
};
 
946
 
 
947
int guess_file_type(const char *begin, size_t len, int *startpos)
 
948
{
 
949
    const char * p, * end;
 
950
    p = begin;
 
951
    end = begin + len;
 
952
 
 
953
    while (p < end)
 
954
    {
 
955
        p = memchr(p, '%', end - p);
 
956
        if (!p)
 
957
            return UNKNOWN_FILE;
 
958
        *startpos = p - begin;
 
959
        if ((end - p) > 2 && !memcmp(p, "%!", 2))
 
960
            return PS_FILE;
 
961
        else if ((end - p) > 7 && !memcmp(p, "%PDF-1.", 7))
 
962
            return PDF_FILE;
 
963
        ++ p;
 
964
    }
 
965
    *startpos = 0;
 
966
    return UNKNOWN_FILE;
 
967
}
 
968
 
 
969
/*
 
970
 * Prints 'filename'. If 'convert' is true, the file will be converted if it is
 
971
 * not postscript or pdf
 
972
 */
 
973
int print_file(const char *filename, int convert)
 
974
{
 
975
    FILE *file;
 
976
    char buf[8192];
 
977
    int type;
 
978
    int startpos;
 
979
    size_t n;
 
980
    FILE *fchandle = NULL;
 
981
    int fcpid = 0, ret;
 
982
 
 
983
    if (!strcasecmp(filename, "<STDIN>"))
 
984
        file = stdin;
 
985
    else {
 
986
        file = fopen(filename, "r");
 
987
        if (!file) {
 
988
            _log("Could not open \"%s\" for reading\n", filename);
 
989
            return 0;
 
990
        }
 
991
    }
 
992
 
 
993
    n = fread(buf, 1, sizeof(buf) - 1, file);
 
994
    buf[n] = '\0';
 
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) {
 
1000
        jobhasjcl = 1;
 
1001
        write_output(buf, startpos);
 
1002
    }*/
 
1003
    if (file != stdin)
 
1004
        rewind(file);
 
1005
 
 
1006
    if (convert) pdfconvertedtops = 0;
 
1007
 
 
1008
    switch (type) {
 
1009
        case PDF_FILE:
 
1010
            _log("Filetype: PDF\n");
 
1011
 
 
1012
            if (!ppd_supports_pdf())
 
1013
            {
 
1014
                char pdf2ps_cmd[PATH_MAX];
 
1015
                FILE *out, *in;
 
1016
                int renderer_pid;
 
1017
                char tmpfilename[PATH_MAX] = "";
 
1018
 
 
1019
                _log("Driver does not understand PDF input, "
 
1020
                     "converting to PostScript\n");
 
1021
 
 
1022
                pdfconvertedtops = 1;
 
1023
 
 
1024
                /* If reading from stdin, write everything into a temporary file */
 
1025
                if (file == stdin)
 
1026
                {
 
1027
                    int fd;
 
1028
                    FILE *tmpfile;
 
1029
                    
 
1030
                    snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
 
1031
                    fd = mkstemp(tmpfilename);
 
1032
                    if (fd < 0) {
 
1033
                        _log("Could not create temporary file: %s\n", strerror(errno));
 
1034
                        return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
 
1035
                    }
 
1036
                    tmpfile = fdopen(fd, "r+");
 
1037
                    copy_file(tmpfile, stdin, buf, n);
 
1038
                    fclose(tmpfile);
 
1039
                    
 
1040
                    filename = tmpfilename;
 
1041
                }
 
1042
 
 
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);
 
1056
 
 
1057
                renderer_pid = start_system_process("pdf-to-ps", pdf2ps_cmd, &in, &out);
 
1058
 
 
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");
 
1062
 
 
1063
                ret = print_file("<STDIN>", 0);
 
1064
 
 
1065
                wait_for_process(renderer_pid);
 
1066
                return ret;
 
1067
            }
 
1068
 
 
1069
            if (file == stdin)
 
1070
                return print_pdf(stdin, buf, n, filename, startpos);
 
1071
            else
 
1072
                return print_pdf(file, NULL, 0, filename, startpos);
 
1073
 
 
1074
        case PS_FILE:
 
1075
            _log("Filetype: PostScript\n");
 
1076
            if (file == stdin)
 
1077
                return print_ps(stdin, buf, n, filename);
 
1078
            else
 
1079
                return print_ps(file, NULL, 0, filename);
 
1080
 
 
1081
        case UNKNOWN_FILE:
 
1082
            if (spooler == SPOOLER_CUPS) {
 
1083
                _log("Cannot process \"%s\": Unknown filetype.\n", filename);
 
1084
                return 0;
 
1085
            }
 
1086
 
 
1087
            _log("Filetype unknown, trying to convert ...\n");
 
1088
            get_fileconverter_handle(buf, &fchandle, &fcpid);
 
1089
 
 
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");
 
1093
 
 
1094
            ret = print_file("<STDIN>", 0);
 
1095
 
 
1096
            if (close_fileconverter_handle(fchandle, fcpid) != EXIT_PRINTED)
 
1097
                rip_die(ret, "Error closing file converter\n");
 
1098
            return ret;
 
1099
    }
 
1100
 
 
1101
    fclose(file);
 
1102
    return 1;
 
1103
}
 
1104
 
 
1105
void signal_terminate(int signal)
 
1106
{
 
1107
    rip_die(EXIT_PRINTED, "Caught termination signal: Job canceled\n");
 
1108
}
 
1109
 
 
1110
jobparams_t * create_job()
 
1111
{
 
1112
    jobparams_t *job = calloc(1, sizeof(jobparams_t));
 
1113
    struct passwd *passwd;
 
1114
 
 
1115
    job->optstr = create_dstr();
 
1116
    job->time = time(NULL);
 
1117
    strcpy(job->copies, "1");
 
1118
    gethostname(job->host, 128);
 
1119
    passwd = getpwuid(getuid());
 
1120
    if (passwd)
 
1121
        strlcpy(job->user, passwd->pw_name, 128);
 
1122
    snprintf(job->title, 128, "%s@%s", job->user, job->host);
 
1123
 
 
1124
    return job;
 
1125
}
 
1126
 
 
1127
void free_job(jobparams_t *job)
 
1128
{
 
1129
    free_dstr(job->optstr);
 
1130
    free(job);
 
1131
}
 
1132
 
 
1133
int main(int argc, char** argv)
 
1134
{
 
1135
    int i;
 
1136
    int verbose = 0, quiet = 0, showdocs = 0;
 
1137
    const char* str;
 
1138
    char *p, *filename;
 
1139
    const char *path;
 
1140
    FILE *genpdqfile = NULL;
 
1141
    FILE *ppdfh = NULL;
 
1142
    char tmp[1024], pstoraster[256];
 
1143
    int havefilter, havepstoraster;
 
1144
    dstr_t *filelist;
 
1145
    list_t * arglist;
 
1146
 
 
1147
    arglist = list_create_from_array(argc -1, (void**)&argv[1]);
 
1148
 
 
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");
 
1153
        list_free(arglist);
 
1154
        return 0;
 
1155
    }
 
1156
 
 
1157
    filelist = create_dstr();
 
1158
    job = create_job();
 
1159
 
 
1160
    jclprepend = NULL;
 
1161
    jclappend = create_dstr();
 
1162
    postpipe = create_dstr();
 
1163
 
 
1164
    options_init();
 
1165
 
 
1166
    signal(SIGTERM, signal_terminate);
 
1167
    signal(SIGINT, signal_terminate);
 
1168
 
 
1169
 
 
1170
    config_from_file(CONFIG_PATH "/filter.conf");
 
1171
 
 
1172
    /* Command line options for verbosity */
 
1173
    if (arglist_remove_flag(arglist, "-v"))
 
1174
        verbose = 1;
 
1175
    if (arglist_remove_flag(arglist, "-q"))
 
1176
        quiet = 1;
 
1177
    if (arglist_remove_flag(arglist, "-d"))
 
1178
        showdocs = 1;
 
1179
    if (arglist_remove_flag(arglist, "--debug"))
 
1180
        debug = 1;
 
1181
 
 
1182
    if (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 */
 
1186
    else
 
1187
        logh = stderr; /* Default: log to stderr */
 
1188
 
 
1189
    /* Start debug logging */
 
1190
    if (debug) {
 
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");
 
1196
 
 
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]);
 
1206
        }
 
1207
    }
 
1208
 
 
1209
    if (getenv("PPD")) {
 
1210
        strncpy(job->ppdfile, getenv("PPD"), 256);
 
1211
        spooler = SPOOLER_CUPS;
 
1212
    }
 
1213
 
 
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*/
 
1220
    }
 
1221
 
 
1222
    if (getenv("PPR_VERSION"))
 
1223
        spooler = SPOOLER_PPR;
 
1224
 
 
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;
 
1231
    }
 
1232
 
 
1233
    if (getenv("LPOPTS")) { /* "LPOPTS": Option settings for some LPD implementations (ex: GNUlpr) */
 
1234
        spooler = SPOOLER_GNULPR;
 
1235
        dstrcatf(job->optstr, "%s ", getenv("LPOPTS"));
 
1236
    }
 
1237
 
 
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;
 
1241
 
 
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=")))
 
1247
            str += 4;
 
1248
        else if ((str = strstr(getenv("PRINTCAP_ENTRY"), "ppdfile=")))
 
1249
            str += 8;
 
1250
        if (str) {
 
1251
            while (isspace(*str)) str++;
 
1252
            p = job->ppdfile;
 
1253
            while (*str != '\0' && !isspace(*str) && *str != '\n' &&
 
1254
                   *str != ':') {
 
1255
                if (isprint(*str) && strchr(shellescapes, *str) == NULL)
 
1256
                    *p++ = *str;
 
1257
                str++;
 
1258
            }
 
1259
        }
 
1260
    }
 
1261
 
 
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");
 
1268
        }
 
1269
    }
 
1270
    while ((str = arglist_get_value(arglist, "--ppd"))) {
 
1271
        strncpy(job->ppdfile, str, 256);
 
1272
        arglist_remove(arglist, "--ppd");
 
1273
    }
 
1274
 
 
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");
 
1285
    }
 
1286
    if ((str = arglist_get_value(arglist, "-n"))) {
 
1287
        if (spooler != SPOOLER_GNULPR && spooler != SPOOLER_LPRNG)
 
1288
            spooler = SPOOLER_LPD;
 
1289
 
 
1290
        strncpy(job->user, str, 127);
 
1291
        job->user[127] = '\0';
 
1292
        arglist_remove(arglist, "-n");
 
1293
    }
 
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;
 
1302
    }
 
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");
 
1308
    }
 
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");
 
1316
    }
 
1317
    /* Check for CPS */
 
1318
    if (arglist_remove_flag(arglist, "--cps") > 0)
 
1319
        spooler = SPOOLER_CPS;
 
1320
 
 
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;
 
1330
    }
 
1331
 
 
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");
 
1336
    }
 
1337
 
 
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");
 
1342
    }
 
1343
 
 
1344
    /* Were we called from a PDQ wrapper? */
 
1345
    if (arglist_remove_flag(arglist, "--pdq"))
 
1346
        spooler = SPOOLER_PDQ;
 
1347
 
 
1348
    /* Were we called to build the PDQ driver declaration file? */
 
1349
    genpdqfile = check_pdq_file(arglist);
 
1350
    if (genpdqfile)
 
1351
        spooler = SPOOLER_PDQ;
 
1352
 
 
1353
    /* spooler specific initialization */
 
1354
    switch (spooler) {
 
1355
        case SPOOLER_PPR:
 
1356
            init_ppr(arglist, job);
 
1357
            break;
 
1358
 
 
1359
        case SPOOLER_CUPS:
 
1360
            init_cups(arglist, filelist, job);
 
1361
            break;
 
1362
 
 
1363
        case SPOOLER_LPRNG:
 
1364
            if (job->ppdfile[0] != '\0') break;
 
1365
        case SPOOLER_LPD:
 
1366
        case SPOOLER_GNULPR:
 
1367
            /* Get PPD file name as the last command line argument */
 
1368
            if (arglist->last)
 
1369
                strncpy(job->ppdfile, (char*)arglist->last->data, 256);
 
1370
            break;
 
1371
 
 
1372
        case SPOOLER_DIRECT:
 
1373
        case SPOOLER_CPS:
 
1374
        case SPOOLER_PDQ:
 
1375
            init_direct_cps_pdq(arglist, filelist, job);
 
1376
            break;
 
1377
    }
 
1378
 
 
1379
    /* Files to be printed (can be more than one for spooler-less printing) */
 
1380
    /* Empty file list -> print STDIN */
 
1381
    dstrtrim(filelist);
 
1382
    if (filelist->len == 0)
 
1383
        dstrcpyf(filelist, "<STDIN>");
 
1384
 
 
1385
    /* Check filelist */
 
1386
    p = strtok(filelist->data, " ");
 
1387
    while (p) {
 
1388
        if (strcmp(p, "<STDIN>") != 0) {
 
1389
            if (p[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);
 
1393
            strclr(p);
 
1394
            }
 
1395
        }
 
1396
        p = strtok(NULL, " ");
 
1397
    }
 
1398
 
 
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)
 
1403
            fclose(logh);
 
1404
        logh = NULL;
 
1405
    }
 
1406
 
 
1407
    /* If we are in debug mode, we do this earlier. */
 
1408
    if (!debug) {
 
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]);
 
1419
        }
 
1420
    }
 
1421
 
 
1422
    /* PPD File */
 
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);
 
1427
 
 
1428
    read_ppd_file(job->ppdfile);
 
1429
 
 
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
 
1433
       if nexessary. */
 
1434
    if (option_count() == 0) {
 
1435
        /* We don't have any options, so we do not need to parse the
 
1436
           PostScript data */
 
1437
        dontparse = 1;
 
1438
    }
 
1439
 
 
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 */
 
1444
        havefilter = 0;
 
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) {
 
1450
                havefilter = 1;
 
1451
                strlcpy(cupsfilter, tmp, 256);
 
1452
                strlcat(cupsfilter, " 0 '' '' 0 '%Y%X'", 256);
 
1453
                break;
 
1454
            }
 
1455
        }
 
1456
 
 
1457
        if (!havefilter) {
 
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");
 
1467
        }
 
1468
        else {
 
1469
            /* use pstoraster script if available, otherwise run Ghostscript directly */
 
1470
            havepstoraster = 0;
 
1471
            path = cupsfilterpath;
 
1472
            while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
 
1473
                strlcat(tmp, "/pstoraster", 1024);
 
1474
                if (access(tmp, X_OK) == 0) {
 
1475
                    havepstoraster = 1;
 
1476
                    strlcpy(pstoraster, tmp, 256);
 
1477
                    strlcat(pstoraster, " 0 '' '' 0 '%X'", 256);
 
1478
                    break;
 
1479
                }
 
1480
            }
 
1481
            if (!havepstoraster) {
 
1482
                strcpy(pstoraster, "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOMEDIAATTRS -sDEVICE=cups -sOutputFile=-%W -");
 
1483
            }
 
1484
 
 
1485
            /* build Ghostscript/CUPS driver command line */
 
1486
            snprintf(cmd, 1024, "%s | %s", pstoraster, cupsfilter);
 
1487
 
 
1488
            /* Set environment variables */
 
1489
            setenv("PPD", job->ppdfile, 1);
 
1490
        }
 
1491
    }
 
1492
 
 
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 */
 
1495
    if (isempty(cmd)) {
 
1496
        strcpy(cmd, "cat%A%B%C%D%E%F%G%H%I%J%K%L%M%Z");
 
1497
        if (dontparse) {
 
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.*/
 
1501
            dontparse = 2;
 
1502
            strcpy(printer_model, "Raw queue");
 
1503
        }
 
1504
    }
 
1505
 
 
1506
    /* Summary for debugging */
 
1507
    _log("\nParameter Summary\n"
 
1508
         "-----------------\n\n"
 
1509
         "Spooler: %s\n"
 
1510
         "Printer: %s\n"
 
1511
         "Shell: %s\n"
 
1512
         "PPD file: %s\n"
 
1513
         "ATTR file: %s\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"));
 
1527
 
 
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();
 
1532
 
 
1533
    /* Were we called to build the PDQ driver declaration file? */
 
1534
    if (genpdqfile) {
 
1535
        print_pdq_driver(genpdqfile, optionset("userval"));
 
1536
        fclose(genpdqfile);
 
1537
        exit(EXIT_PRINTED);
 
1538
    }
 
1539
 
 
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);
 
1545
 
 
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");
 
1550
 
 
1551
        /* Put the backend interface into the postpipe */
 
1552
        /* TODO
 
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\" \"\" )";
 
1558
        */
 
1559
    }
 
1560
 
 
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);
 
1564
 
 
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");
 
1568
 
 
1569
    if (postpipe->len)
 
1570
        _log("Ouput will be redirected to:\n%s\n", postpipe);
 
1571
 
 
1572
 
 
1573
    /* Print documentation page when asked for */
 
1574
    if (do_docs) {
 
1575
        /* Don't print the supplied files, STDIN will be redirected to the
 
1576
           documentation page generator */
 
1577
        dstrcpyf(filelist, "<STDIN>");
 
1578
 
 
1579
        /* Start the documentation page generator */
 
1580
        /* TODO tbd */
 
1581
    }
 
1582
 
 
1583
    /* In debug mode save the data supposed to be fed into the
 
1584
       renderer also into a file, reset the file here */
 
1585
    if (debug)
 
1586
        run_system_process("reset-file", "> " LOG_FILE ".ps");
 
1587
 
 
1588
    filename = strtok_r(filelist->data, " ", &p);
 
1589
    while (filename) {
 
1590
        _log("\n================================================\n\n"
 
1591
             "File: %s\n\n"
 
1592
             "================================================\n\n", filename);
 
1593
 
 
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);
 
1601
            continue;
 
1602
        }
 
1603
 
 
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"));
 
1610
 
 
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);
 
1614
    }
 
1615
 
 
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");
 
1621
            exit(retval);
 
1622
        }
 
1623
        docgenerator_pid = 0;
 
1624
    } */
 
1625
 
 
1626
    /* Close the last input file */
 
1627
    fclose(stdin);
 
1628
 
 
1629
    /* TODO dump everything in $dat when debug is turned on (necessary?) */
 
1630
 
 
1631
    _log("\nClosing foomatic-rip.\n");
 
1632
 
 
1633
 
 
1634
    /* Cleanup */
 
1635
    free_job(job);
 
1636
    if (genpdqfile && genpdqfile != stdout)
 
1637
        fclose(genpdqfile);
 
1638
    free_dstr(filelist);
 
1639
    options_free();
 
1640
    close_log();
 
1641
 
 
1642
    argv_free(jclprepend);
 
1643
    free_dstr(jclappend);
 
1644
    if (backendoptions)
 
1645
        free_dstr(backendoptions);
 
1646
 
 
1647
    list_free(arglist);
 
1648
 
 
1649
    return EXIT_PRINTED;
 
1650
}
 
1651