"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "postscript.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
4.0.17_vs_4.0-20160212.
1 /* postscript.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 "fileconverter.h"
28 #include "renderer.h"
29 #include "process.h"
30
31 #include <errno.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35
36 void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid);
37 int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid);
38
39 #define LT_BEGIN_FEATURE 1
40 #define LT_FOOMATIC_RIP_OPTION_SETTING 2
41 int line_type(const char *line)
42 {
43 const char *p;
44 if (startswith(line, "%%BeginFeature:"))
45 return LT_BEGIN_FEATURE;
46 p = line;
47 while (*p && isspace(*p)) p++;
48 if (!startswith(p, "%%"))
49 return 0;
50 p += 2;
51 while (*p && isspace(*p)) p++;
52 if (startswith(p, "FoomaticRIPOptionSetting:"))
53 return LT_FOOMATIC_RIP_OPTION_SETTING;
54 return 0;
55 }
56
57
58 /* Next, examine the PostScript job for traces of command-line and
59 JCL options. PPD-aware applications and spoolers stuff option
60 settings directly into the file, they do not necessarily send
61 PPD options by the command line. Also stuff in PostScript code
62 to apply option settings given by the command line and to set
63 the defaults given in the PPD file.
64
65 Examination strategy: read lines from STDIN until the first
66 %%Page: comment appears and save them as @psheader. This is the
67 page-independent header part of the PostScript file. The
68 PostScript interpreter (renderer) must execute this part once
69 before rendering any assortment of pages. Then pages can be
70 printed in any arbitrary selection or order. All option
71 settings we find here will be collected in the default option
72 set for the RIP command line.
73
74 Now the pages will be read and sent to the renderer, one after
75 the other. Every page is read into memory until the
76 %%EndPageSetup comment appears (or a certain amount of lines was
77 read). So we can get option settings only valid for this
78 page. If we have such settings we set them in the modified
79 command set for this page.
80
81 If the renderer is not running yet (first page) we start it with
82 the command line built from the current modified command set and
83 send the first page to it, in the end we leave the renderer
84 running and keep input and output pipes open, so that it can
85 accept further pages. If the renderer is still running from
86 the previous page and the current modified command set is the
87 same as the one for the previous page, we send the page. If
88 the command set is different, we close the renderer, re-start
89 it with the command line built from the new modified command
90 set, send the header again, and then the page.
91
92 After the last page the trailer (%%Trailer) is sent.
93
94 The output pipe of this program stays open all the time so that
95 the spooler does not assume that the job has finished when the
96 renderer is re-started.
97
98 Non DSC-conforming documents will be read until a certain line
99 number is reached. Command line or JCL options inserted later
100 will be ignored.
101
102 If options are implemented by PostScript code supposed to be
103 stuffed into the job's PostScript data we stuff the code for all
104 these options into our job data, So all default settings made in
105 the PPD file (the user can have edited the PPD file to change
106 them) are taken care of and command line options get also
107 applied. To give priority to settings made by applications we
108 insert the options's code in the beginnings of their respective
109 sections, so that sommething, which is already inserted, gets
110 executed after our code. Missing sections are automatically
111 created. In non-DSC-conforming files we insert the option code
112 in the beginning of the file. This is the same policy as used by
113 the "pstops" filter of CUPS.
114
115 If CUPS is the spooler, the option settings were already
116 inserted by the "pstops" filter, so we don't insert them
117 again. The only thing we do is correcting settings of numerical
118 options when they were set to a value not available as choice in
119 the PPD file, As "pstops" does not support "real" numerical
120 options, it sees these settings as an invalid choice and stays
121 with the default setting. In this case we correct the setting in
122 the first occurence of the option's code, as this one is the one
123 added by CUPS, later occurences come from applications and
124 should not be touched.
125
126 If the input is not PostScript (if there is no "%!" after
127 $maxlinestopsstart lines) a file conversion filter will
128 automatically be applied to the incoming data, so that we will
129 process the resulting PostScript here. This way we have always
130 PostScript data here and so we can apply the printer/driver
131 features described in the PPD file.
132
133 Supported file conversion filters are "a2ps", "enscript",
134 "mpage", "paps", and spooler-specific filters. All filters
135 convert plain text to PostScript, "a2ps" also other formats.
136 The conversion filter is always used when one prints the
137 documentation pages, as they are created as plain text,
138 when CUPS is the spooler "pstops" is executed after the
139 filter so that the default option settings from the PPD file
140 and CUPS-specific options as N-up get applied. On regular
141 printouts one gets always PostScript when CUPS or PPR is
142 the spooler, so the filter is only used for regular
143 printouts under LPD, LPRng, GNUlpr or without spooler.
144 */
145
146 /* PostScript sections */
147 #define PS_SECTION_JCLSETUP 1
148 #define PS_SECTION_PROLOG 2
149 #define PS_SECTION_SETUP 3
150 #define PS_SECTION_PAGESETUP 4
151
152 #define MAX_NON_DSC_LINES_IN_HEADER 1000
153 #define MAX_LINES_FOR_PAGE_OPTIONS 200
154
155 typedef struct {
156 size_t pos;
157
158 FILE *file;
159 const char *alreadyread;
160 size_t len;
161 } stream_t;
162
163 void _print_ps(stream_t *stream);
164
165 int stream_next_line(dstr_t *line, stream_t *s)
166 {
167 int c;
168 size_t cnt = 0;
169
170 dstrclear(line);
171 while (s->pos < s->len) {
172 c = s->alreadyread[s->pos++];
173 dstrputc(line, c);
174 cnt++;
175 if (c == '\n')
176 return cnt;
177 }
178
179 while ((c = fgetc(s->file)) != EOF) {
180 dstrputc(line, c);
181 cnt++;
182 if (c == '\n')
183 return cnt;
184 }
185 return cnt;
186 }
187
188 int print_ps(FILE *file, const char *alreadyread, size_t len, const char *filename)
189 {
190 stream_t stream;
191
192 if (file != stdin && (dup2(fileno(file), fileno(stdin)) < 0)) {
193 _log("Could not dup %s to stdin.\n", filename);
194 return 0;
195 }
196
197 stream.pos = 0;
198 stream.file = stdin;
199 stream.alreadyread = alreadyread;
200 stream.len = len;
201 _print_ps(&stream);
202 return 1;
203 }
204
205 void _print_ps(stream_t *stream)
206 {
207 char *p;
208
209 int maxlines = 1000; /* Maximum number of lines to be read when the
210 documenent is not DSC-conforming.
211 "$maxlines = 0" means that all will be read and
212 examined. If it is discovered that the input
213 file is DSC-conforming, this will be set to 0. */
214
215 int maxlinestopsstart = 200; /* That many lines are allowed until the
216 "%!" indicating PS comes. These
217 additional lines in the
218 beginning are usually JCL
219 commands. The lines will be
220 ignored by our parsing but
221 passed through. */
222
223 int printprevpage = 0; /* We set this when encountering "%%Page:" and the
224 previous page is not printed yet. Then it will
225 be printed and the new page will be prepared in
226 the next run of the loop (we don't read a new
227 line and don't increase the $linect then). */
228
229 int linect = 0; /* how many lines have we examined */
230 int nonpslines = 0; /* lines before "%!" found yet. */
231 int more_stuff = 1; /* there is more stuff in stdin */
232 int saved = 0; /* DSC line not precessed yet */
233 int isdscjob = 0; /* is the job dsc conforming */
234 int inheader = 1; /* Are we still in the header, before first
235 "%%Page:" comment= */
236
237 int optionsalsointoheader = 0; /* 1: We are in a "%%BeginSetup...
238 %%EndSetup" section after the first
239 "%%Page:..." line (OpenOffice.org
240 does this and intends the options here
241 apply to the whole document and not
242 only to the current page). We have to
243 add all lines also to the end of the
244 @psheader now and we have to set
245 non-PostScript options also in the
246 "header" optionset. 0: otherwise. */
247
248 int insertoptions = 1; /* If we find out that a file with a DSC magic
249 string ("%!PS-Adobe-") is not really DSC-
250 conforming, we insert the options directly
251 after the line with the magic string. We use
252 this variable to store the number of the line
253 with the magic string */
254
255 int prologfound = 0; /* Did we find the
256 "%%BeginProlog...%%EndProlog" section? */
257 int setupfound = 0; /* Did we find the
258 %%BeginSetup...%%EndSetup" section? */
259 int pagesetupfound = 0; /* special page setup handling needed */
260
261 int inprolog = 0; /* We are between "%%BeginProlog" and "%%EndProlog" */
262 int insetup = 0; /* We are between "%%BeginSetup" and "%%EndSetup" */
263 int infeature = 0; /* We are between "%%BeginFeature" and "%%EndFeature" */
264
265 int optionreplaced = 0; /* Will be set to 1 when we are in an
266 option ("%%BeginFeature...
267 %%EndFeature") which we have replaced. */
268
269 int postscriptsection = PS_SECTION_JCLSETUP; /* In which section of the PostScript file
270 are we currently ? */
271
272 int nondsclines = 0; /* Number of subsequent lines found which are at a
273 non-DSC-conforming place, between the sections
274 of the header.*/
275
276 int nestinglevel = 0; /* Are we in the main document (0) or in an
277 embedded document bracketed by "%%BeginDocument"
278 and "%%EndDocument" (>0) We do not parse the
279 PostScript in an embedded document. */
280
281 int inpageheader = 0; /* Are we in the header of a page,
282 between "%%BeginPageSetup" and
283 "%%EndPageSetup" (1) or not (0). */
284
285 int passthru = 0; /* 0: write data into psfifo,
286 1: pass data directly to the renderer */
287
288 int lastpassthru = 0; /* State of 'passthru' in previous line
289 (to allow debug output when $passthru
290 switches. */
291
292 int ignorepageheader = 0; /* Will be set to 1 as soon as active
293 code (not between "%%BeginPageSetup"
294 and "%%EndPageSetup") appears after a
295 "%%Page:" comment. In this case
296 "%%BeginPageSetup" and
297 "%%EndPageSetup" is not allowed any
298 more on this page and will be ignored.
299 Will be set to 0 when a new "%%Page:"
300 comment appears. */
301
302 int optset = optionset("header"); /* Where do the option settings which
303 we have found go? */
304
305 /* current line */
306 dstr_t *line = create_dstr();
307
308 dstr_t *onelinebefore = create_dstr();
309 dstr_t *twolinesbefore = create_dstr();
310
311 /* The header of the PostScript file, to be send after each start of the renderer */
312 dstr_t *psheader = create_dstr();
313
314 /* The input FIFO, data which we have pulled from stdin for examination,
315 but not send to the renderer yet */
316 dstr_t *psfifo = create_dstr();
317
318 FILE *fileconverter_handle = NULL; /* File handle to converter process */
319 pid_t fileconverter_pid = 0; /* PID of the fileconverter process */
320
321 int ignoreline;
322
323 int ooo110 = 0; /* Flag to work around an application bug */
324
325 int currentpage = 0; /* The page which we are currently printing */
326
327 option_t *o;
328 const char *val;
329
330 int linetype;
331
332 dstr_t *linesafterlastbeginfeature = create_dstr(); /* All codelines after the last "%%BeginFeature" */
333
334 char optionname [128];
335 char value [128];
336 int fromcomposite = 0;
337
338 dstr_t *pdest;
339
340 double width, height;
341
342 pid_t rendererpid = 0;
343 FILE *rendererhandle = NULL;
344
345 int retval;
346
347 dstr_t *tmp = create_dstr();
348 jobhasjcl = 0;
349
350 /* We do not parse the PostScript to find Foomatic options, we check
351 only whether we have PostScript. */
352 if (dontparse)
353 maxlines = 1;
354
355 _log("Reading PostScript input ...\n");
356
357 do {
358 ignoreline = 0;
359
360 if (printprevpage || saved || stream_next_line(line, stream)) {
361 saved = 0;
362 if (linect == nonpslines) {
363 /* In the beginning should be the postscript leader,
364 sometimes after some JCL commands */
365 if ( !(line->data[0] == '%' && line->data[1] == '!') &&
366 !(line->data[1] == '%' && line->data[2] == '!')) /* There can be a Windows control character before "%!" */
367 {
368 nonpslines++;
369 if (maxlines == nonpslines)
370 maxlines ++;
371 jobhasjcl = 1;
372
373 if (nonpslines > maxlinestopsstart) {
374 /* This is not a PostScript job, we must convert it */
375 _log("Job does not start with \"%%!\", is it Postscript?\n"
376 "Starting file converter\n");
377
378 /* Reset all variables but conserve the data which we have already read */
379 jobhasjcl = 0;
380 linect = 0;
381 nonpslines = 1; /* Take into account that the line of this run of the loop
382 will be put into @psheader, so the first line read by
383 the file converter is already the second line */
384 maxlines = 1001;
385
386 dstrclear(onelinebefore);
387 dstrclear(twolinesbefore);
388
389 dstrcpyf(tmp, "%s%s%s", psheader, psfifo, line);
390 dstrclear(psheader);
391 dstrclear(psfifo);
392 dstrclear(line);
393
394 /* Start the file conversion filter */
395 if (!fileconverter_pid)
396 get_fileconverter_handle(tmp->data, &fileconverter_handle, &fileconverter_pid);
397 else
398 rip_die(EXIT_JOBERR, "File conversion filter probably crashed\n");
399
400 /* Read the further data from the file converter and not from STDIN */
401 if (close(fileno(stdin)) == -1 && errno != ESPIPE)
402 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't close STDIN\n");
403 if (dup2(fileno(stdin), fileno(fileconverter_handle)) == -1)
404 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't dup fileconverter_handle\n");
405 }
406 }
407 else {
408 /* Do we have a DSC-conforming document? */
409 if ((line->data[0] == '%' && startswith(line->data, "%!PS-Adobe-")) ||
410 (line->data[1] == '%' && startswith(line->data, "%!PS-Adobe-")))
411 {
412 /* Do not stop parsing the document */
413 if (!dontparse) {
414 maxlines = 0;
415 isdscjob = 1;
416 insertoptions = linect + 1;
417 /* We have written into psfifo before, now we continue in
418 psheader and move over the data which is already in psfifo */
419 dstrcat(psheader, psfifo->data);
420 dstrclear(psfifo);
421 }
422 _log("--> This document is DSC-conforming!\n");
423 }
424 else {
425 /* Job is not DSC-conforming, stick in all PostScript
426 option settings in the beginning */
427 append_prolog_section(line, optset, 1);
428 append_setup_section(line, optset, 1);
429 append_page_setup_section(line, optset, 1);
430 prologfound = 1;
431 setupfound = 1;
432 pagesetupfound = 1;
433 }
434 }
435 }
436 else {
437 if (startswith(line->data, "%")) {
438 if (startswith(line->data, "%%BeginDocument")) {
439 /* Beginning of an embedded document
440 Note that Adobe Acrobat has a bug and so uses
441 "%%BeginDocument " instead of "%%BeginDocument:" */
442 nestinglevel++;
443 _log("Embedded document, nesting level now: %d\n", nestinglevel);
444 }
445 else if (nestinglevel > 0 && startswith(line->data, "%%EndDocument")) {
446 /* End of an embedded document */
447 nestinglevel--;
448 _log("End of embedded document, nesting level now: %d\n", nestinglevel);
449 }
450 else if (nestinglevel == 0 && startswith(line->data, "%%Creator")) {
451 /* Here we set flags to treat particular bugs of the
452 PostScript produced by certain applications */
453 p = strstr(line->data, "%%Creator") + 9;
454 while (*p && (isspace(*p) || *p == ':')) p++;
455 if (!strcmp(p, "OpenOffice.org")) {
456 p += 14;
457 while (*p && isspace(*p)) p++;
458 if (sscanf(p, "1.1.%d", &ooo110) == 1) {
459 _log("Document created with OpenOffice.org 1.1.x\n");
460 ooo110 = 1;
461 }
462 } else if (!strcmp(p, "StarOffice 8")) {
463 p += 12;
464 _log("Document created with StarOffice 8\n");
465 ooo110 = 1;
466 }
467 }
468 else if (nestinglevel == 0 && startswith(line->data, "%%BeginProlog")) {
469 /* Note: Below is another place where a "Prolog" section
470 start will be considered. There we assume start of the
471 "Prolog" if the job is DSC-Conformimg, but an arbitrary
472 comment starting with "%%Begin", but not a comment
473 explicitly treated here, is found. This is done because
474 many "dvips" (TeX/LaTeX) files miss the "%%BeginProlog"
475 comment.
476 Beginning of Prolog */
477 _log("\n-----------\nFound: %%%%BeginProlog\n");
478 inprolog = 1;
479 if (inheader)
480 postscriptsection = PS_SECTION_PROLOG;
481 nondsclines = 0;
482 /* Insert options for "Prolog" */
483 if (!prologfound) {
484 append_prolog_section(line, optset, 0);
485 prologfound = 1;
486 }
487 }
488 else if (nestinglevel == 0 && startswith(line->data, "%%EndProlog")) {
489 /* End of Prolog */
490 _log("Found: %%%%EndProlog\n");
491 inprolog = 0;
492 insertoptions = linect +1;
493 }
494 else if (nestinglevel == 0 && startswith(line->data, "%%BeginSetup")) {
495 /* Beginning of Setup */
496 _log("\n-----------\nFound: %%%%BeginSetup\n");
497 insetup = 1;
498 nondsclines = 0;
499 /* We need to distinguish with the $inheader variable
500 here whether we are in the header or on a page, as
501 OpenOffice.org inserts a "%%BeginSetup...%%EndSetup"
502 section after the first "%%Page:..." line and assumes
503 this section to be valid for all pages. */
504 if (inheader) {
505 postscriptsection = PS_SECTION_SETUP;
506 /* If there was no "Prolog" but there are
507 options for the "Prolog", push a "Prolog"
508 with these options onto the psfifo here */
509 if (!prologfound) {
510 dstrclear(tmp);
511 append_prolog_section(tmp, optset, 1);
512 dstrprepend(line, tmp->data);
513 prologfound = 1;
514 }
515 /* Insert options for "DocumentSetup" or "AnySetup" */
516 if (spooler != SPOOLER_CUPS && !setupfound) {
517 /* For non-CUPS spoolers or no spooler at all,
518 we leave everythnig as it is */
519 append_setup_section(line, optset, 0);
520 setupfound = 1;
521 }
522 }
523 else {
524 /* Found option settings must be stuffed into both
525 the header and the currrent page now. They will
526 be written into both the "header" and the
527 "currentpage" optionsets and the PostScript code
528 lines of this section will not only go into the
529 output stream, but also added to the end of the
530 @psheader, so that they get repeated (to preserve
531 the embedded PostScript option settings) on a
532 restart of the renderer due to command line
533 option changes */
534 optionsalsointoheader = 1;
535 _log("\"%%%%BeginSetup\" in page header\n");
536 }
537 }
538 else if (nestinglevel == 0 && startswith(line->data, "%%EndSetup")) {
539 /* End of Setup */
540 _log("Found: %%%%EndSetup\n");
541 insetup = 0;
542 if (inheader) {
543 if (spooler == SPOOLER_CUPS) {
544 /* In case of CUPS, we must insert the
545 accounting stuff just before the
546 %%EndSetup comment in order to leave any
547 EndPage procedures that have been
548 defined by either the pstops filter or
549 the PostScript job itself fully
550 functional. */
551 if (!setupfound) {
552 dstrclear(tmp);
553 append_setup_section(tmp, optset, 0);
554 dstrprepend(line, tmp->data);
555 setupfound = 1;
556 }
557 }
558 insertoptions = linect +1;
559 }
560 else {
561 /* The "%%BeginSetup...%%EndSetup" which
562 OpenOffice.org has inserted after the first
563 "%%Page:..." line ends here, so the following
564 options go only onto the current page again */
565 optionsalsointoheader = 0;
566 }
567 }
568 else if (nestinglevel == 0 && startswith(line->data, "%%Page:")) {
569 if (!lastpassthru && !inheader) {
570 /* In the last line we were not in passthru mode,
571 so the last page is not printed. Prepare to do
572 it now. */
573 printprevpage = 1;
574 passthru = 1;
575 _log("New page found but previous not printed, print it now.\n");
576 }
577 else {
578 /* the previous page is printed, so we can prepare
579 the current one */
580 _log("\n-----------\nNew page: %s", line->data);
581 printprevpage = 0;
582 currentpage++;
583 /* We consider the beginning of the page already as
584 page setup section, as some apps do not use
585 "%%PageSetup" tags. */
586 postscriptsection = PS_SECTION_PAGESETUP;
587
588 /* TODO can this be removed?
589 Save PostScript state before beginning the page
590 $line .= "/foomatic-saved-state save def\n"; */
591
592 /* Here begins a new page */
593 if (inheader) {
594 build_commandline(optset, NULL, 0);
595 /* Here we add some stuff which still
596 belongs into the header */
597 dstrclear(tmp);
598
599 /* If there was no "Setup" but there are
600 options for the "Setup", push a "Setup"
601 with these options onto the @psfifo here */
602 if (!setupfound) {
603 append_setup_section(tmp, optset, 1);
604 setupfound = 1;
605 }
606 /* If there was no "Prolog" but there are
607 options for the "Prolog", push a "Prolog"
608 with these options onto the @psfifo here */
609 if (!prologfound) {
610 append_prolog_section(tmp, optset, 1);
611 prologfound = 1;
612 }
613 /* Now we push this into the header */
614 dstrcat(psheader, tmp->data);
615
616 /* The first page starts, so header ends */
617 inheader = 0;
618 nondsclines = 0;
619 /* Option setting should go into the page
620 specific option set now */
621 optset = optionset("currentpage");
622 }
623 else {
624 /* Restore PostScript state after completing the
625 previous page:
626
627 foomatic-saved-state restore
628 %%Page: ...
629 /foomatic-saved-state save def
630
631 Print this directly, so that if we need to
632 restart the renderer for this page due to
633 a command line change this is done under the
634 old instance of the renderer
635 rint $rendererhandle
636 "foomatic-saved-state restore\n"; */
637
638 /* Save the option settings of the previous page */
639 optionset_copy_values(optionset("currentpage"), optionset("previouspage"));
640 optionset_delete_values(optionset("currentpage"));
641 }
642 /* Initialize the option set */
643 optionset_copy_values(optionset("header"), optionset("currentpage"));
644
645 /* Set the command line options which apply only
646 to given pages */
647 set_options_for_page(optionset("currentpage"), currentpage);
648 pagesetupfound = 0;
649 if (spooler == SPOOLER_CUPS) {
650 /* Remove the "notfirst" flag from all options
651 forseen for the "PageSetup" section, because
652 when these are numerical options for CUPS.
653 they have to be set to the correct value
654 for every page */
655 for (o = optionlist; o; o = o->next) {
656 if (option_get_section(o ) == SECTION_PAGESETUP)
657 o->notfirst = 0;
658 }
659 }
660 /* Now the page header comes, so buffer the data,
661 because we must perhaps shut down and restart
662 the renderer */
663 passthru = 0;
664 ignorepageheader = 0;
665 optionsalsointoheader = 0;
666 }
667 }
668 else if (nestinglevel == 0 && !ignorepageheader &&
669 startswith(line->data, "%%BeginPageSetup")) {
670 /* Start of the page header, up to %%EndPageSetup
671 nothing of the page will be drawn, page-specific
672 option settngs (as letter-head paper for page 1)
673 go here*/
674 _log("\nFound: %%%%BeginPageSetup\n");
675 passthru = 0;
676 inpageheader = 1;
677 postscriptsection = PS_SECTION_PAGESETUP;
678 optionsalsointoheader = (ooo110 && currentpage == 1) ? 1 : 0;
679 /* Insert PostScript option settings
680 (options for section "PageSetup") */
681 if (isdscjob) {
682 append_page_setup_section(line, optset, 0);
683 pagesetupfound = 1;
684 }
685 }
686 else if (nestinglevel == 0 && !ignorepageheader &&
687 startswith(line->data, "%%BeginPageSetup")) {
688 /* End of the page header, the page is ready to be printed */
689 _log("Found: %%%%EndPageSetup\n");
690 _log("End of page header\n");
691 /* We cannot for sure say that the page header ends here
692 OpenOffice.org puts (due to a bug) a "%%BeginSetup...
693 %%EndSetup" section after the first "%%Page:...". It
694 is possible that CUPS inserts a "%%BeginPageSetup...
695 %%EndPageSetup" before this section, which means that
696 the options in the "%%BeginSetup...%%EndSetup"
697 section are after the "%%EndPageSetup", so we
698 continue for searching options up to the buffer size
699 limit $maxlinesforpageoptions. */
700 passthru = 0;
701 inpageheader = 0;
702 optionsalsointoheader = 0;
703 }
704 else if (nestinglevel == 0 && !optionreplaced && (!passthru || !isdscjob) &&
705 ((linetype = line_type(line->data)) &&
706 (linetype == LT_BEGIN_FEATURE || linetype == LT_FOOMATIC_RIP_OPTION_SETTING))) {
707
708 /* parse */
709 if (linetype == LT_BEGIN_FEATURE) {
710 dstrcpy(tmp, line->data);
711 p = strtok(tmp->data, " \t"); /* %%BeginFeature: */
712 p = strtok(NULL, " \t="); /* Option */
713 if (*p == '*') p++;
714 strlcpy(optionname, p, 128);
715 p = strtok(NULL, " \t\r\n"); /* value */
716 fromcomposite = 0;
717 strlcpy(value, p, 128);
718 }
719 else { /* LT_FOOMATIC_RIP_OPTION_SETTING */
720 dstrcpy(tmp, line->data);
721 p = strstr(tmp->data, "FoomaticRIPOptionSetting:");
722 p = strtok(p, " \t"); /* FoomaticRIPOptionSetting */
723 p = strtok(NULL, " \t="); /* Option */
724 strlcpy(optionname, p, 128);
725 p = strtok(NULL, " \t\r\n"); /* value */
726 if (*p == '@') { /* fromcomposite */
727 p++;
728 fromcomposite = 1;
729 }
730 else
731 fromcomposite = 0;
732 strlcpy(value, p, 128);
733 }
734
735 /* Mark that we are in a "Feature" section */
736 if (linetype == LT_BEGIN_FEATURE) {
737 infeature = 1;
738 dstrclear(linesafterlastbeginfeature);
739 }
740
741 /* OK, we have an option. If it's not a
742 Postscript-style option (ie, it's command-line or
743 JCL) then we should note that fact, since the
744 attribute-to-filter option passing in CUPS is kind of
745 funky, especially wrt boolean options. */
746 _log("Found: %s", line->data);
747 if ((o = find_option(optionname)) &&
748 (o->type != TYPE_NONE)) {
749 _log(" Option: %s=%s%s\n", optionname, fromcomposite ? "From" : "", value);
750 if (spooler == SPOOLER_CUPS &&
751 linetype == LT_BEGIN_FEATURE &&
752 !option_get_value(o, optionset("notfirst")) &&
753 strcmp(option_get_value(o, optset) ?: "", value) != 0 &&
754 (inheader || option_get_section(o) == SECTION_PAGESETUP)) {
755
756 /* We have the first occurence of an option
757 setting and the spooler is CUPS, so this
758 setting is inserted by "pstops" or
759 "imagetops". The value from the command
760 line was not inserted by "pstops" or
761 "imagetops" so it seems to be not under
762 the choices in the PPD. Possible
763 reasons:
764
765 - "pstops" and "imagetops" ignore settings
766 of numerical or string options which are
767 not one of the choices in the PPD file,
768 and inserts the default value instead.
769
770 - On the command line an option was applied
771 only to selected pages:
772 "-o <page ranges>:<option>=<values>
773 This is not supported by CUPS, so not
774 taken care of by "pstops".
775
776 We must fix this here by replacing the
777 setting inserted by "pstops" or "imagetops"
778 with the exact setting given on the command
779 line. */
780
781 /* $arg->{$optionset} is already
782 range-checked, so do not check again here
783 Insert DSC comment */
784 pdest = (inheader && isdscjob) ? psheader : psfifo;
785 if (option_is_ps_command(o)) {
786 /* PostScript option, insert the code */
787
788 option_get_command(tmp, o, optset, -1);
789 if (!(val = option_get_value(o, optset)))
790 val = "";
791
792 /* Boolean and enumerated choice options can only be set in the
793 * PageSetup section */
794 if ((inheader && option_is_custom_value(o, val)) || !inheader)
795 {
796 if (o->type == TYPE_BOOL)
797 dstrcatf(pdest, "%%%%BeginFeature: *%s %s\n", o->name,
798 val && !strcmp(val, "1") ? "True" : "False");
799 else
800 dstrcatf(pdest, "%%%%BeginFeature: *%s %s\n", o->name, val);
801
802 dstrcatf(pdest, "%s\n", tmp->data);
803
804 /* We have replaced this option on the FIFO */
805 optionreplaced = 1;
806 }
807 }
808 else { /* Command line or JCL option */
809 val = option_get_value(o, optset);
810
811 if (!inheader || option_is_custom_value(o, val)) {
812 dstrcatf(pdest, "%%%% FoomaticRIPOptionSetting: %s=%s\n",
813 o->name, val ? val : "");
814 optionreplaced = 1;
815 }
816 }
817
818 if (optionreplaced) {
819 val = option_get_value(o, optset);
820 _log(" --> Correcting numerical/string option to %s=%s (Command line argument)\n",
821 o->name, val ? val : "");
822 }
823 }
824
825 /* Mark that we have already found this option */
826 o->notfirst = 1;
827 if (!optionreplaced) {
828 if (o->style != 'G') {
829 /* Controlled by '<Composite>' setting of
830 a member option of a composite option */
831 if (fromcomposite) {
832 dstrcpyf(tmp, "From%s", value);
833 strlcpy(value, tmp->data, 128);
834 }
835
836 /* Non PostScript option
837 Check whether it is valid */
838 if (option_set_value(o, optset, value)) {
839 _log("Setting option\n");
840 strlcpy(value, option_get_value(o, optset), 128);
841 if (optionsalsointoheader)
842 option_set_value(o, optionset("header"), value);
843 if (o->type == TYPE_ENUM &&
844 (!strcmp(o->name, "PageSize") || !strcmp(o->name, "PageRegion")) &&
845 startswith(value, "Custom") &&
846 linetype == LT_FOOMATIC_RIP_OPTION_SETTING) {
847 /* Custom Page size */
848 width = height = 0.0;
849 p = linesafterlastbeginfeature->data;
850 while (*p && isspace(*p)) p++;
851 width = strtod(p, &p);
852 while (*p && isspace(*p)) p++;
853 height = strtod(p, &p);
854 if (width && height) {
855 dstrcpyf(tmp, "%s.%fx%f", value, width, height);
856 strlcpy(value, tmp->data, 128);
857 option_set_value(o, optset, value);
858 if (optionsalsointoheader)
859 option_set_value(o, optionset("header"), value);
860 }
861 }
862 /* For a composite option insert the
863 code from the member options with
864 current setting "From<composite>"
865 The code from the member options
866 is chosen according to the setting
867 of the composite option. */
868 if (option_is_composite(o) && linetype == LT_FOOMATIC_RIP_OPTION_SETTING) {
869 build_commandline(optset, NULL, 0); /* TODO can this be removed? */
870
871 /* TODO merge section and ps_section */
872 if (postscriptsection == PS_SECTION_JCLSETUP)
873 option_get_command(tmp, o, optset, SECTION_JCLSETUP);
874 else if (postscriptsection == PS_SECTION_PROLOG)
875 option_get_command(tmp, o, optset, SECTION_PROLOG);
876 else if (postscriptsection == PS_SECTION_SETUP)
877 option_get_command(tmp, o, optset, SECTION_DOCUMENTSETUP);
878 else if (postscriptsection == PS_SECTION_PAGESETUP)
879 option_get_command(tmp, o, optset, SECTION_PAGESETUP);
880 dstrcat(line, tmp->data);
881 }
882 }
883 else
884 _log(" --> Invalid option setting found in job\n");
885 }
886 else if (fromcomposite) {
887 /* PostScript option, but we have to look up
888 the PostScript code to be inserted from
889 the setting of a composite option, as
890 this option is set to "Controlled by
891 '<Composite>'". */
892 /* Set the option */
893 dstrcpyf(tmp, "From%s", value);
894 strlcpy(value, tmp->data, 128);
895 if (option_set_value(o, optset, value)) {
896 _log(" --> Looking up setting in composite option %s\n", value);
897 if (optionsalsointoheader)
898 option_set_value(o, optionset("header"), value);
899 /* update composite options */
900 build_commandline(optset, NULL, 0);
901 /* Substitute PostScript comment by the real code */
902 /* TODO what exactly is the next line doing? */
903 /* dstrcpy(line, o->compositesubst->data); */
904 }
905 else
906 _log(" --> Invalid option setting found in job\n");
907 }
908 else
909 /* it is a PostScript style option with
910 the code readily inserted, no option
911 for the renderer command line/JCL to set,
912 no lookup of a composite option needed,
913 so nothing to do here... */
914 _log(" --> Option will be set by PostScript interpreter\n");
915 }
916 }
917 else
918 /* This option is unknown to us, WTF? */
919 _log("Unknown option %s=%s found in the job\n", optionname, value);
920 }
921 else if (nestinglevel == 0 && startswith(line->data, "%%EndFeature")) {
922 /* End of feature */
923 infeature = 0;
924 /* If the option setting was replaced, it ends here,
925 too, and the next option is not necessarily also replaced */
926 optionreplaced = 0;
927 dstrclear(linesafterlastbeginfeature);
928 }
929 else if (nestinglevel == 0 && isdscjob && !prologfound &&
930 startswith(line->data, "%%Begin")) {
931 /* In some PostScript files (especially when generated
932 by "dvips" of TeX/LaTeX) the "%%BeginProlog" is
933 missing, so assume that it was before the current
934 line (the first line starting with "%%Begin". */
935 _log("Job claims to be DSC-conforming, but \"%%%%BeginProlog\" "
936 "was missing before first line with another"
937 "\"%%%%BeginProlog\" comment (is this a TeX/LaTeX/dvips-generated"
938 " PostScript file?). Assuming start of \"Prolog\" here.\n");
939 /* Beginning of Prolog */
940 inprolog = 1;
941 nondsclines = 0;
942 /* Insert options for "Prolog" before the current line */
943 dstrcpyf(tmp, "%%%%BeginProlog\n");
944 append_prolog_section(tmp, optset, 0);
945 dstrprepend(line, tmp->data);
946 prologfound = 1;
947 }
948 else if (nestinglevel == 0 && (
949 startswith(line->data, "%RBINumCopies:") ||
950 startswith(line->data, "%%RBINumCopies:"))) {
951 p = strchr(line->data, ':') +1;
952 get_current_job()->rbinumcopies = atoi(p);
953 _log("Found %RBINumCopies: %d\n", get_current_job()->rbinumcopies);
954 }
955 else if (startswith(skip_whitespace(line->data), "%") ||
956 startswith(skip_whitespace(line->data), "$"))
957 /* This is an unknown PostScript comment or a blank
958 line, no active code */
959 ignoreline = 1;
960 }
961 else {
962 /* This line is active PostScript code */
963 if (infeature)
964 /* Collect coe in a "%%BeginFeature: ... %%EndFeature"
965 section, to get the values for a custom option
966 setting */
967 dstrcat(linesafterlastbeginfeature, line->data);
968
969 if (inheader) {
970 if (!inprolog && !insetup) {
971 /* Outside the "Prolog" and "Setup" section
972 a correct DSC-conforming document has no
973 active PostScript code, so consider the
974 file as non-DSC-conforming when there are
975 too many of such lines. */
976 nondsclines++;
977 if (nondsclines > MAX_NON_DSC_LINES_IN_HEADER) {
978 /* Consider document as not DSC-conforming */
979 _log("This job seems not to be DSC-conforming, "
980 "DSC-comment for next section not found, "
981 "stopping to parse the rest, passing it "
982 "directly to the renderer.\n");
983 /* Stop scanning for further option settings */
984 maxlines = 1;
985 isdscjob = 0;
986 /* Insert defaults and command line settings in
987 the beginning of the job or after the last valid
988 section */
989 dstrclear(tmp);
990 if (prologfound)
991 append_prolog_section(tmp, optset, 1);
992 if (setupfound)
993 append_setup_section(tmp, optset, 1);
994 if (pagesetupfound)
995 append_page_setup_section(tmp, optset, 1);
996 dstrinsert(psheader, line_start(psheader->data, insertoptions), tmp->data);
997
998 prologfound = 1;
999 setupfound = 1;
1000 pagesetupfound = 1;
1001 }
1002 }
1003 }
1004 else if (!inpageheader) {
1005 /* PostScript code inside a page, but not between
1006 "%%BeginPageSetup" and "%%EndPageSetup", so
1007 we are perhaps already drawing onto a page now */
1008 if (startswith(onelinebefore->data, "%%Page"))
1009 _log("No page header or page header not DSC-conforming\n");
1010 /* Stop buffering lines to search for options
1011 placed not DSC-conforming */
1012 if (line_count(psfifo->data) >= MAX_LINES_FOR_PAGE_OPTIONS) {
1013 _log("Stopping search for page header options\n");
1014 passthru = 1;
1015 /* If there comes a page header now, ignore it */
1016 ignorepageheader = 1;
1017 optionsalsointoheader = 0;
1018 }
1019 /* Insert PostScript option settings (options for the
1020 * section "PageSetup" */
1021 if (isdscjob && !pagesetupfound) {
1022 append_page_setup_section(psfifo, optset, 1);
1023 pagesetupfound = 1;
1024 }
1025 }
1026 }
1027 }
1028
1029 /* Debug Info */
1030 if (lastpassthru != passthru) {
1031 if (passthru)
1032 _log("Found: %s --> Output goes directly to the renderer now.\n\n", line->data);
1033 else
1034 _log("Found: %s --> Output goes to the FIFO buffer now.\n\n", line->data);
1035 }
1036
1037 /* We are in an option which was replaced, do not output the current line */
1038 if (optionreplaced)
1039 dstrclear(line);
1040
1041 /* If we are in a "%%BeginSetup...%%EndSetup" section after
1042 the first "%%Page:..." and the current line belongs to
1043 an option setting, we have to copy the line also to the
1044 @psheader. */
1045 if (optionsalsointoheader && (infeature || startswith(line->data, "%%EndFeature")))
1046 dstrcat(psheader, line->data);
1047
1048 /* Store or send the current line */
1049 if (inheader && isdscjob) {
1050 /* We are still in the PostScript header, collect all lines
1051 in @psheader */
1052 dstrcat(psheader, line->data);
1053 }
1054 else {
1055 if (passthru && isdscjob) {
1056 if (!lastpassthru) {
1057 /*
1058 * We enter passthru mode with this line, so the
1059 * command line can have changed, check it and close
1060 * the renderer if needed
1061 */
1062 if (rendererpid && !optionset_equal(optionset("currentpage"), optionset("previouspage"), 0)) {
1063 _log("Command line/JCL options changed, restarting renderer\n");
1064 retval = close_renderer_handle(rendererhandle, rendererpid);
1065 if (retval != EXIT_PRINTED)
1066 rip_die(retval, "Error closing renderer\n");
1067 rendererpid = 0;
1068 }
1069 }
1070
1071 /* Flush psfifo and send line directly to the renderer */
1072 if (!rendererpid) {
1073 /* No renderer running, start it */
1074 dstrcpy(tmp, psheader->data);
1075 dstrcat(tmp, psfifo->data);
1076 get_renderer_handle(tmp, &rendererhandle, &rendererpid);
1077 /* psfifo is sent out, flush it */
1078 dstrclear(psfifo);
1079 }
1080
1081 if (!isempty(psfifo->data)) {
1082 /* Send psfifo to renderer */
1083 fwrite(psfifo->data, psfifo->len, 1, rendererhandle);
1084 /* flush psfifo */
1085 dstrclear(psfifo);
1086 }
1087
1088 /* Send line to renderer */
1089 if (!printprevpage) {
1090 fwrite(line->data, line->len, 1, rendererhandle);
1091
1092 while (stream_next_line(line, stream) > 0) {
1093 if (startswith(line->data, "%%")) {
1094 _log("Found: %s", line->data);
1095 _log(" --> Continue DSC parsing now.\n\n");
1096 saved = 1;
1097 break;
1098 }
1099 else {
1100 fwrite(line->data, line->len, 1, rendererhandle);
1101 linect++;
1102 }
1103 }
1104 }
1105 }
1106 else {
1107 /* Push the line onto the stack to split up later */
1108 dstrcat(psfifo, line->data);
1109 }
1110 }
1111
1112 if (!printprevpage)
1113 linect++;
1114 }
1115 else {
1116 /* EOF! */
1117 more_stuff = 0;
1118
1119 /* No PostScript header in the whole file? Then it's not
1120 PostScript, convert it.
1121 We open the file converter here when the file has less
1122 lines than the amount which we search for the PostScript
1123 header ($maxlinestopsstart). */
1124 if (linect <= nonpslines) {
1125 /* This is not a PostScript job, we must convert it */
1126 _log("\nJob does not start with \"%%!\", is it PostScript?\n"
1127 "Starting file converter\n");
1128
1129 /* Reset all variables but conserve the data which
1130 we already have read */
1131 jobhasjcl = 0;
1132 linect = 0;
1133 maxlines = 1000;
1134 dstrclear(onelinebefore);
1135 dstrclear(twolinesbefore);
1136 dstrclear(line);
1137
1138 dstrcpy(tmp, psheader->data);
1139 dstrcat(tmp, psfifo->data);
1140 dstrclear(psfifo);
1141 dstrclear(psheader);
1142
1143 /* Start the file conversion filter */
1144 if (!fileconverter_pid)
1145 get_fileconverter_handle(tmp->data, &fileconverter_handle, &fileconverter_pid);
1146 else
1147 rip_die(EXIT_JOBERR, "File conversion filter probably crashed\n");
1148
1149 /* Read the further data from the file converter and
1150 not from STDIN */
1151 if (close(fileno(stdin)) != 0)
1152 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't close STDIN\n");
1153
1154 if (dup2(fileno(fileconverter_handle), fileno(stdin)) == -1)
1155 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Couldn't dup fileconverterhandle\n");
1156
1157 /* Now we have new (converted) stuff in STDIN, so
1158 continue in the loop */
1159 more_stuff = 1;
1160 }
1161 }
1162
1163 lastpassthru = passthru;
1164
1165 if (!ignoreline && !printprevpage) {
1166 dstrcpy(twolinesbefore, onelinebefore->data);
1167 dstrcpy(onelinebefore, line->data);
1168 }
1169
1170 } while ((maxlines == 0 || linect < maxlines) && more_stuff != 0);
1171
1172 /* Some buffer still containing data? Send it out to the renderer */
1173 if (more_stuff || inheader || !isempty(psfifo->data)) {
1174 /* Flush psfifo and send the remaining data to the renderer, this
1175 only happens with non-DSC-conforming jobs or non-Foomatic PPDs */
1176 if (more_stuff)
1177 _log("Stopped parsing the PostScript data, "
1178 "sending rest directly to the renderer.\n");
1179 else
1180 _log("Flushing FIFO.\n");
1181
1182 if (inheader) {
1183 build_commandline(optset, NULL, 0);
1184 /* No page initialized yet? Copy the "header" option set into the
1185 "currentpage" option set, so that the renderer will find the
1186 options settings. */
1187 optionset_copy_values(optionset("header"), optionset("currentpage"));
1188 optset = optionset("currentpage");
1189
1190 /* If not done yet, insert defaults and command line settings
1191 in the beginning of the job or after the last valid section */
1192 dstrclear(tmp);
1193 if (prologfound)
1194 append_prolog_section(tmp, optset, 1);
1195 if (setupfound)
1196 append_setup_section(tmp, optset, 1);
1197 if (pagesetupfound)
1198 append_page_setup_section(tmp, optset, 1);
1199 dstrinsert(psheader, line_start(psheader->data, insertoptions), tmp->data);
1200
1201 prologfound = 1;
1202 setupfound = 1;
1203 pagesetupfound = 1;
1204 }
1205
1206 if (rendererpid > 0 && !optionset_equal(optionset("currentpage"), optionset("previouspage"), 0)) {
1207 _log("Command line/JCL options changed, restarting renderer\n");
1208 retval = close_renderer_handle(rendererhandle, rendererpid);
1209 if (retval != EXIT_PRINTED)
1210 rip_die(retval, "Error closing renderer\n");
1211 rendererpid = 0;
1212 }
1213
1214 if (!rendererpid) {
1215 dstrcpy(tmp, psheader->data);
1216 dstrcat(tmp, psfifo->data);
1217 get_renderer_handle(tmp, &rendererhandle, &rendererpid);
1218 /* We have sent psfifo now */
1219 dstrclear(psfifo);
1220 }
1221
1222 if (psfifo->len) {
1223 /* Send psfifo to the renderer */
1224 fwrite(psfifo->data, psfifo->len, 1, rendererhandle);
1225 dstrclear(psfifo);
1226 }
1227
1228 /* Print the rest of the input data */
1229 if (more_stuff) {
1230 while (stream_next_line(tmp, stream))
1231 fwrite(tmp->data, tmp->len, 1, rendererhandle);
1232 }
1233 }
1234
1235 /* At every "%%Page:..." comment we have saved the PostScript state
1236 and we have increased the page number. So if the page number is
1237 non-zero we had at least one "%%Page:..." comment and so we have
1238 to give a restore the PostScript state.
1239 if ($currentpage > 0) {
1240 print $rendererhandle "foomatic-saved-state restore\n";
1241 } */
1242
1243 /* Close the renderer */
1244 if (rendererpid) {
1245 retval = close_renderer_handle(rendererhandle, rendererpid);
1246 if (retval != EXIT_PRINTED)
1247 rip_die(retval, "Error closing renderer\n");
1248 rendererpid = 0;
1249 }
1250
1251 /* Close the file converter (if it was used) */
1252 if (fileconverter_pid) {
1253 retval = close_fileconverter_handle(fileconverter_handle, fileconverter_pid);
1254 if (retval != EXIT_PRINTED)
1255 rip_die(retval, "Error closing file converter\n");
1256 fileconverter_pid = 0;
1257 }
1258
1259 free_dstr(line);
1260 free_dstr(onelinebefore);
1261 free_dstr(twolinesbefore);
1262 free_dstr(psheader);
1263 free_dstr(psfifo);
1264 free_dstr(tmp);
1265 }
1266
1267 /*
1268 * Run the renderer command line (and if defined also the postpipe) and returns
1269 * a file handle for stuffing in the PostScript data.
1270 */
1271 void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid)
1272 {
1273 pid_t kid3;
1274 FILE *kid3in;
1275 dstr_t *cmdline = create_dstr();
1276
1277 /* Build the command line and get the JCL commands */
1278 build_commandline(optionset("currentpage"), cmdline, 0);
1279 massage_gs_commandline(cmdline);
1280
1281 _log("\nStarting renderer with command: \"%s\"\n", cmdline->data);
1282 kid3 = start_process("kid3", exec_kid3, (void *)cmdline->data, &kid3in, NULL);
1283 if (kid3 < 0)
1284 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Cannot fork for kid3\n");
1285
1286 /* Feed the PostScript header and the FIFO contents */
1287 if (prepend)
1288 fwrite(prepend->data, prepend->len, 1, kid3in);
1289
1290 /* We are the parent, return glob to the file handle */
1291 *fd = kid3in;
1292 *pid = kid3;
1293
1294 free_dstr(cmdline);
1295 }
1296
1297 /* Close the renderer process and wait until all kid processes finish */
1298 int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid)
1299 {
1300 int status;
1301
1302 _log("\nClosing renderer\n");
1303 fclose(rendererhandle);
1304
1305 status = wait_for_process(rendererpid);
1306 if (WIFEXITED(status))
1307 return WEXITSTATUS(status);
1308 else
1309 return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
1310 }
1311