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