#include "foomaticrip.h"
#include "util.h"
#include "options.h"
#include "fileconverter.h"
#include "renderer.h"
#include "process.h"
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
void get_renderer_handle(const dstr_t *prepend, FILE **fd, pid_t *pid);
int close_renderer_handle(FILE *rendererhandle, pid_t rendererpid);
#define LT_BEGIN_FEATURE 1
#define LT_FOOMATIC_RIP_OPTION_SETTING 2
int line_type(const char *line)
{
const char *p;
if (startswith(line, "%%BeginFeature:"))
return LT_BEGIN_FEATURE;
p = line;
while (*p && isspace(*p)) p++;
if (!startswith(p, "%%"))
return 0;
p += 2;
while (*p && isspace(*p)) p++;
if (startswith(p, "FoomaticRIPOptionSetting:"))
return LT_FOOMATIC_RIP_OPTION_SETTING;
return 0;
}
/* Next, examine the PostScript job for traces of command-line and
JCL options. PPD-aware applications and spoolers stuff option
settings directly into the file, they do not necessarily send
PPD options by the command line. Also stuff in PostScript code
to apply option settings given by the command line and to set
the defaults given in the PPD file.
Examination strategy: read lines from STDIN until the first
%%Page: comment appears and save them as @psheader. This is the
page-independent header part of the PostScript file. The
PostScript interpreter (renderer) must execute this part once
before rendering any assortment of pages. Then pages can be
printed in any arbitrary selection or order. All option
settings we find here will be collected in the default option
set for the RIP command line.
Now the pages will be read and sent to the renderer, one after
the other. Every page is read into memory until the
%%EndPageSetup comment appears (or a certain amount of lines was
read). So we can get option settings only valid for this
page. If we have such settings we set them in the modified
command set for this page.
If the renderer is not running yet (first page) we start it with
the command line built from the current modified command set and
send the first page to it, in the end we leave the renderer
running and keep input and output pipes open, so that it can
accept further pages. If the renderer is still running from
the previous page and the current modified command set is the
same as the one for the previous page, we send the page. If
the command set is different, we close the renderer, re-start
it with the command line built from the new modified command
set, send the header again, and then the page.
After the last page the trailer (%%Trailer) is sent.
The output pipe of this program stays open all the time so that
the spooler does not assume that the job has finished when the
renderer is re-started.
Non DSC-conforming documents will be read until a certain line
number is reached. Command line or JCL options inserted later
will be ignored.
If options are implemented by PostScript code supposed to be
stuffed into the job's PostScript data we stuff the code for all
these options into our job data, So all default settings made in
the PPD file (the user can have edited the PPD file to change
them) are taken care of and command line options get also
applied. To give priority to settings made by applications we
insert the options's code in the beginnings of their respective
sections, so that sommething, which is already inserted, gets
executed after our code. Missing sections are automatically
created. In non-DSC-conforming files we insert the option code
in the beginning of the file. This is the same policy as used by
the "pstops" filter of CUPS.
If CUPS is the spooler, the option settings were already
inserted by the "pstops" filter, so we don't insert them
again. The only thing we do is correcting settings of numerical
options when they were set to a value not available as choice in
the PPD file, As "pstops" does not support "real" numerical
options, it sees these settings as an invalid choice and stays
with the default setting. In this case we correct the setting in
the first occurence of the option's code, as this one is the one
added by CUPS, later occurences come from applications and
should not be touched.
If the input is not PostScript (if there is no "%!" after
$maxlinestopsstart lines) a file conversion filter will
automatically be applied to the incoming data, so that we will
process the resulting PostScript here. This way we have always
PostScript data here and so we can apply the printer/driver
features described in the PPD file.
Supported file conversion filters are "a2ps", "enscript",
"mpage", and spooler-specific filters. All filters convert
plain text to PostScript, "a2ps" also other formats. The
conversion filter is always used when one prints the
documentation pages, as they are created as plain text,
when CUPS is the spooler "pstops" is executed after the
filter so that the default option settings from the PPD file
and CUPS-specific options as N-up get applied. On regular
printouts one gets always PostScript when CUPS or PPR is
the spooler, so the filter is only used for regular
printouts under LPD, LPRng, GNUlpr or without spooler.
*/
/* PostScript sections */
#define PS_SECTION_JCLSETUP 1
#define PS_SECTION_PROLOG 2
#define PS_SECTION_SETUP 3
#define PS_SECTION_PAGESETUP 4
#define MAX_NON_DSC_LINES_IN_HEADER 1000
#define MAX_LINES_FOR_PAGE_OPTIONS 200
typedef struct {
size_t pos;
FILE *file;
const char *alreadyread;
size_t len;
} stream_t;
void _print_ps(stream_t *stream);
int stream_next_line(dstr_t *line, stream_t *s)
{
int c;
size_t cnt = 0;
dstrclear(line);
while (s->pos < s->len) {
c = s->alreadyread[s->pos++];
dstrputc(line, c);
cnt++;
if (c == '\n')
return cnt;
}
while ((c = fgetc(s->file)) != EOF) {
dstrputc(line, c);
cnt++;
if (c == '\n')
return cnt;
}
return cnt;
}
int print_ps(FILE *file, const char *alreadyread, size_t len, const char *filename)
{
stream_t stream;
if (file != stdin && (dup2(fileno(file), fileno(stdin)) < 0)) {
_log("Could not dup %s to stdin.\n", filename);
return 0;
}
stream.pos = 0;
stream.file = stdin;
stream.alreadyread = alreadyread;
stream.len = len;
_print_ps(&stream);
return 1;
}
void _print_ps(stream_t *stream)
{
char *p;
int maxlines = 1000; /* Maximum number of lines to be read when the
documenent is not DSC-conforming.
"$maxlines = 0" means that all will be read and
examined. If it is discovered that the input
file is DSC-conforming, this will be set to 0. */
int maxlinestopsstart = 200; /* That many lines are allowed until the
"%!" indicating PS comes. These
additional lines in the
beginning are usually JCL
commands. The lines will be
ignored by our parsing but
passed through. */
int printprevpage = 0; /* We set this when encountering "%%Page:" and the
previous page is not printed yet. Then it will
be printed and the new page will be prepared in
the next run of the loop (we don't read a new
line and don't increase the $linect then). */
int linect = 0; /* how many lines have we examined */