summaryrefslogtreecommitdiff
path: root/foomatic-rip.in
diff options
context:
space:
mode:
authorDidier Raboud <didier@raboud.com>2010-05-23 00:07:10 +0200
committerDidier Raboud <didier@raboud.com>2010-05-23 00:07:10 +0200
commitf56211f0ff4ddf9371ebc4c31e8f95df1fc81a5c (patch)
treea24f15ea2fc96e0688e96c58e35dde7adeaef3fb /foomatic-rip.in
parentb834bd56528993fa05a5645da808227aee3e9de7 (diff)
parent014f0e14a3c6a044d99a67c8f4e1c4065452479e (diff)
Merge branch 'upstream'
Conflicts: debian/README.Debian debian/changelog debian/control debian/copyright debian/foomatic-filters.config debian/foomatic-filters.postinst debian/foomatic-filters.postrm debian/foomatic-filters.templates debian/parseconfig.pl debian/po/de.po debian/po/fr.po debian/po/ja.po debian/po/nl.po debian/po/pt_BR.po debian/po/templates.pot debian/po/tr.po debian/rules
Diffstat (limited to 'foomatic-rip.in')
-rw-r--r--foomatic-rip.in6617
1 files changed, 0 insertions, 6617 deletions
diff --git a/foomatic-rip.in b/foomatic-rip.in
deleted file mode 100644
index 18b06a0..0000000
--- a/foomatic-rip.in
+++ /dev/null
@@ -1,6617 +0,0 @@
-#!@PERL@
-# The above Perl path may vary on your system; fix it!!! -*- perl -*-
-
-use strict;
-use POSIX;
-use Cwd;
-
-my $ripversion='$Revision$';
-#'# Fix emacs syntax highlighting
-
-# foomatic-rip is a spooler-independent filter script which takes
-# PostScript as standard input and generates the printer's page
-# description language (PDL)/raster format as standard output. This
-# kind of filter is usually called Raster Image Processor (RIP),
-# therefore the name "foomatic-rip".
-
-# Save it in one of the directories of your $PATH, so that it gets
-# found when called from the command line (for spooler-less printing),
-# link it to spooler-specific directories when you use CUPS or PPR:
-
-# ln -s /usr/bin/foomatic-rip /usr/lib/cups/filter/
-# ln -s /usr/bin/foomatic-rip /usr/lib/ppr/lib/
-# ln -s /usr/bin/foomatic-rip /usr/lib/ppr/interfaces/
-
-# Mark this filter world-readable and world-executable (note that most
-# spoolers run the print filters as a special user, as "lp", not as
-# "root" or as the user who sent the job).
-
-# See http://www.openprinting.org/cups-doc.html
-# http://www.openprinting.org/lpd-doc.html
-# http://www.openprinting.org/ppr-doc.html
-# http://www.openprinting.org/pdq-doc.html
-# http://www.openprinting.org/direct-doc.html
-# http://www.openprinting.org/ppd-doc.html
-
-# ==========================================================================
-#
-# User-configurable settings, edit them if needed
-#
-# ==========================================================================
-
-# What path to use for filter programs and such. Your printer driver
-# must be in the path, as must be the renderer, $enscriptcommand, and
-# possibly other stuff. The default path is often fine on Linux, but
-# may not be on other systems.
-#
-my $execpath = "@EXECPATH@";
-
-# CUPS raster drivers are searched here
-my $cupsfilterpath = "@prefix@/lib/cups/filter:/usr/local/lib/cups/filter:/usr/local/libexec/cups/filter:/opt/cups/filter:/usr/lib/cups/filter";
-
-# Location of the configuration file "filter.conf", this file can be
-# used to change the settings of foomatic-rip without editing
-# foomatic-rip. itself. This variable must contain the full pathname
-# of the directory which contains the configuration file, usually
-# "/etc/foomatic".
-# Some versions of configure do not fully expand $sysconfdir
-my $prefix = "@prefix@";
-my $configpath = "@sysconfdir@/foomatic";
-
-# For the stuff below, the settings in the configuration file have priority.
-
-# Set to 1 to insert postscript code for page accounting (CUPS only).
-my $ps_accounting = 1;
-my $accounting_prolog = "";
-
-# Enter here your personal command for converting non-postscript files
-# (especially text) to PostScript. If you leave it blank, at first the
-# line "textfilter: ..." from /etc/foomatic/filter.conf is read and
-# then the commands given on the list below are tried, beginning with
-# the first one.
-# You can set this to "a2ps", "enscript" or "mpage" to select one of the
-# default command strings.
-my $fileconverter = '@FILECONVERTER@';
-
-my($kid0,$kid1,$kid2,$kid3,$kid4);
-my($kidfailed,$kid3finished,$kid4finished);
-my($convkidfailed,$dockidfailed,$kid0finished,$kid1finished,$kid2finished);
-my($fileconverterpid,$rendererpid,$fileconverterhandle,$rendererhandle);
-my($jobhasjcl);
-
-# What 'echo' program to use. It needs -e and -n. Linux's builtin
-# and regular echo work fine; non-GNU platforms may need to install
-# gnu echo and put gecho here or something.
-#
-my $myecho = '@ECHO@';
-
-# Which shell to use for executing shell commands. Some of the PPD files
-# specify a FoomaticRIPCommandLine that makes use of constructs not available
-# from a vanilla Bourne shell. On systems where /bin/sh is a vanilla Bourne
-# we need to use a more "modern" shell to execute the command. This will
-# be set via a 'preferred_shell: (shell)' setting in the foomatic.conf file
-# or automatically detected at runtime later on in this program.
-#
-my $modern_shell = '';
-
-# Set debug to 1 to enable the debug logfile for this filter; it will
-# appear as defined by $logfile. It will contain status from this
-# filter, plus the renderer's stderr output. You can also add a line
-# "debug: 1" to your /etc/foomatic/filter.conf to get all your
-# Foomatic filters into debug mode.
-#
-# WARNING: This logfile is a security hole; do not use in production.
-my $debug = 0;
-
-# This is the location of the debug logfile (and also the copy of the
-# processed PostScript data) in case you have enabled debugging above.
-# The logfile will get the extension ".log", the PostScript data ".ps".
-my $logfile = "/tmp/foomatic-rip";
-
-# End interesting enduser options
-
-# ==========================================================================
-#
-# foomatic-rip spooler-independent PS->Printer filter (RIP) of Foomatic
-#
-# Copyright 2002 - 2007 Grant Taylor <gtaylor@picante.com>
-# & Till Kamppeter <till.kamppeter@gmail.com>
-# & Helge Blischke <h.blischke@srz.de>
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; either version 2 of the License, or (at your
-# option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-# USA.
-#
-
-my $added_lf = "\n";
-
-# Flush everything immediately.
-$|=1;
-
-
-
-## Constants used by this filter
-
-# Error codes, as some spooles behave different depending on the reason why
-# the RIP failed, we return an error code. As I have only found a table of
-# error codes for the PPR spooler. If our spooler is really PPR, these
-# definitions get overwritten by the ones of the PPR version currently in
-# use.
-
-my $EXIT_PRINTED = 0; # file was printed normally
-my $EXIT_PRNERR = 1; # printer error occured
-my $EXIT_PRNERR_NORETRY = 2; # printer error with no hope of retry
-my $EXIT_JOBERR = 3; # job is defective
-my $EXIT_SIGNAL = 4; # terminated after catching signal
-my $EXIT_ENGAGED = 5; # printer is otherwise engaged (connection
- # refused)
-my $EXIT_STARVED = 6; # starved for system resources
-my $EXIT_PRNERR_NORETRY_ACCESS_DENIED = 7; # bad password? bad port
- # permissions?
-my $EXIT_PRNERR_NOT_RESPONDING = 8; # just doesn't answer at all
- # (turned off?)
-my $EXIT_PRNERR_NORETRY_BAD_SETTINGS = 9; # interface settings are invalid
-my $EXIT_PRNERR_NO_SUCH_ADDRESS = 10; # address lookup failed, may be
- # transient
-my $EXIT_PRNERR_NORETRY_NO_SUCH_ADDRESS = 11; # address lookup failed, not
- # transient
-my $EXIT_INCAPABLE = 50; # printer wants (lacks) features
- # or resources
-# Standard Unix signal names
-#my SIGHUP = 1;
-#my SIGINT = 2;
-#my SIGQUIT = 3;
-#my SIGKILL = 9;
-#my SIGTERM = 15;
-#my SIGUSR1 = 10;
-#my SIGUSR2 = 12;
-#my SIGTTIN = 21;
-#my SIGTTOU = 22;
-
-my $ESPIPE = 29; # the errno value when seeking a pipe or socket
-
-
-
-## Some important variables
-
-# We don't know yet, which spooler will be used. If we don't detect
-# one. we assume that we do spooler-less printing. Supported spoolers
-# are currently:
-
-# cups - CUPS - Common Unix Printing System
-# solaris - Solaris LP (possibly some other SysV LP services as well)
-# lpd - LPD - Line Printer Daemon
-# lprng - LPRng - LPR - New Generation
-# gnulpr - GNUlpr, an enhanced LPD (development stopped)
-# ppr - PPR (foomatic-rip runs as a PPR RIP)
-# ppr_int - PPR (foomatic-rip runs as an interface)
-# cps - CPS - Coherent Printing System
-# pdq - PDQ - Print, Don't Queue (development stopped)
-# direct - Direct, spooler-less printing
-
-my $spooler = 'direct';
-
-# PPD file name
-my $ppdfile = "";
-
-# Printer model
-my $model = "";
-
-# Printer queue name
-my $printer = "";
-
-# Printing options
-my $optstr = "";
-
-# Job ID
-my $jobid = "";
-
-# User who sent job
-my $jobuser = ((getpwuid($<))[0] || `whoami` || "");
-chomp $jobuser;
-
-# Host from which job was sent
-my $jobhost = `hostname`;
-chomp $jobhost;
-
-# Job title
-my $jobtitle = "$jobuser\@$jobhost";
-
-# Number of copies
-my $copies = "1";
-
-# Post pipe (command into which the output of this filter should be piped)
-my $postpipe = "";
-
-# job meta-data file path (for Solaris LP)
-my $attrpath = '';
-
-# Files to be printed
-my @filelist = ();
-
-# Where to send debugging log output. Initialized to STDERR until the command
-# line arguments are parsed.
-my $logh = *STDERR;
-
-# JCL prefix to put before the JCL options (Can be modified by a
-# "*JCLBegin:" keyword in the PPD file):
-my $jclbegin = "\033%-12345X\@PJL\n";
-
-# JCL command to switch the printer to the PostScript interpreter (Can
-# be modified by a "*JCLToPSInterpreter:" keyword in the PPD file):
-my $jcltointerpreter = "";
-
-# JCL command to close a print job (Can be modified by a "*JCLEnd:"
-# keyword in the PPD file):
-my $jclend = "\033%-12345X\@PJL RESET\n";
-
-# Prefix for starting every JCL command (Can be modified by
-# "*FoomaticJCLPrefix:" keyword in the PPD file):
-my $jclprefix = "\@PJL ";
-
-# Under which name were we called and in which directory do we reside
-$0 =~ m!^(.*/)([^/]+)$!;
-my $programdir = $1;
-my $programname = $2;
-
-# Filters to convert non-PostScript files
-my @fileconverters =
- (# a2ps (converts also other files than text)
- 'a2ps -1 @@--medium=@@PAGESIZE@@ @@--center-title=@@JOBTITLE@@ -o -',
- # enscript
- 'enscript -G @@-M @@PAGESIZE@@ @@-b "Page $%|@@JOBTITLE@@ ' .
- '--margins=36:36:36:36 --mark-wrapped-lines=arrow --word-wrap -p-',
- # mpage
- 'mpage -o -1 @@-b @@PAGESIZE@@ @@-H -h @@JOBTITLE@@ -m36l36b36t36r ' .
- '-f -P- -');
-
-# spooler-specific file converters, default for the specific spooler when
-# none of the converters above is chosen. Remove weird characters from the
-# command line arguments to enhance security
-my @fixed_args =
- (defined($ARGV[0])?removespecialchars($ARGV[0]):"",
- defined($ARGV[1])?removespecialchars($ARGV[1]):"",
- defined($ARGV[2])?removespecialchars($ARGV[2]):"",
- defined($ARGV[3])?removespecialchars($ARGV[3]):"",
- defined($ARGV[4])?removespecialchars($ARGV[4]):"");
-my $spoolerfileconverters = {
- 'cups' => "${programdir}texttops '$fixed_args[0]' '$fixed_args[1]' '$fixed_args[2]' " .
- "'$fixed_args[3]' '$fixed_args[4] page-top=36 page-bottom=36 " .
- "page-left=36 page-right=36 nolandscape cpi=12 lpi=7 " .
- "columns=1 wrap'"
- };
-
-## Config file
-
-# Read config file if present
-my %conf = readConfFile("$configpath/filter.conf");
-
-# Get execution path from config file
-$execpath = $conf{execpath} if defined $conf{execpath};
-$ENV{'PATH'} = $execpath;
-
-# Get CUPS filter path from config file
-$cupsfilterpath = $conf{cupsfilterpath} if defined $conf{cupsfilterpath};
-
-# Set debug mode
-$debug = $conf{debug} if defined $conf{debug};
-
-# Determine which filter to use for non-PostScript files to be converted
-# to PostScript
-if (defined $conf{textfilter}) {
- $fileconverter = $conf{textfilter};
- $fileconverter eq 'a2ps' and $fileconverter = $fileconverters[0];
- $fileconverter eq 'enscript' and $fileconverter = $fileconverters[1];
- $fileconverter eq 'mpage' and $fileconverter = $fileconverters[2];
-}
-
-# Set the preferred shell for "system()" execution
-(defined $conf{preferred_shell}) &&
- ($modern_shell = $conf{preferred_shell});
-# if none was preferred, look for a shell that will work
-foreach my $shell ('/bin/sh', '/bin/bash', '/bin/ksh', '/bin/zsh') {
- if (($modern_shell eq '') && (-x $shell)) {
- open(FD, "| ".$shell." -c \"((0<1))\" 2>/dev/null");
- (close(FD) == 1) && ($modern_shell = $shell);
- }
-}
-
-## Environment variables;
-
-# "PPD": PPD file name for CUPS, Solaris, or PPR (if we run as PPR RIP)
-if (defined($ENV{'PPD'})) {
- # Clean the file name from weird characters which could cause
- # unexpected behaviour
- $ppdfile = removespecialchars($ENV{'PPD'});
- # CUPS, Solaris LP, and PPR (RIP filter) use the "PPD" environment variable
- # to make the PPD file name available (we set CUPS here preliminarily,
- # in the next step we check for Solaris LP and the PPR)
- $spooler = 'cups';
-}
-
-# "SPOOLER_KEY": Solaris LP print service
-if (defined($ENV{'SPOOLER_KEY'})) {
- $spooler = 'solaris';
-
- $ppdfile = $ENV{'PPD'};
- # set the printer name from the PPD file name
- ($ppdfile =~ m!^.*/([^/]+)\.ppd$!) &&
- ($printer = $1);
-
- # Solaris LP may augment the "options" string argument from the command
- # line with an attributes file ($ATTRPATH)
- (defined($attrpath = $ENV{'ATTRPATH'})) &&
- ($optstr = read_attribute_file($attrpath));
-}
-
-# "PPR_VERSION": PPR
-if (defined($ENV{'PPR_VERSION'})) {
- # We have PPR
- $spooler = 'ppr';
-}
-
-# "PPR_RIPOPTS": PPR
-if (defined($ENV{'PPR_RIPOPTS'})) {
- # PPR 1.5 allows the user to specify options for the PPR RIP with the
- # "--ripopts" option on the "ppr" command line. They are provided to
- # the RIP via the "PPR_RIPOPTS" environment variable.
- # Clean the option string from weird characters which could cause
- # unexpected behaviour
- $optstr .= removespecialchars("$ENV{'PPR_RIPOPTS'} ");
- # We have PPR
- $spooler = 'ppr';
-}
-
-# "LPOPTS": Option settings for some LPD implementations (ex: GNUlpr)
-if (defined($ENV{'LPOPTS'})) {
- my @lpopts = split(/,/, removespecialchars($ENV{'LPOPTS'}));
- foreach my $opt (@lpopts) {
- $opt =~ s/^\s+//;
- $opt =~ s/\s+$//;
- if ($opt =~ /\s+/) {
- $opt = "\"$opt\"";
- }
- $optstr .= "$opt ";
- }
- # We have an LPD which accepts "-o" for options
- $spooler = 'gnulpr';
-}
-
-
-
-## Named command line options
-
-# We do not use Getopt::Long because it does not work when between the
-# option and the argument is no space ("-w80" instead of "-w 80"). This
-# happens in the command line of LPRng, but also users could type in
-# options this way when printing without spooler.
-
-# Make one option string with a non-printable character as separator,
-# So we can parse it more easily.
-
-# To avoid the separator to be in the options itselves, it is filters
-# out of the options. This does not break anything as having non
-# printable characters in the command line options does not make sense
-# nor is this needed. This way misinterpretation and even abuse is
-# prevented.
-
-my $argstr = "\x01" .
- join("\x01", map { removeunprintables($_) } @ARGV) . "\x01";
-
-# Debug mode activated via command line
-if ($argstr =~ s/\x01--debug\x01/\x01/) {
- $debug = 1;
-}
-
-# Command line options for verbosity
-my $verbose = ($argstr =~ s/\x01-v\x01/\x01/);
-my $quiet = ($argstr =~ s/\x01-q\x01/\x01/);
-my $show_docs = ($argstr =~ s/\x01-d\x01/\x01/);
-my $do_docs;
-my $cupscolorprofile;
-
-if ($debug) {
- # Grotesquely unsecure; use for debugging only
- open LOG, "> ${logfile}.log";
- $logh = *LOG;
-
- use IO::Handle;
- $logh->autoflush(1);
-} elsif (($quiet) && (!$verbose)) {
- # Quiet mode, do not log
- open LOG, "> /dev/null";
- $logh = *LOG;
-
- use IO::Handle;
- $logh->autoflush(1);
-} else {
- # Default: log to STDERR
- $logh=*STDERR;
-}
-
-
-
-## Start debug logging
-if ($debug) {
- # If we are not in debug mode, we do this later, as we must find out at
- # first which spooler is used. When printing without spooler we
- # suppress logging because foomatic-rip is called directly on the
- # command line and so we avoid logging onto the console.
- print $logh "foomatic-rip version $ripversion running...\n";
- # Print the command line only in debug mode, Mac OS X adds very many
- # options so that CUPS cannot handle the output of the command line
- # in its log files. If CUPS encounters a line with more than 1024
- # characters sent into its log files, it aborts the job with an error.
- if (($debug) || ($spooler ne 'cups')) {
- print $logh "called with arguments: '", join("', '",@ARGV), "'\n";
- }
-}
-
-
-
-## Continue with named options
-
-# Check for LPRng first so we do not pick up bogus ppd files by the -p option
-if ($argstr =~ s/\x01--lprng\x01/\x01/) {
- # We have LPRng
- $spooler = 'lprng';
-}
-# 'PRINTCAP_ENTRY' environment variable is : LPRng
-# the :ppd=/path/to/ppdfile printcap entry should be used
-if (defined($ENV{'PRINTCAP_ENTRY'})){
- $spooler = 'lprng';
- my( @pc);
- @pc = split( /\s*:\s*/, $ENV{'PRINTCAP_ENTRY'} );
- shift @pc;
- foreach (@pc) {
- if( /^ppd=(.*)$/ or /^ppdfile=(.*)$/ ){
- $ppdfile = removespecialchars($1) if $1;
- }
- }
-} elsif ($argstr =~ s/\x01--lprng\x01/\x01/g) {
- # We have LPRng
- $spooler = 'lprng';
-}
-
-
-# PPD file name given via the command line
-# allow duplicates, and use the last specified one
-while ( ($spooler ne 'lprng') and ($argstr =~ s/\x01-p(\x01|)([^\x01]+)\x01/\x01/)) {
- $ppdfile = removeshellescapes($2);
-}
-while ($argstr =~ s/\x01--ppd(\x01|=|)([^\x01]+)\x01/\x01/) {
- $ppdfile = removeshellescapes($2);
-}
-
-# Check for LPD/GNUlpr by typical options which the spooler puts onto
-# the filter's command line (options "-w": text width, "-l": text
-# length, "-i": indent, "-x", "-y": graphics size, "-c": raw printing,
-# "-n": user name, "-h": host name)
-if ($argstr =~ s/\x01-h(\x01|)([^\x01]+)\x01/\x01/) {
- # We have LPD or GNUlpr
- if (($spooler ne 'lpd') && ($spooler ne 'gnulpr') && ($spooler ne 'lprng')) {
- $spooler = 'lpd';
- }
- $jobhost = $2;
-}
-if ($argstr =~ s/\x01-n(\x01|)([^\x01]+)\x01/\x01/) {
- # We have LPD or GNUlpr
- if (($spooler ne 'lpd') && ($spooler ne 'gnulpr') && ($spooler ne 'lprng')) {
- $spooler = 'lpd';
- }
- $jobuser = $2;
-}
-if (($argstr =~ s/\x01-w(\x01|)\d+\x01/\x01/) ||
- ($argstr =~ s/\x01-l(\x01|)\d+\x01/\x01/) ||
- ($argstr =~ s/\x01-x(\x01|)\d+\x01/\x01/) ||
- ($argstr =~ s/\x01-y(\x01|)\d+\x01/\x01/) ||
- ($argstr =~ s/\x01-i(\x01|)\d+\x01/\x01/) ||
- ($argstr =~ s/\x01-c\x01/\x01/)) {
- # We have LPD or GNUlpr
- if (($spooler ne 'lpd') && ($spooler ne 'gnulpr') && ($spooler ne 'lprng')) {
- $spooler = 'lpd';
- }
-}
-
-# LPRng delivers the option settings via the "-Z" argument
-if ($argstr =~ s/\x01-Z(\x01|)([^\x01]+)\x01/\x01/) {
- my @lpopts = split(/,/, $2);
- foreach my $opt (@lpopts) {
- $opt =~ s/^\s+//;
- $opt =~ s/\s+$//;
- $opt = removeshellescapes($opt);
- if ($opt =~ /\s+/) {
- $opt = "\"$opt\"";
- }
- $optstr .= "$opt ";
- }
- # We have LPRng
- $spooler = 'lprng';
-}
-
-# Job title and options for stock LPD
-if ($argstr =~ s/\x01-[jJ](\x01|)([^\x01]+)\x01/\x01/) {
- # An LPD
- $jobtitle = removeshellescapes($2);
- # Classic LPD hack
- if ($spooler eq "lpd") {
- $optstr .= "$jobtitle ";
- }
-}
-
-# Check for CPS
-if ($argstr =~ s/\x01--cps\x01/\x01/) {
- # We have cps
- $spooler = 'cps';
-}
-
-# Options for spooler-less printing, CPS, or PDQ
-while ($argstr =~ s/\x01-o(\x01|)([^\x01]+)\x01/\x01/) {
- my $opt = $2;
- $opt =~ s/^\s+//;
- $opt =~ s/\s+$//;
- $opt = removeshellescapes($opt);
- if ($opt =~ /\s+/) {
- $opt = "\"$opt\"";
- }
- $optstr .= "$opt ";
- # If we don't print as a PPR RIP or as a CPS filter, we print without
- # spooler (we check for PDQ later)
- if (($spooler ne 'ppr') && ($spooler ne 'cps')) {
- $spooler = 'direct';
- }
-}
-
-# Printer for spooler-less printing or PDQ
-if ($argstr =~ s/\x01-d(\x01|)([^\x01]+)\x01/\x01/) {
- $printer = removeshellescapes($2);
-}
-# Printer for spooler-less printing, PDQ, or LPRng
-if ($argstr =~ s/\x01-P(\x01|)([^\x01]+)\x01/\x01/) {
- $printer = removeshellescapes($2);
-}
-
-# Were we called from a PDQ wrapper?
-if ($argstr =~ s/\x01--pdq\x01/\x01/) {
- # We have PDQ
- $spooler = 'pdq';
-}
-
-# Were we called to build the PDQ driver declaration file?
-# "--appendpdq=<file>" appends the data to the <file>,
-# "--genpdq=<file>" creates/overwrites <file> for the data, and
-# "--genpdq" writes to standard output
-my $genpdqfile = "";
-if (($argstr =~ s/\x01--(gen)(raw|)pdq(\x01|=|)([^\x01]*)\x01/\x01/) ||
- ($argstr =~ s/\x01--(append)(raw|)pdq(\x01|=|)([^\x01]+)\x01/\x01/)) {
- # Determine output file name
- if (!$4) {
- $genpdqfile = ">&STDOUT";
- } else {
- if ($1 eq 'gen') {
- $genpdqfile = "> " . removeshellescapes($4);
- } else {
- $genpdqfile = ">> " . removeshellescapes($4);
- }
- }
- # Do we want to have a PDQ driver declaration for a raw printer?
- if ($2 eq 'raw') {
- my $time = time();
- my @pdqfile =
-"driver \"Raw-Printer-$time\" {
- # This PDQ driver declaration file was generated automatically by
- # foomatic-rip to allow raw (filter-less) printing.
- language_driver all {
- # We accept all file types and pass them through without any changes
- filetype_regx \"\"
- convert_exec {
- ln -s \$INPUT \$OUTPUT
- }
- }
- filter_exec {
- ln -s \$INPUT \$OUTPUT
- }
-}";
- open PDQFILE, $genpdqfile or
- rip_die("Cannot write PDQ driver declaration file",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- print PDQFILE join('', @pdqfile);
- close PDQFILE;
- exit $EXIT_PRINTED;
- }
- # We have PDQ
- $spooler = 'pdq';
-}
-
-
-# remove extra spacing if running as LPRng filter
-$added_lf = "" if $spooler eq 'lprng';
-
-## Command line arguments without name
-
-# Remaining arguments
-my @rargs = split(/\x01/, $argstr);
-shift @rargs;
-
-# Load definitions for PPR error messages, check whether we run as
-# PPR interface or as PPR RIP
-my( $ppr_printer, $ppr_address, $ppr_options, $ppr_jobbreak, $ppr_feedback,
- $ppr_codes, $ppr_jobname, $ppr_routing, $ppr_for, $ppr_filetype,
- $ppr_filetoprint );
-if ($spooler eq 'ppr') {
- # Read interface.sh so we will know the correct exit codes and
- # also signal.sh for the signal codes
- my $deffound = 0; # Did we find one of the definition files
- my @definitions;
- for my $file (("lib/interface.sh", "lib/signal.sh")) {
-
- open FILE, "< $file" || do {
- print $logh "error opening $file.\n";
- next;
- };
-
- $deffound = 1;
- while(my $line = <FILE>) {
- # Translate the shell script to Perl
- if (($line !~ m/^\s*$/) && ($line !~ m/^\s*\#/)) {
- $line =~ s/^\s*([^\#\s]*)/\$$1;/;
- push (@definitions, $line);
- }
- }
- close FILE;
- }
-
- if ($deffound) {
- # Apply the definitions loaded from PPR
- eval join('',@definitions) || do {
- print $logh "unable to evaluate definitions\n";
- rip_die ("Error in definitions evaluation",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- };
- }
-
- # Check whether we run as a PPR interface (if not, we run as a PPR RIP)
- if (($rargs[3] =~ /^\s*\d\d?\s*$/) &&
- ($rargs[5] =~ /^\s*\d\d?\s*$/) &&
- (($#rargs == 10) || ($#rargs == 9) || ($#rargs == 7))) {
- # PPR calls interfaces with many command line parameters,
- # where the forth and the sixth is a small integer
- # number. In addition, we have 8 (PPR <= 1.31), 10
- # (PPR>=1.32), 11 (PPR >= 1.50) command line parameters.
- # We also check whether the current working directory is a
- # PPR directory.
-
- # Get all command line parameters
- $ppr_printer = removeshellescapes($rargs[0]);
- $ppr_address = $rargs[1];
- $ppr_options = removeshellescapes($rargs[2]);
- $ppr_jobbreak = $rargs[3];
- $ppr_feedback = $rargs[4];
- $ppr_codes = $rargs[5];
- $ppr_jobname = removeshellescapes($rargs[6]);
- $ppr_routing = removeshellescapes($rargs[7]);
- $ppr_for = $rargs[8];
- $ppr_filetype = $rargs[9];
- $ppr_filetoprint = removeshellescapes($rargs[10]);
-
- # Common job parameters
- $printer = $ppr_printer;
- $jobtitle = $ppr_jobname;
- if ((!$jobtitle) && ($ppr_filetoprint)) {
- $jobtitle = $ppr_filetoprint;
- }
- $optstr .= "$ppr_options $ppr_routing";
-
- # Get the path of the PPD file from the queue configuration
- $ppdfile = `LANG=en_US; ppad show $ppr_printer | grep PPDFile`;
- $ppdfile = removeshellescapes($ppdfile);
- $ppdfile =~ s/PPDFile:\s+//;
- if ($ppdfile !~ m!^/!) {
- $ppdfile = "../../share/ppr/PPDFiles/$ppdfile";
- }
- chomp($ppdfile);
-
- # We have PPR and run as an interface
- $spooler = 'ppr_int';
- }
-}
-
-# CUPS
-my( $cups_jobid, $cups_user, $cups_jobtitle, $cups_copies, $cups_options,
- $cups_filename );
-if ($spooler eq 'cups') {
-
- # Use CUPS font path ("FontPath" in /etc/cups/cupsd.conf)
- if ($ENV{'CUPS_FONTPATH'}) {
- $ENV{'GS_LIB'} = $ENV{'CUPS_FONTPATH'} .
- ($ENV{'GS_LIB'} ? ":$ENV{'GS_LIB'}" : "");
- } else {
- if ($ENV{'CUPS_DATADIR'}) {
- $ENV{'GS_LIB'} = "$ENV{'CUPS_DATADIR'}/fonts" .
- ($ENV{'GS_LIB'} ? ":$ENV{'GS_LIB'}" : "");
- }
- }
-
- # Get all command line parameters
- $cups_jobid = removeshellescapes($rargs[0]);
- $cups_user = removeshellescapes($rargs[1]);
- $cups_jobtitle = removeshellescapes($rargs[2]);
- $cups_copies = removeshellescapes($rargs[3]);
- $cups_options = removeshellescapes($rargs[4]);
- $cups_filename = removeshellescapes($rargs[5]);
-
- # Common job parameters
- #$printer = $cups_printer;
- $jobid = $cups_jobid;
- $jobtitle = $cups_jobtitle;
- $jobuser = $cups_user;
- $copies = $cups_copies;
- $optstr .= $cups_options;
-
- # Check for and handle inputfile vs stdin
- if ((defined($cups_filename)) && ($cups_filename) &&
- ($cups_filename ne '-')) {
- # We get the input from a file
- @filelist = ($cups_filename);
- print $logh "Getting input from file $cups_filename\n";
- }
-}
-
-# Solaris LP spooler
-if ($spooler eq 'solaris') {
- # Get all command line parameters
- # $printer = # argv[0]
- # ($rargs[0] =~ m!^.*/([^/]+)$!);
- # $request_id = removeshellescapes($rargs[0]); # argv[1]
- # $user_name = removeshellescapes($rargs[1]); # argv[2]
- $jobtitle = removeshellescapes($rargs[2]); # argv[3]
- # $copies = removeshellescapes($rargs[3]); # argv[4] # handled by the
- # interface script
- $optstr .= removeshellescapes($rargs[4]); # argv[5]
- ($#rargs > 4) && # argv[6...]
- (@filelist = @rargs[5, $#rargs]);
-}
-
-# LPD/LPRng/GNUlpr
-if (($spooler eq 'lpd') ||
- ($spooler eq 'lprng' and !$ppdfile) ||
- ($spooler eq 'gnulpr')) {
-
- # Get PPD file name as the last command line argument
- $ppdfile = removeshellescapes($rargs[$#rargs]);
-
-}
-
-
-# No spooler, CPS, or PDQ
-if (($spooler eq 'direct') || ($spooler eq 'cps') || ($spooler eq 'pdq')) {
- # Which files do we want to print?
- @filelist = map { removeshellescapes($_) } @rargs;
-}
-
-
-
-## Additional spooler-specific preparations
-
-# CUPS
-
-if ($spooler eq 'cups') {
-
- # This piece of PostScript code (initial idea 2001 by Michael
- # Allerhand (michael.allerhand at ed dot ac dot uk, vastly
- # improved by Till Kamppeter in 2002) lets GhostScript output
- # the page accounting information which CUPS needs on standard
- # error.
- # Redesign by Helge Blischke (2004-11-17):
- # - As the PostScript job itself may define BeginPage and/or EndPage
- # procedures, or the alternate pstops filter may have inserted
- # such procedures, we make sure that the accounting routine
- # will safely coexist with those. To achieve this, we force
- # - the accountint stuff to be inserted at the very end of the
- # PostScript job's setup section,
- # - the accounting stuff just using the return value of the
- # existing EndPage procedure, if any (and providing a default one
- # if not).
- # - As PostScript jobs may contain calls to setpagedevice "between"
- # pages, e.g. to change media type, do in-job stapling, etc.,
- # we cannot rely on the "showpage count since last pagedevice
- # activation" but instead count the physical pages by ourselves
- # (in a global dictionary).
-
- if (defined $conf{ps_accounting}) {
- $ps_accounting = $conf{ps_accounting};
- }
- $accounting_prolog = $ps_accounting ? "[{
-%% Code for writing CUPS accounting tags on standard error
-
-/cupsPSLevel2 % Determine whether we can do PostScript level 2 or newer
- systemdict/languagelevel 2 copy
- known{get exec}{pop pop 1}ifelse 2 ge
-def
-
-cupsPSLevel2
-{ % in case of level 2 or higher
- currentglobal true setglobal % define a dictioary foomaticDict
- globaldict begin % in global VM and establish a
- /foomaticDict % pages count key there
- <<
- /PhysPages 0
- >>def
- end
- setglobal
-}if
-
-/cupsGetNumCopies { % Read the number of Copies requested for the current
- % page
- cupsPSLevel2
- {
- % PS Level 2+: Get number of copies from Page Device dictionary
- currentpagedevice /NumCopies get
- }
- {
- % PS Level 1: Number of copies not in Page Device dictionary
- null
- }
- ifelse
- % Check whether the number is defined, if it is \"null\" use #copies
- % instead
- dup null eq {
- pop #copies
- }
- if
- % Check whether the number is defined now, if it is still \"null\" use 1
- % instead
- dup null eq {
- pop 1
- } if
-} bind def
-
-/cupsWrite { % write a string onto standard error
- (%stderr) (w) file
- exch writestring
-} bind def
-
-/cupsFlush % flush standard error to make it sort of unbuffered
-{
- (%stderr)(w)file flushfile
-}bind def
-
-cupsPSLevel2
-{ % In language level 2, we try to do something reasonable
- <<
- /EndPage
- [ % start the array that becomes the procedure
- currentpagedevice/EndPage 2 copy known
- {get} % get the existing EndPage procedure
- {pop pop {exch pop 2 ne}bind}ifelse % there is none, define the default
- /exec load % make sure it will be executed, whatever it is
- /dup load % duplicate the result value
- { % true: a sheet gets printed, do accounting
- currentglobal true setglobal % switch to global VM ...
- foomaticDict begin % ... and access our special dictionary
- PhysPages 1 add % count the sheets printed (including this one)
- dup /PhysPages exch def % and save the value
- end % leave our dict
- exch setglobal % return to previous VM
- (PAGE: )cupsWrite % assemble and print the accounting string ...
- 16 string cvs cupsWrite % ... the sheet count ...
- ( )cupsWrite % ... a space ...
- cupsGetNumCopies % ... the number of copies ...
- 16 string cvs cupsWrite % ...
- (\\n)cupsWrite % ... a newline
- cupsFlush
- }/if load
- % false: current page gets discarded; do nothing
- ]cvx bind % make the array executable and apply bind
- >>setpagedevice
-}
-{
- % In language level 1, we do no accounting currently, as there is no global VM
- % the contents of which are undesturbed by save and restore.
- % If we may be sure that showpage never gets called inside a page related save / restore pair
- % we might implement an hack with showpage similar to the one above.
-}ifelse
-
-} stopped cleartomark
-" : "";
-
- # On which queue are we printing?
- # CUPS gives the PPD file the same name as the printer queue,
- # so we can get the queue name from the name of the PPD file.
- $ppdfile =~ m!^(.*/)([^/]+)\.ppd$!;
- $printer = $2;
-}
-
-# No spooler, CPS, or PDQ
-
-if (($spooler eq 'direct') || ($spooler eq 'cps') || ($spooler eq 'pdq')) {
-
- # Path for personal Foomatic configuration
- my $user_default_path = "$ENV{'HOME'}/.foomatic";
-
- if (!$ppdfile) {
- if (!$printer) {
- # No printer definition file selected, check whether we have a
- # default printer defined.
- for my $conf_file (("./.directconfig",
- "./directconfig",
- "./.config",
- "$user_default_path/direct/.config",
- "$user_default_path/direct.conf",
- "$configpath/direct/.config",
- "$configpath/direct.conf")) {
- if (open CONFIG, "< $conf_file") {
- while (my $line = <CONFIG>) {
- chomp $line;
- if ($line =~ /^default\s*:\s*([^:\s]+)\s*$/) {
- $printer = $1;
- last;
- }
- }
- close CONFIG;
- }
- if ($printer) {
- last;
- }
- }
- }
-
- # Neither in a config file nor on the command line a printer was
- # selected.
- if (!$printer) {
- rip_die("No printer definition (option \"-P <name>\") " .
- "specified!", $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
-
- # Search for the PPD file
-
- # Search also common spooler-specific locations, this way a printer
- # configured under a certain spooler can also be used without
- # spooler
-
- if (-r $printer) {
- $ppdfile = $printer;
- # CPS can have the PPD in the spool directory
- } elsif (($spooler eq 'cps') &&
- (-r "/var/spool/lpd/${printer}/${printer}.ppd")) {
- $ppdfile = "/var/spool/lpd/${printer}/${printer}.ppd";
- } elsif (($spooler eq 'cps') &&
- (-r "/var/local/spool/lpd/${printer}/${printer}.ppd")) {
- $ppdfile = "/var/local/spool/lpd/${printer}/${printer}.ppd";
- } elsif (($spooler eq 'cps') &&
- (-r "/var/local/lpd/${printer}/${printer}.ppd")) {
- $ppdfile = "/var/local/lpd/${printer}/${printer}.ppd";
- } elsif (($spooler eq 'cps') &&
- (-r "/var/spool/lpd/${printer}.ppd")) {
- $ppdfile = "/var/spool/lpd/${printer}.ppd";
- } elsif (($spooler eq 'cps') &&
- (-r "/var/local/spool/lpd/${printer}.ppd")) {
- $ppdfile = "/var/local/spool/lpd/${printer}.ppd";
- } elsif (($spooler eq 'cps') &&
- (-r "/var/local/lpd/${printer}.ppd")) {
- $ppdfile = "/var/local/lpd/${printer}.ppd";
- } elsif (-r "${printer}.ppd") { # current dir
- $ppdfile = "${printer}.ppd";
- } elsif (-r "$user_default_path/${printer}.ppd") { # user dir
- $ppdfile = "$user_default_path/${printer}.ppd";
- } elsif (-r "$configpath/direct/${printer}.ppd") { # system dir
- $ppdfile = "$configpath/direct/${printer}.ppd";
- } elsif (-r "$configpath/${printer}.ppd") { # system dir
- $ppdfile = "$configpath/${printer}.ppd";
- } elsif (-r "/etc/cups/ppd/${printer}.ppd") { # CUPS config dir
- $ppdfile = "/etc/cups/ppd/${printer}.ppd";
- } elsif (-r "/usr/local/etc/cups/ppd/${printer}.ppd") {
- $ppdfile = "/usr/local/etc/cups/ppd/${printer}.ppd";
- } elsif (-r "/usr/share/ppr/PPDFiles/${printer}.ppd") { # PPR PPDs
- $ppdfile = "/usr/share/ppr/PPDFiles/${printer}.ppd";
- } elsif (-r "/usr/local/share/ppr/PPDFiles/${printer}.ppd") {
- $ppdfile = "/usr/local/share/ppr/PPDFiles/${printer}.ppd";
- } else {
- rip_die ("There is no readable PPD file for the printer " .
- "$printer, is it configured?",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- }
-}
-
-
-
-## Files to be printed (can be more than one for spooler-less printing)
-
-# Empty file list -> print STDIN
-if ($#filelist < 0) {
- @filelist = ("<STDIN>");
-}
-
-# Check file list
-my $file;
-my $filecnt = 0;
-for $file (@filelist) {
- if ($file ne "<STDIN>") {
- if ($file =~ /^-/) {
- rip_die ("Invalid argument: $file",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- } elsif (! -r $file) {
- print $logh "File $file does not exist/is not readable\n";
- splice(@filelist, $filecnt, 1);
- $filecnt --;
- }
- }
- $filecnt ++;
-}
-
-
-
-## When we print without spooler or with CPS do not log onto STDERR unless
-## the "-v" ('Verbose') is set or the debug mode is used
-if ((($spooler eq 'direct') || ($spooler eq 'cps') || ($genpdqfile)) &&
- (!$verbose) && (!$debug)) {
- close $logh;
- open LOG, "> /dev/null";
- $logh = *LOG;
-
- use IO::Handle;
- $logh->autoflush(1);
-}
-
-
-
-## Start logging
-if (!$debug) {
- # If we are in debug mode, we do this earlier.
- print $logh "foomatic-rip version $ripversion running...\n";
- # Print the command line only in debug mode, Mac OS X adds very many
- # options so that CUPS cannot handle the output of the command line
- # in its log files. If CUPS encounters a line with more than 1024
- # characters sent into its log files, it aborts the job with an error.
- if (($debug) || ($spooler ne 'cups')) {
- print $logh "called with arguments: '", join("', '",@ARGV), "'\n";
- }
-}
-
-
-
-## PPD file
-
-# Load the PPD file and build a data structure for the renderer's
-# command line and the options
-open PPD, "< $ppdfile" || do {
- print $logh "error opening $ppdfile.\n";
- rip_die ("Unable to open PPD file $ppdfile",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
-};
-
-print $logh "Parsing PPD file ...\n";
-
-my $dat = {}; # data structure for the options
-my $currentargument = ""; # We are currently reading this argument
-
-# If we have an old Foomatic 2.0.x PPD file, read its built-in Perl
-# data structure into @datablob and the default values in %ppddefaults
-# Then delete the $dat structure, replace it by the one "eval"ed from
-# @datablob, and correct the default settings according to the ones of
-# the main PPD structure
-my @datablob;
-my $jclprefixset = 0;
-
-# Parse the PPD file
-sub undossify( $ );
-while(<PPD>) {
- # foomatic-rip should also work with PPD file downloaded under Windows.
- $_ = undossify($_);
- # Parse keywords
- if (m!^\*NickName:\s*\"(.*)$!) {
- # "*NickName: <code>"
- my $line = $1;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $cmd = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $cmd .= substr($line, 0, -2);
- } else {
- # line ends here
- $cmd .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $cmd .= $1;
- $model = unhtmlify($cmd);
- } elsif (m!^\*FoomaticIDs:\s*\"?\s*(\S+?)\s+(\S+?)\s*\"?\s*$!) {
- # "*FoomaticIDs: <printer ID> <driver ID>"
- my $id = $1;
- my $driver = $2;
- # Store the values
- $dat->{'id'} = $id;
- $dat->{'driver'} = $driver;
- } elsif (m!^\*FoomaticRIPPostPipe:\s*\"(.*)$!) {
- # "*FoomaticRIPPostPipe: <code>"
- my $line = $1;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $cmd = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $cmd .= substr($line, 0, -2);
- } else {
- # line ends here
- $cmd .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $cmd .= $1;
- $postpipe = unhtmlify($cmd);
- } elsif (m!^\*FoomaticRIPCommandLine:\s*\"(.*)$!) {
- # "*FoomaticRIPCommandLine: <code>"
- my $line = $1;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $cmd = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $cmd .= substr($line, 0, -2);
- } else {
- # line ends here
- $cmd .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $cmd .= $1;
- $dat->{'cmd'} = unhtmlify($cmd);
- } elsif (m!^\*FoomaticNoPageAccounting:\s*\"?\s*(\S+?)\s*\"?\s*$!) {
- # "*FoomaticRIPNoPageAccounting: <boolean value>"
- my $value = $1;
- # Apply the value
- if ($value =~ /^True$/i) {
- # Driver is not compatible with page accounting according to the
- # Foomatic database, so turn it off for this driver
- $ps_accounting = 0;
- $accounting_prolog = '';
- print $logh "CUPS page accounting disabled by driver.\n";
- }
- } elsif (m!^\*cupsFilter:\s*\"(.*)$!) {
- # "*cupsFilter: <code>"
- my $line = $1;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $cmd = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $cmd .= substr($line, 0, -2);
- } else {
- # line ends here
- $cmd .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $cmd .= $1;
- my $cupsfilterline = unhtmlify($cmd);
- if ($cupsfilterline =~ /^\s*(\S+)\s+\d+\s+(\S+)\s*$/) {
- print $logh "*cupsFilter: \"$cupsfilterline\"\n";
- # Make a hash by mime type for all CUPS filters set in this PPD
- $dat->{'cupsfilter'}{$1} = $2;
- }
- } elsif (m!^\*CustomPageSize\s+True:\s*\"(.*)$!) {
- # "*CustomPageSize True: <code>"
- my $setting = "Custom";
- my $translation = "Custom Size";
- my $line = $1;
- # Make sure that the argument is in the data structure
- checkarg ($dat, "PageSize");
- checkarg ($dat, "PageRegion");
- # Make sure that the setting is in the data structure
- checksetting ($dat, "PageSize", $setting);
- checksetting ($dat, "PageRegion", $setting);
- $dat->{'args_byname'}{'PageSize'}{'vals_byname'}{$setting}{'comment'} = $translation;
- $dat->{'args_byname'}{'PageRegion'}{'vals_byname'}{$setting}{'comment'} = $translation;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $code = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $code .= substr($line, 0, -2);
- } else {
- # line ends here
- $code .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $code .= $1;
- if ($code !~ m!^%% FoomaticRIPOptionSetting!m) {
- $dat->{'args_byname'}{'PageSize'}{'vals_byname'}{$setting}{'driverval'} = $code;
- $dat->{'args_byname'}{'PageRegion'}{'vals_byname'}{$setting}{'driverval'} = $code;
- }
- } elsif (m!^\*(JCL|)OpenUI\s+\*([^:]+):\s*(\S+)\s*$!) {
- # "*[JCL]OpenUI *<option>[/<translation>]: <type>"
- my $argnametrans = $2;
- my $argtype = $3;
- my $argname;
- my $translation = "";
- if ($argnametrans =~ m!^([^:/\s]+)/([^:]*)$!) {
- $argname = $1;
- $translation = $2;
- } else {
- $argname = $argnametrans;
- }
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the values
- $dat->{'args_byname'}{$argname}{'comment'} = $translation;
- # Set the argument type only if not defined yet, a
- # definition in "*FoomaticRIPOption" has priority
- if ( !($dat->{'args_byname'}{$argname}{'type'}) ) {
- if ($argtype eq "PickOne") {
- $dat->{'args_byname'}{$argname}{'type'} = 'enum';
- } elsif ($argtype eq "PickMany") {
- $dat->{'args_byname'}{$argname}{'type'} = 'pickmany';
- } elsif ($argtype eq "Boolean") {
- $dat->{'args_byname'}{$argname}{'type'} = 'bool';
- }
- }
- # Mark in which argument we are currently, so that we can find
- # the entries for the choices
- $currentargument = $argname;
- } elsif (m!^\*(JCL|)CloseUI:\s+\*([^:/\s]+)\s*$!) {
- # "*[JCL]CloseUI *<option>"
- my $argname = $2;
- # Unmark the current argument to do not mis-interpret any keywords
- # as choices
- $currentargument = "";
- } elsif ((m!^\*FoomaticRIPOption ([^/:\s]+):\s*\"?\s*(\S+?)\s+(\S+)\s+(\S)\s*\"?\s*$!) ||
- (m!^\*FoomaticRIPOption ([^/:\s]+):\s*\"?\s*(\S+?)\s+(\S+)\s+(\S)\s+(\S+?)\s*\"?\s*$!)){
- # "*FoomaticRIPOption <option>: <type> <style> <spot> [<order>]"
- # <order> only used for 1-choice enum options
- my $argname = $1;
- my $argtype = $2;
- my $argstyle = $3;
- my $spot = $4;
- my $order = $5;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the values
- $dat->{'args_byname'}{$argname}{'type'} = $argtype;
- if ($argstyle eq "PS") {
- $dat->{'args_byname'}{$argname}{'style'} = 'G';
- } elsif ($argstyle eq "CmdLine") {
- $dat->{'args_byname'}{$argname}{'style'} = 'C';
- } elsif ($argstyle eq "JCL") {
- $dat->{'args_byname'}{$argname}{'style'} = 'J';
- $dat->{'jcl'} = 1;
- } elsif ($argstyle eq "Composite") {
- $dat->{'args_byname'}{$argname}{'style'} = 'X';
- }
- $dat->{'args_byname'}{$argname}{'spot'} = $spot;
- # $order only defined here for 1-choice enum options
- if ($order) {
- $dat->{'args_byname'}{$argname}{'order'} = $order;
- }
- } elsif (m!^\*FoomaticRIPOptionPrototype\s+([^/:\s]+):\s*\"(.*)$!) {
- # "*FoomaticRIPOptionPrototype <option>: <code>"
- # Used for numerical and string options only
- my $argname = $1;
- my $line = $2;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the value
- # Code string can have multiple lines, read all of them
- my $proto = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $proto .= substr($line, 0, -2);
- } else {
- # line ends here
- $proto .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $proto .= $1;
- $dat->{'args_byname'}{$argname}{'proto'} = unhtmlify($proto);
- } elsif (m!^\*FoomaticRIPOptionRange\s+([^/:\s]+):\s*\"?\s*(\S+?)\s+(\S+?)\s*\"?\s*$!) {
- # "*FoomaticRIPOptionRange <option>: <min> <max>"
- # Used for numerical options only
- my $argname = $1;
- my $min = $2;
- my $max = $3;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the values
- $dat->{'args_byname'}{$argname}{'min'} = $min;
- $dat->{'args_byname'}{$argname}{'max'} = $max;
- } elsif (m!^\*FoomaticRIPOptionMaxLength\s+([^/:\s]+):\s*\"?\s*(\S+?)\s*\"?\s*$!) {
- # "*FoomaticRIPOptionMaxLength <option>: <length>"
- # Used for string options only
- my $argname = $1;
- my $maxlength = $2;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the value
- $dat->{'args_byname'}{$argname}{'maxlength'} = $maxlength;
- } elsif (m!^\*FoomaticRIPOptionAllowedChars\s+([^/:\s]+):\s*\"(.*)$!) {
- # "*FoomaticRIPOptionAllowedChars <option>: <code>"
- # Used for string options only
- my $argname = $1;
- my $line = $2;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $code = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $code .= substr($line, 0, -2);
- } else {
- # line ends here
- $code .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $code .= $1;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the value
- $dat->{'args_byname'}{$argname}{'allowedchars'} = unhtmlify($code);
- } elsif (m!^\*FoomaticRIPOptionAllowedRegExp\s+([^/:\s]+):\s*\"(.*)$!) {
- # "*FoomaticRIPOptionAllowedRegExp <option>: <code>"
- # Used for string options only
- my $argname = $1;
- my $line = $2;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $code = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $code .= substr($line, 0, -2);
- } else {
- # line ends here
- $code .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $code .= $1;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the value
- $dat->{'args_byname'}{$argname}{'allowedregexp'} =
- unhtmlify($code);
- } elsif (m!^\*OrderDependency:\s*(\S+)\s+(\S+)\s+\*([^:/\s]+)\s*$!) {
- # "*OrderDependency: <order> <section> *<option>"
- my $order = $1;
- my $section = $2;
- my $argname = $3;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the values
- $dat->{'args_byname'}{$argname}{'order'} = $order;
- $dat->{'args_byname'}{$argname}{'section'} = $section;
- } elsif (m!^\*Default([^/:\s]+):\s*([^/:\s]+)\s*$!) {
- # "*Default<option>: <value>"
- my $argname = $1;
- my $default = $2;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the value
- $dat->{'args_byname'}{$argname}{'default'} = $default;
- } elsif (m!^\*FoomaticRIPDefault([^/:\s]+):\s*\"?\s*([^/:\s]+?)\s*\"?\s*$!) {
- # "*FoomaticRIPDefault<option>: <value>"
- # Used for numerical options only
- my $argname = $1;
- my $default = $2;
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Store the value
- $dat->{'args_byname'}{$argname}{'fdefault'} = $default;
- } elsif (m!^\*$currentargument\s+([^:]+):\s*\"(.*)$!) {
- # "*<option> <choice>[/<translation>]: <code>"
- my $settingtrans = $1;
- my $line = $2;
- my $translation = "";
- my $setting = "";
- if ($settingtrans =~ m!^([^:/\s]+)/([^:]*)$!) {
- $setting = $1;
- $translation = $2;
- } else {
- $setting = $settingtrans;
- }
- # Make sure that the argument is in the data structure
- checkarg ($dat, $currentargument);
- # Make sure that the setting is in the data structure (enum options)
- my $bool =
- ($dat->{'args_byname'}{$currentargument}{'type'} eq 'bool');
- if ($bool) {
- if (lc($setting) eq "true") {
- if (!$dat->{'args_byname'}{$currentargument}{'comment'}) {
- $dat->{'args_byname'}{$currentargument}{'comment'} =
- $translation;
- }
- $dat->{'args_byname'}{$currentargument}{'comment_true'} =
- $translation;
- } else {
- $dat->{'args_byname'}{$currentargument}{'comment_false'} =
- $translation;
- }
- } else {
- checksetting ($dat, $currentargument, $setting);
- # Make sure that this argument has a default setting, even if
- # none is defined in this PPD file
- if (!defined ($dat->{'args_byname'}{$currentargument}{'default'})) {
- $dat->{'args_byname'}{$currentargument}{'default'} = $setting;
- }
- $dat->{'args_byname'}{$currentargument}{'vals_byname'}{$setting}{'comment'} = $translation;
- }
- # Store the value
- # Code string can have multiple lines, read all of them
- my $code = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $code .= substr($line, 0, -2);
- } else {
- # line ends here
- $code .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $code .= $1;
- if ($code !~ m!^%% FoomaticRIPOptionSetting!) {
- if ($bool) {
- if (lc($setting) eq "true") {
- $dat->{'args_byname'}{$currentargument}{'proto'} = $code;
- } else {
- $dat->{'args_byname'}{$currentargument}{'protof'} = $code;
- }
- } else {
- $dat->{'args_byname'}{$currentargument}{'vals_byname'}{$setting}{'driverval'} = $code;
- }
- }
- } elsif ((m!^\*FoomaticRIPOptionSetting\s+([^/:=\s]+)=([^/:=\s]+):\s*\"(.*)$!) ||
- (m!^\*FoomaticRIPOptionSetting\s+([^/:=\s]+):\s*\"(.*)$!)) {
- # "*FoomaticRIPOptionSetting <option>[=<choice>]: <code>"
- # For boolean options <choice> is not given
- my $argname = $1;
- my $setting = $2;
- my $line = $3;
- my $bool = 0;
- if (!$line) {
- $line = $setting;
- $bool = 1;
- }
- # Make sure that the argument is in the data structure
- checkarg ($dat, $argname);
- # Make sure that the setting is in the data structure (enum options)
- if (!$bool) {
- checksetting ($dat, $argname, $setting);
- # Make sure that this argument has a default setting, even if
- # none is defined in this PPD file
- if (!defined ($dat->{'args_byname'}{$argname}{'default'})) {
- $dat->{'args_byname'}{$argname}{'default'} = $setting;
- }
- }
- # Store the value
- # Code string can have multiple lines, read all of them
- my $code = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $code .= substr($line, 0, -2);
- } else {
- # line ends here
- $code .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $code .= $1;
- if ($bool) {
- $dat->{'args_byname'}{$argname}{'proto'} = unhtmlify($code);
- } else {
- $dat->{'args_byname'}{$argname}{'vals_byname'}{$setting}{'driverval'} = unhtmlify($code);
- }
- } elsif (m!^\*(Foomatic|)JCL(Begin|ToPSInterpreter|End|Prefix):\s*\"(.*)$!) {
- # "*(Foomatic|)JCL(Begin|ToPSInterpreter|End|Prefix): <code>"
- # The printer supports PJL/JCL when there is such a line
- $dat->{'jcl'} = 1;
- my $item = $2;
- my $line = $3;
- # Store the value
- # Code string can have multiple lines, read all of them
- my $code = "";
- while ($line !~ m!\"!) {
- if ($line =~ m!&&$!) {
- # line continues in next line
- $code .= substr($line, 0, -2);
- } else {
- # line ends here
- $code .= "$line\n";
- }
- # Read next line
- $line = <PPD>;
- chomp $line;
- }
- $line =~ m!^([^\"]*)\"!;
- $code .= $1;
- if ($item eq 'Begin') {
- $jclbegin = unhexify($code);
- $jclprefix = "" if (!$jclprefixset) && ($jclbegin !~ /PJL/s);
- } elsif ($item eq 'ToPSInterpreter') {
- $jcltointerpreter = unhexify($code);
- } elsif ($item eq 'End') {
- $jclend = unhexify($code);
- } elsif ($item eq 'Prefix') {
- $jclprefix = unhexify($code);
- $jclprefixset = 1;
- }
- } elsif (m!^\*\% COMDATA \#(.*)$!) {
- # If we have an old Foomatic 2.0.x PPD file, collect its Perl data
- push (@datablob, $1);
- }
-}
-close PPD;
-
-# If we have an old Foomatic 2.0.x PPD file use its Perl data structure
-if ($#datablob >= 0) {
- print $logh "${added_lf}You are using an old Foomatic 2.0 PPD file, consider " .
- "upgrading.${added_lf}\n";
- my $VAR1;
- if (eval join('',@datablob)) {
- # Overtake default settings from the main structure of the PPD file
- for my $arg (@{$dat->{'args'}}) {
- if ($arg->{'default'}) {
- $VAR1->{'argsbyname'}{$arg->{'name'}}{'default'} =
- $arg->{'default'};
- }
- }
- undef $dat;
- $dat = $VAR1;
- $dat->{'jcl'} = $dat->{'pjl'};
- } else {
- # Perl structure broken
- print $logh "${added_lf}Unable to evaluate datablob, print job may come " .
- "out incorrectly or not at all.${added_lf}\n";
- }
-}
-
-
-
-## We do not need to parse the PostScript job when we don't have
-## any options. If we have options, we must check whether the
-## default settings from the PPD file are valid and correct them
-## if nexessary.
-
-my $dontparse = 0;
-if ((!defined(@{$dat->{'args'}})) ||
- ($#{$dat->{'args'}} < 0)) {
- # We don't have any options, so we do not need to parse the
- # PostScript data
- $dontparse = 1;
-} else {
- # Let the default value of a boolean option being 0 or 1 instead of
- # "True" or "False", range-check the defaults of all options and
- # issue warnings if the values are not valid
- checkoptions($dat, 'default');
-
- # Adobe's PPD specs do not support numerical
- # options. Therefore the numerical options are mapped to
- # enumerated options in the PPD file and their characteristics
- # as a numerical option are stored in "*Foomatic..."
- # keywords. A default must be between the enumerated
- # fixed values. The default
- # value must be given by a "*FoomaticRIPDefault<option>:
- # <value>" line in the PPD file. But this value is only valid
- # if the "official" default given by a "*Default<option>:
- # <value>" line (it must be one of the enumerated values)
- # points to the enumerated value which is closest to this
- # value. This way a user can select a default value with a
- # tool only supporting PPD files but not Foomatic extensions.
- # This tool only modifies the "*Default<option>: <value>" line
- # and if the "*FoomaticRIPDefault<option>: <value>" had always
- # priority, the user's change in "*Default<option>: <value>"
- # would have no effect.
-
- for my $arg (@{$dat->{'args'}}) {
- if ($arg->{'fdefault'}) {
- if ($arg->{'default'}) {
- if ($arg->{'type'} =~ /^(int|float)$/) {
- if ($arg->{'fdefault'} < $arg->{'min'}) {
- $arg->{'fdefault'} = $arg->{'min'};
- }
- if ($arg->{'fdefault'} > $arg->{'max'}) {
- $arg->{'fdefault'} = $arg->{'max'};
- }
- if ($arg->{'type'} eq 'int') {
- $arg->{'fdefault'} = POSIX::floor($arg->{'fdefault'});
- }
- my $mindiff = abs($arg->{'max'} - $arg->{'min'});
- my $closestvalue;
- for my $val (@{$arg->{'vals'}}) {
- if (abs($arg->{'fdefault'} - $val->{'value'}) <
- $mindiff) {
- $mindiff =
- abs($arg->{'fdefault'} - $val->{'value'});
- $closestvalue = $val->{'value'};
- }
- }
- if (($arg->{'default'} == $closestvalue) ||
- (abs($arg->{'default'} - $closestvalue) /
- $closestvalue < 0.001)) {
- $arg->{'default'} = $arg->{'fdefault'};
- }
- }
- } else {
- $arg->{'default'} = $arg->{'fdefault'};
- }
- }
- }
-}
-
-# Is our PPD for a CUPS raster driver
-if (my $cupsfilter = $dat->{'cupsfilter'}{"application/vnd.cups-raster"}) {
-
- # Search filter in cupsfilterpath
- # The %Y is a placeholder for the option settings
- my $havefilter = 0;
- for (split(':', $cupsfilterpath)) {
- if (-x "$_/$cupsfilter") {
- $havefilter=1;
- $cupsfilter = "$_/$cupsfilter 0 '' '' 0 '%Y%X'";
- last;
- }
- }
-
- if (!$havefilter) {
-
- # We do not have the required filter, so we assume that
- # rendering this job is supposed to be done on a remote
- # server. So we do not define a renderer command line and
- # embed only the option settings (as we had a PostScript
- # printer). This way the settings are # taken into account
- # when the job is rendered on the server.
- print $logh "${added_lf}CUPS filter for this PPD file not found " .
- "assuming that job will be rendered on a remote server. Only " .
- "the PostScript of the options will be inserted into the " .
- "PostScript data stream.${added_lf}\n";
-
- } else {
-
- # use pstoraster script if available, otherwise run GhostScript
- # directly
- my $pstoraster = "pstoraster";
- my $havepstoraster = 0;
- for (split(':', $cupsfilterpath)) {
- if (-x "$_/$pstoraster") {
- $havepstoraster=1;
- $pstoraster = "$_/$pstoraster 0 '' '' 0 '%X'";
- last;
- }
- }
-
- if (!$havepstoraster) {
-
- # Build GhostScript command line
- $pstoraster = "gs -dQUIET -dDEBUG -dPARANOIDSAFER -dNOPAUSE -dBATCH -dNOMEDIAATTRS -sDEVICE=cups -sOutputFile=-%W -"
-
- }
-
- # build GhostScript/CUPS driver command line
- $dat->{'cmd'} = "$pstoraster | $cupsfilter";
-
- # Set environment variables
- $ENV{'PPD'} = $ppdfile;
-
- }
-}
-
-# Was the RIP command line defined in the PPD file? If not, we assume a
-# PostScript printer and do not render/translate the input data
-if (!defined($dat->{'cmd'})) {
- $dat->{'cmd'} = "cat%A%B%C%D%E%F%G%H%I%J%K%L%M%Z";
- if ($dontparse) {
- # No command line, no options, we have a raw queue, don't check
- # whether the input is PostScript and ignore the "docs" option,
- # simply pass the input data to the backend.
- $dontparse = 2;
- $model = "Raw queue";
- }
-}
-
-
-
-## Summary for debugging
-print $logh "${added_lf}Parameter Summary\n";
-print $logh "-----------------${added_lf}\n";
-print $logh "Spooler: $spooler\n";
-print $logh "Printer: $printer\n";
-print $logh "Shell: $modern_shell\n";
-print $logh "PPD file: $ppdfile\n";
-print $logh "ATTR file: $attrpath\n";
-print $logh "Printer model: $model\n";
-# Print the options string only in debug mode, Mac OS X adds very many
-# options so that CUPS cannot handle the output of the option string
-# in its log files. If CUPS encounters a line with more than 1024 characters
-# sent into its log files, it aborts the job with an error.
-if (($debug) || ($spooler ne 'cups')) {
- print $logh "Options: $optstr\n";
-}
-print $logh "Job title: $jobtitle\n";
-print $logh "File(s) to be printed: ${added_lf}@filelist${added_lf}\n";
-print $logh "GhostScript extra search path ('GS_LIB'): $ENV{'GS_LIB'}\n"
- if $ENV{'GS_LIB'};
-
-
-
-## Parse options from command line ($optstr)
-
-# Before we start, save the defaults for printing documentation pages
-
-copyoptions($dat, 'default', 'userval');
-
-
-# The options are "foo='bar nut'", "foo", "nofoo", "'bar nut'", or
-# "foo:'bar nut'" (when GPR was used) all with spaces between...
-# In addition they can be preceeded by page ranges, separated with a
-# colon.
-
-my @opts;
-
-# Variable for PPR's backend interface name (parallel, tcpip, atalk, ...)
-
-my $backend = "";
-
-# Array to collect unknown options so that they can get passed to the
-# backend interface of PPR. For other spoolers we ignore them.
-
-my @backendoptions = ();
-
-# "foo='bar nut'"
-while ($optstr =~ s!(((even|odd|[\d,-]+):|)\w+=[\'\"].*?[\'\"]) ?!!i) {
- push (@opts, $1);
-}
-
-# "foo:'bar nut'" (GPR separates option and setting with a colon ":")
-while ($optstr =~ s!(((even|odd|[\d,-]+):|)\w+:[\'\"].*?[\'\"]) ?!!i) {
-#while ($optstr =~ s!(\w+=[\'\"].*?[\'\"])!!i) {
- push (@opts, $1);
-}
-
-# "'bar nut'", "'foo=bar nut'", "'foo:bar nut'"
-while ($optstr =~ s!([\'\"].+?[\'\"]) ?!!) {
- my $opt = $1;
- $opt =~ s/[\'\"]//g; # Make only sure that we didn't quote
- # the option for a second time when we read
- # rge options from the command line or
- # environment variable
- push (@opts, $opt);
-
-}
-
-# "foo", "nofoo"
-push(@opts, split(/ /,$optstr));
-
-# Now actually process those pesky options...
-
-for (@opts) {
- print $logh "Pondering option '$_'\n";
-
- # "docs" option to print help page
- if ((lc($_) =~ /^\s*docs\s*$/) ||
- (lc($_) =~ /^\s*docs\s*=\s*true\s*$/)) {
- # The second one is necessary becuase CUPS 1.1.15 or newer sees
- # "docs" as boolean option and modifies it to "docs=true"
- $do_docs = 1;
- next;
- }
-
- # "profile" option to supply a color correction profile to a
- # CUPS raster driver
- if (lc($_) =~ /^\s*profile=(\S+)\s*$/) {
- $cupscolorprofile=$1;
- $dat->{'cmd'} =~ s!\%X!profile=$cupscolorprofile!g;
- $dat->{'cmd'} =~ s!\%W! -c\"<</cupsProfile($cupscolorprofile)>>setpagedevice\"!g;
- next;
- }
-
- # Is the command line option limited to certain page ranges? If so,
- # mark the setting with a hash key containing the ranges
- my $optionset;
- if (s/^(even|odd|[\d,-]+)://i) {
- $optionset = "pages:$1";
- } else {
- $optionset = 'userval';
- }
-
- # Solaris options that have no reason to be
- if (/^nobanner$/ || /^dest=.+$/ || /^protocol=.+$/) {
- next;
- }
-
- my $arg;
- if ((m!([^=]+)=\'?(.*)\'?!) || (m!([^=:]+):\'?(.*)\'?!)) {
- my ($aname, $avalue) = ($1, $2);
-
- if (($optionset =~ /pages/) &&
- ($arg = argbyname($aname)) &&
- ((!defined($arg->{'section'})) ||
- ($arg->{'section'} !~ /^(Any|Page)Setup/))) {
- print $logh "This option is not a \"PageSetup\" or " .
- "\"AnySetup\" option, so it cannot be restricted to " .
- "a page range.\n";
- next;
- }
-
- # At first look for the "backend" option to determine the PPR
- # backend to use
- if (($aname =~ m!^backend$!i) && ($spooler eq 'ppr_int')) {
- # Backend interface name
- $backend = $avalue;
- } elsif ($aname =~ m!^media$!i) {
-
- # Standard arguments?
- # media=x,y,z
- # sides=one|two-sided-long|short-edge
-
- # Rummage around in the media= option for known media, source,
- # etc types.
- # We ought to do something sensible to make the common manual
- # boolean option work when specified as a media= tray thing.
- #
- # Note that this fails miserably when the option value is in
- # fact a number; they all look alike. It's unclear how many
- # drivers do that. We may have to standardize the verbose
- # names to make them work as selections, too.
-
- my @values = split(',',$avalue);
- for (@values) {
- my $val;
- if ($dat->{'args_byname'}{'PageSize'} and
- $val=valbyname($dat->{'args_byname'}{'PageSize'},$_)) {
- $dat->{'args_byname'}{'PageSize'}{$optionset} =
- $val->{'value'};
- # Keep "PageRegion" in sync
- if ($dat->{'args_byname'}{'PageRegion'} and
- $val=valbyname($dat->{'args_byname'}{'PageRegion'},
- $_)) {
- $dat->{'args_byname'}{'PageRegion'}{$optionset} =
- $val->{'value'};
- }
- } elsif ($dat->{'args_byname'}{'PageSize'}
- and /^Custom/) {
- $dat->{'args_byname'}{'PageSize'}{$optionset} = $_;
- # Keep "PageRegion" in sync
- if ($dat->{'args_byname'}{'PageRegion'}) {
- $dat->{'args_byname'}{'PageRegion'}{$optionset} =
- $_;
- }
- } elsif ($dat->{'args_byname'}{'MediaType'} and
- $val=valbyname($dat->{'args_byname'}{'MediaType'},
- $_)) {
- $dat->{'args_byname'}{'MediaType'}{$optionset} =
- $val->{'value'};
- } elsif ($dat->{'args_byname'}{'InputSlot'} and
- $val=valbyname($dat->{'args_byname'}{'InputSlot'},
- $_)) {
- $dat->{'args_byname'}{'InputSlot'}{$optionset} =
- $val->{'value'};
- } elsif (lc($_) eq 'manualfeed') {
- # Special case for our typical boolean manual
- # feeder option if we didn't match an InputSlot above
- if (defined($dat->{'args_byname'}{'ManualFeed'})) {
- $dat->{'args_byname'}{'ManualFeed'}{$optionset} = 1;
- }
- } else {
- print $logh "Unknown \"media\" component: \"$_\".\n";
- }
- }
- } elsif ($aname =~ m!^sides$!i) {
- # Handle the standard duplex option, mostly
- if ($avalue =~ m!^two-sided!i) {
- if (defined($dat->{'args_byname'}{'Duplex'})) {
- # Default to long-edge binding here, for the case that
- # there is no binding setting
- $dat->{'args_byname'}{'Duplex'}{$optionset} =
- 'DuplexNoTumble';
- # Check the binding: "long edge" or "short edge"
- if ($avalue =~ m!long-edge!i) {
- if (defined($dat->{'args_byname'}{'Binding'})) {
- $dat->{'args_byname'}{'Binding'}{$optionset} =
- $dat->{'args_byname'}{'Binding'}{'vals_byname'}{'LongEdge'}{'value'};
- } else {
- $dat->{'args_byname'}{'Duplex'}{$optionset} =
- 'DuplexNoTumble';
- }
- } elsif ($avalue =~ m!short-edge!i) {
- if (defined($dat->{'args_byname'}{'Binding'})) {
- $dat->{'args_byname'}{'Binding'}{$optionset} =
- $dat->{'args_byname'}{'Binding'}{'vals_byname'}{'ShortEdge'}{'value'};
- } else {
- $dat->{'args_byname'}{'Duplex'}{$optionset} =
- 'DuplexTumble';
- }
- }
- }
- } elsif ($avalue =~ m!^one-sided!i) {
- if (defined($dat->{'args_byname'}{'Duplex'})) {
- $dat->{'args_byname'}{'Duplex'}{$optionset} = 'None';
- }
- }
-
- # We should handle the other half of this option - the
- # BindEdge bit. Also, are there well-known ipp/cups
- # options for Collate and StapleLocation? These may be
- # here...
-
- } else {
- # Various non-standard printer-specific options
- if ($arg = argbyname($aname)) {
- if (defined(my $newvalue =
- checkoptionvalue($dat, $aname, $avalue, 0))) {
- # If the choice is valid, use it, otherwise
- # ignore it.
- $arg->{$optionset} = $newvalue;
- # If this argument is PageSize or PageRegion,
- # also set the other
- syncpagesize($dat, $aname, $avalue, $optionset);
- } else {
- # Invalid choice, make log entry
- print $logh "Invalid choice $aname=$avalue.\n";
- }
- } elsif ($spooler eq 'ppr_int') {
- # Unknown option, pass it to PPR's backend interface
- push (@backendoptions, "$aname=$avalue");
- } else {
- # Unknown option, make log entry
- print $logh "Unknown option $aname=$avalue.\n";
- }
- }
- } elsif (m!^([\d\.]+)x([\d\.]+)([A-Za-z]*)$!) {
- my ($w, $h, $u) = ($1, $2, $3);
- # Custom paper size
- if (($w != 0) && ($h != 0) &&
- ($arg=argbyname("PageSize")) &&
- (defined($arg->{'vals_byname'}{'Custom'}))) {
- $arg->{$optionset} = "Custom.${w}x${h}${u}";
- # Keep "PageRegion" in sync
- if ($dat->{'args_byname'}{'PageRegion'}) {
- $dat->{'args_byname'}{'PageRegion'}{$optionset} =
- $arg->{$optionset};
- }
- }
- } elsif ((m!^\s*no(.+)\s*$!i) and ($arg=argbyname($1))) {
- # standard bool args:
- # landscape; what to do here?
- # duplex; we should just handle this one OK now?
- $arg->{$optionset} = 0;
- } elsif (m!^\s*(.+)\s*$!) {
- if ($arg=argbyname($1)) {
- $arg->{$optionset} = 1;
- } else {
- print $logh "Unknown boolean option \"$1\".\n";
- }
- }
-}
-$do_docs = 1 if( $show_docs );
-
-
-## Were we called to build the PDQ driver declaration file?
-my @pdqfile;
-if ($genpdqfile) {
- @pdqfile = buildpdqdriver($dat, 'userval');
- open PDQFILE, $genpdqfile or
- rip_die("Cannot write PDQ driver declaration file",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- print PDQFILE join('', @pdqfile);
- close PDQFILE;
- exit $EXIT_PRINTED;
-}
-
-
-
-## Set the $postpipe
-
-# $postpipe when running as a PPR RIP
-if ($spooler eq 'ppr') {
- # The PPR RIP sends the data output to /dev/fd/3 instead of to STDOUT
- if (-w "/dev/fd/3") {
- $postpipe = "| cat - > /dev/fd/3";
- } else {
- $postpipe = "| cat - >&3";
- }
-}
-
-# Set up PPR backend (if we run as a PPR interface).
-if ($spooler eq 'ppr_int') {
-
- # Is the chosen backend installed and executable
- if (!-x "interfaces/$backend") {
- my $pwd = cwd;
- print $logh "The backend interface $pwd/interfaces/$backend " .
- "does not exist/is not executable!\n";
- rip_die ("The backend interface $pwd/interfaces/$backend " .
- "does not exist/is not executable!",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
-
- # foomatic-rip cannot use foomatic-rip as backend
- if ($backend eq "foomatic-rip") {
- print $logh "\"foomatic-rip\" cannot use itself as backend " .
- "interface!\n";
- ppr_die ($ppr_printer,
- "\"foomatic-rip\" cannot use itself as backend interface!",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
-
- # Put the backend interface into the $postpipe
- $postpipe = "| ( interfaces/$backend \"$ppr_printer\" ".
- "\"$ppr_address\" \"" . join(" ",@backendoptions) .
- "\" \"$ppr_jobbreak\" \"$ppr_feedback\" " .
- "\"$ppr_codes\" \"$ppr_jobname\" \"$ppr_routing\" " .
- "\"$ppr_for\" \"\" )";
-
-}
-
-# CUPS and PDQ have their own backends, they do not need a $postpipe
-if (($spooler eq 'cups') || ($spooler eq 'pdq')) {
- # No $postpipe for CUPS or PDQ, even if one is defined in the PPD file
- $postpipe = "";
-}
-
-# CPS needs always a $postpipe, set the default one for local printing
-# if none is set
-if (($spooler eq 'cps') && !$postpipe) {
- $postpipe = "| cat - > \$LPDDEV";
-}
-
-if ($postpipe) {
- print $logh "${added_lf}Output will be redirected to:\n$postpipe${added_lf}\n";
-}
-
-
-
-## Print documentation page when asked for
-my ($docgeneratorhandle, $docgeneratorpid,$retval);
-if ($do_docs) {
- # Don't print the supplied files, STDIN will be redirected to the
- # documentation page generator
- @filelist = ("<STDIN>");
- # Start the documentation page generator
- ($docgeneratorhandle, $docgeneratorpid) =
- getdocgeneratorhandle($dat);
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error opening documentation page generator",
- $retval);
- }
- # Read the further data from the documentation page generator and
- # not from STDIN
- if (!close STDIN && $! != $ESPIPE) {
- rip_die ("Couldn't close STDIN",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!open (STDIN, "<&$docgeneratorhandle")) {
- rip_die ("Couldn't dup \$docgeneratorhandle",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if( $show_docs ){
- while( <$docgeneratorhandle> ){
- print;
- }
- exit(0);
- }
-}
-
-
-
-
-## In debug mode save the data supposed to be fed into the
-## renderer also into a file, reset the file here
-
-if ($debug) {
- modern_system("> ${logfile}.ps");
-}
-
-
-
-## From here on we have to repeat all the rest of the program for
-## every file to print
-
-for $file (@filelist) {
-
- print $logh
-"${added_lf}================================================\n${added_lf}".
-"File: $file\n${added_lf}" .
-"================================================\n${added_lf}";
-
-
-
- ## If we do not print standard input, open the file to print
- if ($file ne "<STDIN>") {
- if (! -r $file) {
- print $logh "File $file missing or not readable, skipping.\n";
- next;
- }
- close STDIN;
- open STDIN, "< $file" || do {
- print $logh "Cannot open $file, skipping.\n";
- next;
- }
- }
-
-
-
- ## Do we have a raw queue
- if ($dontparse == 2) {
- # Raw queue, simply pass the input into the $postpipe (or to STDOUT
- # when there is no $postpipe)
- print $logh "Raw printing, executing \"cat $postpipe\"${added_lf}\n";
- modern_system("cat $postpipe");
- next;
- }
-
-
-
- ## First, for arguments with a default, stick the default in as
- ## the initial value for the "header" option set, this option set
- ## consists of the PPD defaults, the options specified on the
- ## command line, and the options set in the header part of the
- ## PostScript file (all before the first page begins).
-
- copyoptions($dat, 'userval', 'header');
-
-
-
- ## 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.
-
- my $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.
-
- my $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.
-
- my $maxlinesforpageoptions=200; # Unfortunately, CUPS does not bracket
- # "PageSetup" option with
- # "%%BeginPageSetup" and
- # "%%EndPageSetup", so the options
- # can simply stand after the
- # page header and before the
- # page code, without special
- # marking. So buffer this amount
- # of lines before printing the
- # page to check for options.
-
- my $maxnondsclinesinheader=1000; # If there is a block of more lines
- # than this in the document
- # header which is not in the
- # "%%BeginProlog...%%EndProlog"
- # or
- # "%%BeginSetup...%%EndSetup"
- # sections, the document is not
- # considered as DSC-conforming
- # and the rest gets passed
- # through to the renderer without
- # further parsing for options.
-
- my $nondsclines = 0; # Amount of lines found which are not in
- # a section (see
- # $maxnondsclinesinheader).
-
- my $nonpslines = 0; # lines before "%!" found yet.
-
- my $more_stuff = 1; # there is more stuff in stdin.
-
- my $linect = 0; # how many lines have we examined?
-
- my $onelinebefore = ""; # The line before the current line
- # (Non-DSC comments are ignored)
-
- my $twolinesbefore = ""; # The line two lines before the current
- # line (Non-DSC comments are ignored)
-
- my $linesafterlastbeginfeature = ""; # All code lines after the last
- # "%%BeginFeature:"
-
- my @psheader = (); # The header of the PostScript file,
- # to be sent after each start of the
- # renderer
-
- my @psfifo = (); # The input FIFO, data which we have
- # pulled from stdin for examination,
- # but not sent to the renderer yet.
-
- my $passthru = 0; # 0: write data into @psfifo; 1: pass
- # data directly to the renderer
-
- my $isdscjob = 0; # Is the job DSC conforming
-
- my $inheader = 1; # Are we still in the header, before
- # first "%%Page:" comment?
-
- my $optionset = 'header'; # Where do the option settings, which
- # we have found, go?
-
- my $optionsalsointoheader = 0; # 1: We are in a "%%BeginSetup...
- # %%EndSetup" section after the first
- # "%%Page:..." line (OpenOffice.org
- # does this and intends the options here
- # apply to the whole document and not
- # only to the current page). We have to
- # add all lines also to the end of the
- # @psheader now and we have to set
- # non-PostScript options also in the
- # "header" optionset. 0: otherwise.
-
- my $nestinglevel = 0; # Are we in the main document (0) or
- # in an embedded document bracketed by
- # "%%BeginDocument" and "%%EndDocument"
- # (>0) We do not parse the PostScript
- # in an embedded document.
-
- my $inpageheader = 0; # Are we in the header of a page,
- # between "%%BeginPageSetup" and
- # "%%EndPageSetup" (1) or not (0).
-
- my $lastpassthru = 0; # State of $passthru in previous line
- # (to allow debug output when $passthru
- # switches.
-
- my $ignorepageheader = 0; # Will be set to 1 as soon as active
- # code (not between "%%BeginPageSetup"
- # and "%%EndPageSetup") appears after a
- # "%%Page:" comment. In this case
- # "%%BeginPageSetup" and
- # "%%EndPageSetup" is not allowed any
- # more on this page and will be ignored.
- # Will be set to 0 when a new "%%Page:"
- # comment appears.
-
- my $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).
-
- $fileconverterhandle = undef; # File handle to the fileconverter process
-
- $fileconverterpid = 0; # PID of the fileconverter process
-
- $rendererhandle = undef; # File handle to the renderer process
-
- $rendererpid = 0; # PID of the renderer process
-
- my $prologfound = 0; # Did we find the
- # "%%BeginProlog...%%EndProlog" section?
-
- my $setupfound = 0; # Did we find the
- # "%%BeginSetup...%%EndSetup" section?
-
- my $pagesetupfound = 0; # special page setup handling needed
-
- my $inprolog = 0; # We are between "%%BeginProlog" and
- # "%%EndProlog".
-
- my $insetup = 0; # We are between "%%BeginSetup" and
- # "%%EndSetup".
-
- my $infeature = 0; # We are between "%%BeginFeature" and
- # "%%EndFeature".
-
- my $postscriptsection = 'jclsetup'; # In which section of the PostScript
- # file are we currently?
-
- $nondsclines = 0; # Number of subsequent lines found which
- # are at a non-DSC-conforming place,
- # between the sections of the header.
-
- my $optionreplaced = 0; # Will be set to 1 when we are in an
- # option ("%%BeginFeature...
- # %%EndFeature") which we have replaced.
-
- $jobhasjcl = 0; # When the job does not start with
- # PostScript directly, but is a
- # PostScript job, we set this to 1
- # to avoid adding the JCL options
- # for the second time.
-
- my $insertoptions = 1; # If we find out that a file with
- # a DSC magic string
- # ("%!PS-Adobe-") is not really
- # DSC-conforming, we insert the
- # options directly after the line
- # with the magic string. We use
- # this variable to store the
- # number of the line with the
- # magic string.
-
- my $currentpage = 0; # The page which we are currently
- # printing.
-
- my $ooo110 = 0; # Flag to work around an application
- # bug.
-
- my $saved = 0; # DSC line not processed yet
-
- if ($dontparse) {
- # We do not parse the PostScript to find Foomatic options, we check
- # only whether we have PostScript.
- $maxlines = 1;
- }
-
- print $logh "Reading PostScript input ...\n";
-
- my $line; # Line to be read from stdin
- do {
- my $ignoreline = 0; # Comment line to be ignored when
- # determining the last active line
- # and the one before the last
-
- if (($printprevpage) || ($saved) || ($line=<STDIN>)) {
- $saved = 0;
-
- if ($linect == $nonpslines) {
- # In the beginning should be the postscript leader,
- # sometimes after some JCL commands
- if ($line !~ m/^.?%!/) { # There can be a Windows control
- # character before "%!"
- $nonpslines ++;
- if ($maxlines == $nonpslines) {
- $maxlines ++;
- }
- $jobhasjcl = 1;
- if ($nonpslines > $maxlinestopsstart) {
- # This is not a PostScript job, we must convert it
- print $logh "${added_lf}Job does not start with \"%!\", " .
- "is it PostScript?\n" .
- "Starting file converter\n";
- # Reset all variables but conserve the data which
- # we have already read.
- $jobhasjcl = 0;
- $linect = 0;
- $nonpslines = 1; # Take into account that the line
- # of this run of the loop will be
- # put into @psheader, so the
- # first line read by the file
- # converter is already the second
- # line.
- $maxlines = 1001;
- $onelinebefore = "";
- $twolinesbefore = "";
- my $alreadyread = join('', @psheader, @psfifo) .
- $line;
- $line = "";
- @psheader = ();
- @psfifo = ();
- # Start the file conversion filter
- if (!$fileconverterpid) {
- ($fileconverterhandle, $fileconverterpid) =
- getfileconverterhandle
- ($dat, $alreadyread);
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error opening file converter",
- $retval);
- }
- } else {
- rip_die("File conversion filter probably " .
- "crashed",
- $EXIT_JOBERR);
- }
- # Read the further data from the file converter and
- # not from STDIN
- if (!close STDIN && $! != $ESPIPE) {
- rip_die ("Couldn't close STDIN",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!open (STDIN, "<&$fileconverterhandle")) {
- rip_die ("Couldn't dup \$fileconverterhandle",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- }
- } else {
- # Do we have a DSC-conforming document?
- if ($line =~ m/^.?%!PS-Adobe-/) {
- # Do not stop parsing the document
- if (!$dontparse) {
- $maxlines = 0;
- $isdscjob = 1;
- $insertoptions = $linect + 1;
- # We have written into @psfifo before,
- # now we continue in @psheader and move
- # over the data which is already in @psfifo
- push (@psheader, @psfifo);
- @psfifo = ();
- }
- print $logh
- "--> This document is DSC-conforming!\n";
- } else {
- # Job is not DSC-conforming, stick in all PostScript
- # option settings in the beginning
- $line .= makeprologsection($dat, $optionset, 1);
- $line .= makesetupsection($dat, $optionset, 1);
- $line .= makepagesetupsection($dat, $optionset, 1);
- $prologfound = 1;
- $setupfound = 1;
- $pagesetupfound = 1;
- }
- }
- } else {
- if ($line =~ /^\%\%/) {
- if ($line =~ m/^\s*\%\%BeginDocument[: ]/) {
- # Beginning of an embedded document
- # Note that Adobe Acrobat has a bug and so uses
- # "%%BeginDocument " instead of "%%BeginDocument:"
- $nestinglevel ++;
- print $logh "Embedded document, " .
- "nesting level now: $nestinglevel\n";
- } elsif (($line =~ m/^\s*\%\%EndDocument/) &&
- ($nestinglevel > 0)) {
- # End of an embedded document
- $nestinglevel --;
- print $logh "End of Embedded document, " .
- "nesting level now: $nestinglevel\n";
- } elsif (($line =~ m/^\s*\%\%Creator[: ](.*)$/) &&
- ($nestinglevel == 0)) {
- # Here we set flags to treat particular bugs of the
- # PostScript produced by certain applications
- my $creator = $1;
- if ($creator =~ /^\s*OpenOffice.org\s+1.1.\d+\s*$/) {
- # OpenOffice.org 1.1.x
- # The option settings supposed to affect the
- # whole document are put into the "%%PageSetup"
- # section of the first page
- print $logh "Document created with " .
- "OpenOffice.org 1.1.x\n";
- $ooo110 = 1;
- }
- } elsif (($line =~ m/^\%\%BeginProlog/) &&
- ($nestinglevel == 0)) {
- # Note: Below is another place where a "Prolog"
- # section start will be considered. There we assume
- # start of the "Prolog" if the job is DSC-Conformimg,
- # but an arbitrary comment starting with "%%Begin", but
- # not a comment explicitly treated here, is found. This
- # is done because many "dvips" (TeX/LaTeX) files miss
- # the "%%BeginProlog" comment.
- # Beginning of Prolog
- print $logh "${added_lf}-----------\nFound: \%\%BeginProlog\n";
- $inprolog = 1;
- $postscriptsection = 'prolog' if $inheader;
- $nondsclines = 0;
- # Insert options for "Prolog"
- if (!$prologfound) {
- $line .= makeprologsection($dat, $optionset, 0);
- }
- $prologfound = 1;
- } elsif (($line =~ m/^\%\%EndProlog/) &&
- ($nestinglevel == 0)) {
- # End of Prolog
- print $logh "Found: \%\%EndProlog\n";
- $inprolog = 0;
- $insertoptions = $linect + 1;
- } elsif (($line =~ m/^\%\%BeginSetup/) &&
- ($nestinglevel == 0)) {
- # Beginning of Setup
- print $logh "${added_lf}-----------\nFound: \%\%BeginSetup\n";
- $insetup = 1;
- # We need to distinguish with the $inheader variable
- # here whether we are in the header or on a page, as
- # OpenOffice.org inserts a "%%BeginSetup...%%EndSetup"
- # section after the first "%%Page:..." line and assumes
- # this section to be valid for all pages.
- $postscriptsection = 'setup' if $inheader;
- $nondsclines = 0;
- if ($inheader) {
- # If there was no "Prolog" but there are
- # options for the "Prolog", push a "Prolog"
- # with these options onto the @psfifo here
- if (!$prologfound) {
- # "Prolog" missing, insert it here
- $line =
- makeprologsection($dat, $optionset, 1) .
- $line;
- # Now we have a "Prolog"
- $prologfound = 1;
- }
- # Insert options for "DocumentSetup" or "AnySetup"
- if ($spooler ne 'cups') {
- # For non-CUPS spoolers or no spooler at all,
- # we leave everything as it is.
- if (!$setupfound) {
- $line .=
- makesetupsection($dat, $optionset, 0);
- }
- $setupfound = 1;
- }
- } else {
- # Found option settings must be stuffed into both
- # the header and the currrent page now. They will
- # be written into both the "header" and the
- # "currentpage" optionsets and the PostScript code
- # lines of this section will not only go into the
- # output stream, but also added to the end of the
- # @psheader, so that they get repeated (to preserve
- # the embedded PostScript option settings) on a
- # restart of the renderer due to command line
- # option changes
- $optionsalsointoheader = 1;
- print $logh "\"%%BeginSetup\" in page header\n";
- }
- } elsif (($line =~ m/^\%\%EndSetup/) &&
- ($nestinglevel == 0)) {
- # End of Setup
- print $logh "Found: \%\%EndSetup\n";
- $insetup = 0;
- if ($inheader) {
- if ($spooler eq 'cups') {
- # In case of CUPS, we must insert the
- # accounting stuff just before the
- # %%EndSetup comment in order to leave any
- # EndPage procedures that have been
- # defined by either the pstops filter or
- # the PostScript job itself fully
- # functional.
- if (!$setupfound) {
- $line = makesetupsection($dat,
- $optionset, 0) .
- $line;
- }
- $setupfound = 1;
- }
- $insertoptions = $linect + 1;
- } else {
- # The "%%BeginSetup...%%EndSetup" which
- # OpenOffice.org has inserted after the first
- # "%%Page:..." line ends here, so the following
- # options go only onto the current page again
- $optionsalsointoheader = 0;
- }
- } elsif (($line =~ m/^\%\%Page:(.*)$/) &&
- ($nestinglevel == 0)) {
- if ((!$lastpassthru) && (!$inheader)) {
- # In the last line we were not in passthru mode,
- # so the last page is not printed. Prepare to do
- # it now.
- $printprevpage = 1;
- # Print the previous page
- $passthru = 1;
- print $logh "New page found but previous not " .
- "printed, print it now.\n";
- } else {
- # The previous page is printed, so we can prepare
- # the current one
- $printprevpage = 0;
- print $logh "${added_lf}-----------\nNew page: $1\n";
- # Count pages
- $currentpage ++;
- # We consider the beginning of the page already as
- # page setup section, as some apps do not use
- # "%%PageSetup" tags.
- $postscriptsection = 'pagesetup';
- # Save PostScript state before beginning the page
- #$line .= "/foomatic-saved-state save def\n";
- # Here begins a new page
- if ($inheader) {
- # Here we add some stuff which still belongs
- # into the header
- my $stillforheader;
- # If there was no "Setup" but there are
- # options for the "Setup", push a "Setup"
- # with these options onto the @psfifo here
- if (!$setupfound) {
- # "Setup" missing, insert it here
- $stillforheader =
- makesetupsection($dat, $optionset, 1) .
- $stillforheader;
- # Now we have a "Setup"
- $setupfound = 1;
- }
- # If there was no "Prolog" but there are
- # options for the "Prolog", push a "Prolog"
- # with these options onto the @psfifo here
- if (!$prologfound) {
- # "Prolog" missing, insert it here
- $stillforheader =
- makeprologsection($dat, $optionset,
- 1) .
- $stillforheader;
- # Now we have a "Prolog"
- $prologfound = 1;
- }
- # Now we push this onto the header
- push (@psheader, $stillforheader);
- # The first page starts, so the header ends
- $inheader = 0;
- $nondsclines = 0;
- # Option setting should go into the
- # page-specific option set now
- $optionset = 'currentpage';
- } else {
- # Restore PostScript state after completing the
- # previous page:
- #
- # foomatic-saved-state restore
- # %%Page: ...
- # /foomatic-saved-state save def
- #
- # Print this directly, so that if we need to
- # restart the renderer for this page due to
- # a command line change this is done under the
- # old instance of the renderer
- #print $rendererhandle
- # "foomatic-saved-state restore\n";
-
- # Save the option settings of the previous page
- copyoptions($dat, 'currentpage',
- 'previouspage');
- deleteoptions($dat, 'currentpage');
- }
- # Initialize the option set
- copyoptions($dat, 'header', 'currentpage');
- # Set command line options which apply only
- # given pages
- setoptionsforpage($dat, 'currentpage', $currentpage);
- $pagesetupfound = 0;
- if ($spooler eq 'cups') {
- # Remove the "notfirst" flag from all options
- # forseen for the "PageSetup" section, because
- # when these are numerical options for CUPS.
- # they have to be set to the correct value
- # for every page
- for my $arg (@{$dat->{'args'}}) {
- if (($arg->{'section'} eq 'PageSetup') &&
- (defined($arg->{'notfirst'}))) {
- delete($arg->{'notfirst'});
- }
- }
- }
- # Insert PostScript option settings
- # (options for section "PageSetup".
- if ($isdscjob) {
- $line .=
- makepagesetupsection($dat, $optionset,
- 0);
- $pagesetupfound = 1;
- }
- # Now the page header comes, so buffer the data,
- # because we must perhaps shut down and restart
- # the renderer
- $passthru = 0;
- $ignorepageheader = 0;
- $optionsalsointoheader = 0;
- }
- } elsif (($line =~ m/^\%\%BeginPageSetup/) &&
- ($nestinglevel == 0) &&
- (!$ignorepageheader)) {
- # Start of the page header, up to %%EndPageSetup
- # nothing of the page will be drawn, page-specific
- # option settngs (as letter-head paper for page 1)
- # go here
- print $logh "${added_lf}Found: \%\%BeginPageSetup\n";
- $passthru = 0;
- $inpageheader = 1;
- $postscriptsection = 'pagesetup';
- if (($ooo110) && ($currentpage == 1)) {
- $optionsalsointoheader = 1;
- } else {
- $optionsalsointoheader = 0;
- }
- } elsif (($line =~ m/^\%\%EndPageSetup/) &&
- ($nestinglevel == 0) &&
- (!$ignorepageheader)) {
- # End of the page header, the page is ready to be
- # printed
- print $logh "Found: \%\%EndPageSetup\n";
- print $logh "End of page header\n";
- # We cannot for sure say that the page header ends here
- # OpenOffice.org puts (due to a bug) a "%%BeginSetup...
- # %%EndSetup" section after the first "%%Page:...". It
- # is possible that CUPS inserts a "%%BeginPageSetup...
- # %%EndPageSetup" before this section, which means that
- # the options in the "%%BeginSetup...%%EndSetup"
- # section are after the "%%EndPageSetup", so we
- # continue for searching options up to the buffer size
- # limit $maxlinesforpageoptions.
- $passthru = 0;
- $inpageheader = 0;
- $optionsalsointoheader = 0;
- } elsif ((($line =~ m/^\%\%(BeginFeature):\s*\*?([^\*\s=]+)\s+()(\S[^\r\n]*)\r?\n?$/) ||
- ($line =~ m/^\s*\%\%\s*(FoomaticRIPOptionSetting):\s*([^\*\s=]+)\s*=\s*(\@?)([^\@\s][^\r\n]*)\r?\n?$/)) &&
- ($nestinglevel == 0) &&
- (!$optionreplaced) &&
- ((!$passthru) || (!$isdscjob))) {
- my ($linetype, $option, $fromcomposite, $value) =
- ($1, $2, $3, $4);
-
- # Mark that we are in a "Feature" section
- if ($linetype eq 'BeginFeature') {
- $infeature = 1;
- $linesafterlastbeginfeature = "";
- }
-
- # OK, we have an option. If it's not a
- # *ostscript-style option (ie, it's command-line or
- # JCL) then we should note that fact, since the
- # attribute-to-filter option passing in CUPS is kind of
- # funky, especially wrt boolean options.
-
- print $logh "Found: $line";
- if (my $arg=argbyname($option)) {
- print $logh " Option: $option=" .
- ($fromcomposite ? "From" : "") . $value;
- if (($spooler eq 'cups') &&
- ($linetype eq 'BeginFeature') &&
- (!defined($arg->{'notfirst'})) &&
- ($arg->{$optionset} ne $value) &&
- (($inheader) ||
- ($arg->{section} eq 'PageSetup'))) {
-
- # We have the first occurence of an option
- # setting and the spooler is CUPS, so this
- # setting is inserted by "pstops" or
- # "imagetops". The value from the command
- # line was not inserted by "pstops" or
- # "imagetops" so it seems to be not under
- # the choices in the PPD. Possible
- # reasons:
- #
- # - "pstops" and "imagetops" ignore settings
- # of numerical or string options which are
- # not one of the choices in the PPD file,
- # and inserts the default value instead.
- #
- # - On the command line an option was applied
- # only to selected pages:
- # "-o <page ranges>:<option>=<values>
- # This is not supported by CUPS, so not
- # taken care of by "pstops".
- #
- # We must fix this here by replacing the
- # setting inserted by "pstops" or "imagetops"
- # with the exact setting given on the command
- # line.
-
- # $arg->{$optionset} is already
- # range-checked, so do not check again here
- # Insert DSC comment
- my $dest = ((($inheader) && ($isdscjob)) ?
- \@psheader : \@psfifo);
- my $val;
- if ($arg->{'style'} eq 'G') {
- # PostScript option, insert the code
- if ($arg->{'type'} eq 'bool') {
- # Boolean option
- push(@{$dest},
- "%%BeginFeature: *$option " .
- ($arg->{$optionset} == 1 ?
- "True" : "False") . "\n");
- if (defined($arg->{$optionset}) &&
- $arg->{$optionset} == 1) {
- push(@{$dest}, $arg->{'proto'} .
- "\n");
- } elsif ($arg->{'protof'}) {
- push(@{$dest}, $arg->{'protof'} .
- "\n");
- }
- } elsif ((($arg->{'type'} eq 'enum') ||
- ($arg->{'type'} eq 'string') ||
- ($arg->{'type'} eq
- 'password')) &&
- (defined($val =
- $arg->{'vals_byname'}{$arg->{$optionset}}))) {
- # Enumerated choice of string or enum
- # option
- push(@{$dest},
- "%%BeginFeature: " .
- "*$option $arg->{$optionset}\n");
- push(@{$dest}, $val->{'driverval'} . "\n");
- } elsif ((($arg->{'type'} eq 'string') ||
- ($arg->{'type'} eq
- 'password')) &&
- ($arg->{$optionset} eq 'None')) {
- # 'None' is mapped to the empty string
- # in string options
- push(@{$dest},
- "%%BeginFeature: " .
- "*$option $arg->{$optionset}\n");
- my $driverval = $arg->{'proto'};
- $driverval =~ s/\%s//g;
- push(@{$dest}, $driverval . "\n");
- } else {
- # Setting for numerical or string
- # option which is not under the
- # enumerated choices
- push(@{$dest},
- "%%BeginFeature: " .
- "*$option $arg->{$optionset}\n");
- my $sprintfproto = $arg->{'proto'};
- $sprintfproto =~ s/\%(?!s)/\%\%/g;
- push(@{$dest},
- sprintf($sprintfproto,
- $arg->{$optionset}) .
- "\n");
- }
- } else {
- # Command line or JCL option
- push(@{$dest},
- "%% FoomaticRIPOptionSetting: " .
- "$option=$arg->{$optionset}\n");
- }
- print $logh " --> Correcting numerical/string " .
- "option to $option=$arg->{$optionset}" .
- " (Command line argument)\n";
- # We have replaced this option on the
- # FIFO
- $optionreplaced = 1;
- }
- # Mark that we have already found this option
- $arg->{'notfirst'} = 1;
- if (!$optionreplaced) {
- if ($arg->{'style'} ne 'G') {
- # "Controlled by '<Composite>'" setting of
- # a member option of a composite option
- if ($fromcomposite) {
- $value = "From$value";
- }
- # Non-PostScript option
- # Check whether it is valid
- if (defined(my $newvalue =
- checkoptionvalue($dat, $option,
- $value, 0))) {
- print $logh " --> Setting option\n";
- # Valid choice, set it.
- $arg->{$optionset} = $newvalue;
- if ($optionsalsointoheader) {
- $arg->{'header'} = $newvalue;
- }
- if (($arg->{'type'} eq 'enum') &&
- (($option eq 'PageSize') ||
- ($option eq 'PageRegion')) &&
- ($newvalue =~ /^Custom/) &&
- ($linetype eq
- 'FoomaticRIPOptionSetting')) {
- # Custom page size
- $linesafterlastbeginfeature =~
- /^[\s\r\n]*([\d\.]+)[\s\r\n]+([\d\.]+)[\s\r\n]+/s;
- my ($w, $h) = ($1, $2);
- if (($w) && ($h) &&
- ($w != 0) && ($h != 0)) {
- $newvalue =
- "$newvalue.${w}x$h";
- $arg->{$optionset} = $newvalue;
- if ($optionsalsointoheader) {
- $arg->{'header'} =
- $newvalue;
- }
- }
- }
- # For a composite option insert the
- # code from the member options with
- # current setting "From<composite>"
- # The code from the member options
- # is chosen according to the setting
- # of the composite option.
- if (($arg->{'style'} eq 'X') &&
- ($linetype eq
- 'FoomaticRIPOptionSetting')) {
- buildcommandline($dat, $optionset);
- $line .=
- $arg->{$postscriptsection};
- }
- # If this argument is PageSize or
- # PageRegion, also set the other
- syncpagesize($dat, $option, $newvalue,
- $optionset);
- if ($optionsalsointoheader) {
- syncpagesize($dat, $option,
- $newvalue, 'header');
- }
- } else {
- # Invalid option, log it.
- print $logh " --> Invalid option " .
- "setting found in job\n";
- }
- } elsif ($fromcomposite) {
- # PostScript option, but we have to look up
- # the PostScript code to be inserted from
- # the setting of a composite option, as
- # this option is set to "Controlled by
- # '<Composite>'".
- # Set the option
- if (defined(my $newvalue =
- checkoptionvalue
- ($dat, $option,
- "From$value", 0))) {
- print $logh " --> Looking up setting " .
- "in composite option '$value'\n";
- # Valid choice, set it.
- $arg->{$optionset} = $newvalue;
- if ($optionsalsointoheader) {
- $arg->{'header'} = $newvalue;
- }
- # Update composite options
- buildcommandline($dat, $optionset);
- # Substitute PostScript comment by
- # the real code
- $line = $arg->{'compositesubst'};
- } else {
- # Invalid option, log it.
- print $logh " --> Invalid option " .
- "setting found in job\n";
- }
- } else {
- # it is a PostScript style option with
- # the code readily inserted, no option
- # for the renderer command line/JCL to set,
- # no lookup of a composite option needed,
- # so nothing to do here...
- print $logh
- " --> Option will be set by " .
- "PostScript interpreter\n";
- }
- }
- } else {
- # This option is unknown to us. WTF?
- print $logh "Unknown option $option=$value found " .
- "in the job\n";
- }
- } elsif (($line =~ m/^\%\%EndFeature/) &&
- ($nestinglevel == 0)) {
- # End of Feature
- $infeature = 0;
- # If the option setting was replaced, it ends here,
- # too, and the next option is not necessarily also
- # replaced.
- $optionreplaced = 0;
- $linesafterlastbeginfeature = "";
- } elsif (($line =~ m/^\%\%Begin/) &&
- ($isdscjob) &&
- (!$prologfound) &&
- ($nestinglevel == 0)) {
- # In some PostScript files (especially when generated
- # by "dvips" of TeX/LaTeX) the "%%BeginProlog" is
- # missing, so assume that it was before the current
- # line (the first line starting with "%%Begin".
- print $logh "Job claims to be DSC-conforming, but " .
- "\"%%BeginProlog\" was missing before first " .
- "line with another \"%%Begin...\" comment " .
- "(is this a TeX/LaTeX/dvips-generated PostScript " .
- "file?). Assuming start of \"Prolog\" here.\n";
- # Beginning of Prolog
- $inprolog = 1;
- $nondsclines = 0;
- # Insert options for "Prolog" before the current line
- if (!$prologfound) {
- $line =
- "%%BeginProlog\n" .
- makeprologsection($dat, $optionset, 0) .
- $line;
- }
- $prologfound = 1;
- } elsif (($line =~ m/^\s*\%/) || ($line =~ m/^\s*$/)) {
- # This is an unknown PostScript comment or a blank
- # line, no active code
- $ignoreline = 1;
- }
- } else {
- # This line is active PostScript code
- if ($infeature) {
- # Collect coe in a "%%BeginFeature: ... %%EndFeature"
- # section, to get the values for a custom option
- # setting
- $linesafterlastbeginfeature .= $line;
- }
- if ($inheader) {
- if ((!$inprolog) && (!$insetup)) {
- # Outside the "Prolog" and "Setup" section
- # a correct DSC-conforming document has no
- # active PostScript code, so consider the
- # file as non-DSC-conforming when there are
- # too many of such lines.
- $nondsclines ++;
- if ($nondsclines > $maxnondsclinesinheader) {
- # Consider document as not DSC-conforming
- print $logh "This job seems not to be " .
- "DSC-conforming, DSC-comment for " .
- "next section not found, stopping " .
- "to parse the rest, passing it " .
- "directly to the renderer.\n";
- # Stop scanning for further option settings
- $maxlines = 1;
- $isdscjob = 0;
- # Insert defaults and command line settings
- # in the beginning of the job or after the
- # last valid section
- splice(@psheader, $insertoptions, 0,
- ($prologfound ? () :
- makeprologsection($dat, $optionset,
- 1)),
- ($setupfound ? () :
- makesetupsection($dat, $optionset,
- 1)),
- ($pagesetupfound ? () :
- makepagesetupsection($dat,
- $optionset,
- 1)));
- $prologfound = 1;
- $setupfound = 1;
- $pagesetupfound = 1;
- }
- }
- } else {
- if (!$inpageheader) {
- # PostScript code inside a page, but not between
- # "%%BeginPageSetup" and "%%EndPageSetup", so
- # we are perhaps already drawing onto a page now
- if ($onelinebefore =~ m/^\%\%Page:/) {
- print $logh "No page header or page " .
- "header not DSC-conforming\n";
- }
- # Stop buffering lines to search for options
- # placed not DSC-conforming
- if (scalar(@psfifo) >=
- $maxlinesforpageoptions) {
- print $logh "Stopping search for " .
- "page header options\n";
- $passthru = 1;
- # If there comes a page header now, ignore
- # it
- $ignorepageheader = 1;
- $optionsalsointoheader = 0;
- }
- }
- }
- }
- }
-
- # Debug info
- if ($lastpassthru != $passthru) {
- if ($passthru) {
- print $logh "Found: $line" .
- " --> Output goes directly to the renderer now.\n${added_lf}";
- } else {
- print $logh "Found: $line" .
- " --> Output goes to the FIFO buffer now.${added_lf}\n";
- }
- }
-
- # We are in an option which was replaced, do not output
- # the current line.
- if ($optionreplaced) {
- $line = "";
- }
-
- # If we are in a "%%BeginSetup...%%EndSetup" section after
- # the first "%%Page:..." and the current line belongs to
- # an option setting, we have to copy the line also to the
- # @psheader.
- if (($optionsalsointoheader) &&
- (($infeature) || ($line =~ m/^\%\%EndFeature/))) {
- push (@psheader, $line);
- }
-
- # Store or send the current line
- if (($inheader) && ($isdscjob)) {
- # We are still in the PostScript header, collect all lines
- # in @psheader
- push (@psheader, $line);
- } else {
- if (($passthru) && ($isdscjob)) {
- if (!$lastpassthru) {
- # We enter passthru mode with this line, so the
- # command line can have changed, check it and
- # close the renderer if needed
- if (($rendererpid) &&
- (!optionsequal($dat, 'currentpage',
- 'previouspage', 0))) {
- print $logh "Command line/JCL options " .
- "changed, restarting renderer\n";
- $retval = closerendererhandle
- ($rendererhandle, $rendererpid);
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error closing renderer",
- $retval);
- }
- $rendererpid = 0;
- }
- }
- # Flush @psfifo and send line directly to the renderer
- if (!$rendererpid) {
- # No renderer running, start it
- ($rendererhandle, $rendererpid) =
- getrendererhandle
- ($dat, join('', @psheader, @psfifo));
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error opening renderer",
- $retval);
- }
- # @psfifo is sent out, flush it.
- @psfifo = ();
- }
- if ($#psfifo >= 0) {
- # Send @psfifo to renderer
- print $rendererhandle join('', @psfifo);
- # flush @psfifo
- @psfifo = ();
- }
- # Send line to renderer
- if (!$printprevpage) {
- print $rendererhandle $line;
-
- while ($line=<STDIN>)
- {
- if ($line =~ /^\%\%[A-Za-z\s]{3,}/) {
- print $logh "Found: $line" .
- " --> Continue DSC parsing now.${added_lf}\n";
- $saved = 1;
- last;
- } else {
- print $rendererhandle $line;
- $linect++;
- }
- }
- }
- } else {
- # Push the line onto the stack for later spitting up...
- push (@psfifo, $line);
- }
- }
-
- if (!$printprevpage) {
- $linect++;
- }
-
- } else {
- # EOF!
- $more_stuff = 0;
- # No PostScript header in the whole file? Then it's not
- # PostScript, convert it.
- # We open the file converter here when the file has less
- # lines than the amount which we search for the PostScript
- # header ($maxlinestopsstart).
- if ($linect <= $nonpslines) {
- # This is not a PostScript job, we must convert it
- print $logh "${added_lf}Job does not start with \"%!\", " .
- "is it PostScript?\n" .
- "Starting file converter\n";
- # Reset all variables but conserve the data which
- # we have already read.
- $jobhasjcl = 0;
- $linect = 0;
- $nonpslines = 0;
- $maxlines = 1000;
- $onelinebefore = "";
- $twolinesbefore = "";
- my $alreadyread = join('', @psheader, @psfifo);
- @psheader = ();
- @psfifo = ();
- $line = "";
- # Start the file conversion filter
- if (!$fileconverterpid) {
- ($fileconverterhandle, $fileconverterpid) =
- getfileconverterhandle($dat, $alreadyread);
- if ( defined($retval) and $retval != $EXIT_PRINTED) {
- rip_die ("Error opening file converter",
- $retval);
- }
- } else {
- rip_die("File conversion filter probably " .
- "crashed",
- $EXIT_JOBERR);
- }
- # Read the further data from the file converter and
- # not from STDIN
- if (!close STDIN && $! != $ESPIPE) {
- rip_die ("Couldn't close STDIN",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!open (STDIN, "<&$fileconverterhandle")) {
- rip_die ("Couldn't dup \$fileconverterhandle",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- # Now we have new (converted) stuff in STDIN, so
- # continue in the loop
- $more_stuff = 1;
- }
- }
-
- $lastpassthru = $passthru;
-
- if ((!$ignoreline) && (!$printprevpage)) {
- $twolinesbefore = $onelinebefore;
- $onelinebefore = $line;
- }
-
- } while ((($maxlines == 0) or ($linect < $maxlines)) and
- ($more_stuff != 0));
-
- # Some buffer still containing data? Send it out to the renderer.
- if (($more_stuff != 0) || ($inheader) || ($#psfifo >= 0)) {
- # Flush @psfifo and send the remaining data to the renderer, this
- # only happens with non-DSC-conforming jobs or non-Foomatic PPDs
- if ($more_stuff) {
- print $logh "Stopped parsing the PostScript data, ".
- "sending rest directly to renderer.\n";
- } else {
- print $logh "Flushing FIFO.\n";
- }
- if ($inheader) {
- # No page initialized yet? Copy the "header" option set into the
- # "currentpage" option set, so that the renderer will find the
- # options settings.
- copyoptions($dat, 'header', 'currentpage');
- $optionset = 'currentpage';
- # If not done yet, insert defaults and command line settings
- # in the beginning of the job or after the last valid section
- splice(@psheader, $insertoptions, 0,
- ($prologfound ? () :
- makeprologsection($dat, $optionset, 1)),
- ($setupfound ? () :
- makesetupsection($dat, $optionset, 1)),
- ($pagesetupfound ? () :
- makepagesetupsection($dat, $optionset, 1)));
- $prologfound = 1;
- $setupfound = 1;
- $pagesetupfound = 1;
- }
- if (($rendererpid) &&
- (!optionsequal($dat, 'currentpage',
- 'previouspage', 0))) {
- print $logh "Command line/JCL options " .
- "changed, restarting renderer\n";
- $retval = closerendererhandle
- ($rendererhandle, $rendererpid);
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error closing renderer",
- $retval);
- }
- $rendererpid = 0;
- }
- if (!$rendererpid) {
- ($rendererhandle, $rendererpid) =
- getrendererhandle($dat, join('', @psheader, @psfifo));
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error opening renderer",
- $retval);
- }
- # We have sent @psfifo now
- @psfifo = ();
- }
- if ($#psfifo >= 0) {
- # Send @psfifo to renderer
- print $rendererhandle join('', @psfifo);
- # flush @psfifo
- @psfifo = ();
- }
- # Print the rest of the input data
- if ($more_stuff) {
- while (<STDIN>) {
- print $rendererhandle $_;
- }
- }
- }
-
- # At every "%%Page:..." comment we have saved the PostScript state
- # and we have increased the page number. So if the page number is
- # non-zero we had at least one "%%Page:..." comment and so we have
- # to give a restore the PostScript state.
- #if ($currentpage > 0) {
- # print $rendererhandle "foomatic-saved-state restore\n";
- #}
-
- # Close the renderer
- if ($rendererpid) {
- $retval = closerendererhandle ($rendererhandle, $rendererpid);
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error closing renderer",
- $retval);
- }
- $rendererpid = 0;
- }
-
- # Close the file converter (if it was used)
- if ($fileconverterpid) {
- $retval = closefileconverterhandle
- ($fileconverterhandle, $fileconverterpid);
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error closing file converter",
- $retval);
- }
- $fileconverterpid = 0;
- }
-}
-
-
-## Close the documentation page generator
-if ($docgeneratorpid) {
- $retval = closedocgeneratorhandle
- ($docgeneratorhandle, $docgeneratorpid);
- if ($retval != $EXIT_PRINTED) {
- rip_die ("Error closing documentation page generator",
- $retval);
- }
- $docgeneratorpid = 0;
-}
-
-
-
-## Close last input file
-close STDIN;
-
-
-
-## Only for debugging
-if ($debug && 1) {
- use Data::Dumper;
- local $Data::Dumper::Purity=1;
- local $Data::Dumper::Indent=1;
- print $logh Dumper($dat);
-}
-
-
-
-## The End
-print $logh "${added_lf}Closing foomatic-rip.\n";
-close $logh;
-
-exit $retval;
-
-
-
-## Functions to let foomatic-rip fork to do several tasks in parallel.
-
-# To do the filtering without loading the whole file into memory we work
-# on a data stream, we read the data line by line analyse it to decide what
-# filters to use and start the filters if we have found out which we need.
-# We buffer the data only as long as we didn't determing which filters to
-# use for this piece of data and with which options. There are no temporary
-# files used.
-
-# foomatic-rip splits into up to 6 parallel processes to do the whole
-# filtering (listed in the order of the data flow):
-
-# KID0: Generate documentation pages (only jobs with "docs" option)
-# KID2: Put together already read data and current input stream for
-# feeding into the file conversion filter (only non-PostScript
-# and "docs" jobs)
-# KID1: Run the file conversion filter to convert non-PostScript
-# input into PostScript (only non-PostScript and "docs" jobs)
-# MAIN: Prepare the job auto-detecting the spooler, reading the PPD,
-# extracting the options from the command line, and parsing
-# the job data itself. It analyses the job data to check
-# whether it is PostScript and starts KID1/KID2 if not, it
-# also stuffs PostScript code from option settings into the
-# PostScript data stream. It starts the renderer (KID3/KID4)
-# as soon as it knows its command line and restarts it when
-# page-specific option settings need another command line
-# or different JCL commands.
-# KID3: The rendering process. In most cases GhostScript, "cat"
-# for native PostScript printers with their manufacturer's
-# PPD files.
-# KID4: Put together the JCL commands and the renderer's output
-# and send all that either to STDOUT or pipe it into the
-# command line defined with $postpipe.
-
-## This function runs the renderer command line (and if defined also
-## the postpipe) and returns a file handle for stuffing in the
-## PostScript data.
-sub getrendererhandle {
-
- my ($dat, $prepend) = @_;
-
- print $logh "${added_lf}Starting renderer\n";
-
- # Catch signals
- $retval = $EXIT_PRINTED;
- use sigtrap qw(handler set_exit_prnerr USR1
- handler set_exit_prnerr_noretry USR2
- handler set_exit_engaged TTIN);
-
- # Variables for the kid processes reporting their state
-
- # Set up a pipe for the kids to pass their exit stat to the main process
- pipe KID_MESSAGE, KID_MESSAGE_IN;
-
- # When one kid fails put the exit stat here
- $kidfailed = 0;
-
- # When a kid exits successfully, mark it here
- $kid3finished = 0;
- $kid4finished = 0;
-
- # Build the command line and get the JCL commands
- buildcommandline($dat, 'currentpage');
- my $commandline = $dat->{'currentcmd'};
- my @jclprepend = @{$dat->{'jclprepend'}} if defined $dat->{'jclprepend'};
- my @jclappend = @{$dat->{'jclappend'}} if defined $dat->{'jclappend'};
-
- use IO::Handle;
- pipe KID3_IN, KID3;
- KID3->autoflush(1);
- $kid3 = fork();
- if (!defined($kid3)) {
- close KID3;
- close KID3_IN;
- print $logh "$0: cannot fork for kid3!\n";
- rip_die ("can't fork for kid3",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if ($kid3) {
-
- # we are the parent; return a glob to the filehandle
- close KID3_IN;
-
- # Feed in the PostScript header and the FIFO contents
- print KID3 $prepend;
-
- KID3->flush();
- return ( *KID3, $kid3 );
-
- } else {
- close KID3;
-
- pipe KID4_IN, KID4;
- KID4->autoflush(1);
- $kid4 = fork();
- if (!defined($kid4)) {
- close KID4;
- close KID4_IN;
- print $logh "$0: cannot fork for kid4!\n";
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("can't fork for kid4",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
-
- if ($kid4) {
- # parent, child of primary task; we are |commandline|
- close KID4_IN;
-
- print $logh "renderer PID kid4=$kid4\n";
- print $logh "renderer command: $commandline\n";
-
- if (!close STDIN && $! != $ESPIPE) {
- close KID3_IN;
- close KID4;
- close KID_MESSAGE;
- print KID_MESSAGE_IN
- "3 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("Couldn't close STDIN in $kid4",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!open (STDIN, "<&KID3_IN")) {
- close KID3_IN;
- close KID4;
- close KID_MESSAGE;
- print KID_MESSAGE_IN
- "3 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("Couldn't dup KID3_IN",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!close STDOUT) {
- close KID3_IN;
- close KID4;
- close KID_MESSAGE;
- print KID_MESSAGE_IN
- "3 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("Couldn't close STDOUT in $kid4",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!open (STDOUT, ">&KID4")) {
- close KID3_IN;
- close KID4;
- close KID_MESSAGE;
- print KID_MESSAGE_IN
- "3 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("Couldn't dup KID4",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if ($debug) {
- if (!open (STDERR, ">&$logh")) {
- close KID3_IN;
- close KID4;
- close KID_MESSAGE;
- print KID_MESSAGE_IN
- "3 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("Couldn't dup logh to stderr",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- }
-
- # Massage commandline to execute foomatic-gswrapper
- my $havewrapper = 0;
- for (split(':', $ENV{'PATH'})) {
- if (-x "$_/foomatic-gswrapper") {
- $havewrapper=1;
- last;
- }
- }
- if ($havewrapper) {
- $commandline =~ s!^\s*gs\s!foomatic-gswrapper !g;
- $commandline =~ s!(\|\s*)gs\s!\|foomatic-gswrapper !g;
- $commandline =~ s!(;\s*)gs\s!; foomatic-gswrapper !g;
- }
-
- # If the renderer command line contains the "echo"
- # command, replace the "echo" by the user-chosen $myecho
- # (important for non-GNU systems where GNU echo is in a
- # special path
- $commandline =~ s!^\s*echo\s!$myecho !g;
- $commandline =~ s!(\|\s*)echo\s!\|$myecho !g;
- $commandline =~ s!(;\s*)echo\s!; $myecho !g;
-
- # In debug mode save the data supposed to be fed into the
- # renderer also into a file
- if ($debug) {
- $commandline = "tee -a ${logfile}.ps | ( $commandline )";
- }
-
- # Actually run the thing...
- modern_system("$commandline");
- if ($? != 0) {
- my $rendererretval = $? >> 8;
- print $logh "renderer return value: $rendererretval\n";
- my $renderersignal = $? & 127;
- print $logh "renderer received signal: $rendererretval\n";
- close STDOUT;
- close KID4;
- close STDIN;
- close KID3_IN;
- # Handle signals
- if ($renderersignal == SIGUSR1) {
- $retval = $EXIT_PRNERR;
- } elsif ($renderersignal == SIGUSR2) {
- $retval = $EXIT_PRNERR_NORETRY;
- } elsif ($renderersignal == SIGTTIN) {
- $retval = $EXIT_ENGAGED;
- }
- if ($retval != $EXIT_PRINTED) {
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $retval\n";
- close KID_MESSAGE_IN;
- exit $retval;
- }
- # Evaluate renderer result
- if ($rendererretval == 0) {
- # Success, exit with 0 and inform main process
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_PRINTED\n";
- close KID_MESSAGE_IN;
- exit $EXIT_PRINTED;
- } elsif ($rendererretval == 1) {
- # Syntax error? PostScript error?
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_JOBERR\n";
- close KID_MESSAGE_IN;
- rip_die ("Possible error on renderer command line or PostScript error. Check options.",
- $EXIT_JOBERR);
- } elsif ($rendererretval == 139) {
- # Seems to indicate a core dump
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_JOBERR\n";
- close KID_MESSAGE_IN;
- rip_die ("The renderer may have dumped core.",
- $EXIT_JOBERR);
- } elsif ($rendererretval == 141) {
- # Broken pipe, presumably additional filter interface
- # exited.
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_PRNERR\n";
- close KID_MESSAGE_IN;
- rip_die ("A filter used in addition to the renderer" .
- " itself may have failed.",
- $EXIT_PRNERR);
- } elsif (($rendererretval == 243) || ($retval == 255)) {
- # PostScript error?
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_JOBERR\n";
- close KID_MESSAGE_IN;
- exit $EXIT_JOBERR;
- } else {
- # Unknown error
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_PRNERR\n";
- close KID_MESSAGE_IN;
- rip_die ("The renderer command line returned an" .
- " unrecognized error code $rendererretval.",
- $EXIT_PRNERR);
- }
- }
- close STDOUT;
- close KID4;
- close STDIN;
- close KID3_IN;
- # When arrived here the renderer command line was successful
- # So exit with zero exit value here and inform the main process
- close KID_MESSAGE;
- print KID_MESSAGE_IN "3 $EXIT_PRINTED\n";
- close KID_MESSAGE_IN;
- # Wait for postpipe/output child
- waitpid($kid4, 0);
- print $logh "KID3 finished\n";
- exit $EXIT_PRINTED;
- } else {
- # child, trailing task on the pipe; we write jcl stuff
- close KID4;
- close KID3_IN;
-
- my $fileh = *STDOUT;
-
- # Do we have a $postpipe, if yes, launch the command(s) and
- # point our output into it/them
- if ($postpipe) {
- if (!open PIPE,$postpipe) {
- close KID4_IN;
- close KID_MESSAGE;
- print KID_MESSAGE_IN
- "4 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("cannot execute postpipe $postpipe",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- $fileh = *PIPE;
- }
-
- # Debug output
- print $logh "JCL: " . join("", @jclprepend) . "<job data> ${added_lf}" .
- join("", @jclappend) . "\n";
-
- # wrap the JCL around the job data, if there are any
- # options specified...
- # Should the driver already have inserted JCL commands we merge
- # our JCL header with the one from the driver
- my $driverjcl = 0;
- if ( @jclprepend > 1 ) {
- # JCL header read from renderer output
- my @jclheader = ();
- # Determine magic string of JCL in use (usually "@PJL")
- # For that we take the first part of the second JCL line up
- # to the first space
- if ($jclprepend[1] =~ /^(\S+)/) {
- my $jclstr = $1;
- # Read from the renderer output until the first non-JCL
- # line appears
- while (my $line = <KID4_IN>) {
- push(@jclheader, $line);
- last if ($line !~ /$jclstr/);
- }
- # If we had read at least two lines, at least one is
- # a JCL header, so do the merging
- if (@jclheader > 1) {
- $driverjcl = 1;
- # Discard the first and the last entry of the
- # @jclprepend array, we only need the option settings
- # to merge them in
- pop(@jclprepend);
- shift(@jclprepend);
- # Line after which we insert new JCL commands in the
- # JCL header of the job
- my $insert = 1;
- # Go through every JCL command in @jclprepend
- for my $line (@jclprepend) {
- # Search the command in the JCL header from the
- # driver. As search term use only the string from
- # the beginning of the line to the "=", so the
- # command will also be found when it has another
- # value
- $line =~ /^([^=]+)/;
- my $cmd = $1;
- $cmd =~ s/^\s*(.*?)\s*$/$1/;
- my $cmdfound = 0;
- for (@jclheader) {
- # If the command is there, replace it
- $_ =~ s/$cmd\b.*(\r\n|\n|\r)/$line/ and
- $cmdfound = 1;
- }
- if (!$cmdfound) {
- # If the command is not found, insert it
- if (@jclheader > 2) {
- # @jclheader has more than one line,
- # insert the new command beginning
- # right after the first line and continuing
- # after the previous inserted command
- splice(@jclheader, $insert, 0, $line);
- $insert ++;
- } else {
- # If we have only one line of JCL it
- # is probably something like the
- # "@PJL ENTER LANGUAGE=..." line
- # which has to be in the end, but
- # it also contains the
- # "<esc>%-12345X" which has to be in the
- # beginning of the job. So we split the
- # line right before the $jclstr and
- # append our command to the end of the
- # first part and let the second part
- # be a second JCL line.
- $jclheader[0] =~
- /^(.*?)($jclstr.*(\r\n|\n|\r))/;
- my $first = "$1$line";
- my $second = "$2";
- my $third = $jclheader[1];
- @jclheader = ($first, $second, $third);
- }
- }
- }
- # Now pass on the merged JCL header
- print $fileh @jclheader;
- } else {
- # The driver didn't create a JCL header, simply
- # prepend ours and then pass on the line which we
- # already have read
- print $fileh @jclprepend, @jclheader;
- }
- } else {
- # No merging of JCL header possible, simply prepend it
- print $fileh @jclprepend;
- }
- }
-
- # The rest of the job data
- my $buf;
- while (read(KID4_IN, $buf, 1024)) {
- print $fileh $buf;
- }
-
- # A JCL trailer
- if (( @jclprepend > 1 ) && (!$driverjcl)) {
- print $fileh @jclappend;
- }
-
- if (!close $fileh) {
- close KID4_IN;
- close KID_MESSAGE;
- print KID_MESSAGE_IN
- "4 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_IN;
- rip_die ("error closing $fileh",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- close KID4_IN;
-
- print $logh "tail process done writing data to STDOUT\n";
-
- # Handle signals of the backend interface
- if ($retval != $EXIT_PRINTED) {
- close KID_MESSAGE;
- print KID_MESSAGE_IN "4 $retval\n";
- close KID_MESSAGE_IN;
- exit $retval;
- }
-
- # Successful exit, inform main process
- close KID_MESSAGE;
- print KID_MESSAGE_IN "4 $EXIT_PRINTED\n";
- close KID_MESSAGE_IN;
-
- print $logh "KID4 finished\n";
- exit($EXIT_PRINTED);
- }
- }
-}
-
-
-
-## Close the renderer process and wait until all kid processes finish.
-
-sub closerendererhandle {
-
- my ($rendererhandle, $rendererpid) = @_;
-
- print $logh "${added_lf}Closing renderer\n";
-
- # Do it!
- close $rendererhandle;
-
- # Wait for all kid processes to finish or one kid process to fail
- close KID_MESSAGE_IN;
- while ((!$kidfailed) &&
- !(($kid3finished) &&
- ($kid4finished))) {
- my $message = <KID_MESSAGE>;
- chomp $message;
- if ($message =~ /(\d+)\s+(\d+)/) {
- my $kid_id = $1;
- my $exitstat = $2;
- print $logh "KID$kid_id exited with status $exitstat\n";
- if ($exitstat > 0) {
- $kidfailed = $exitstat;
- } elsif ($kid_id == 3) {
- $kid3finished = 1;
- } elsif ($kid_id == 4) {
- $kid4finished = 1;
- }
- }
- }
-
- close KID_MESSAGE;
-
- # If a kid failed, return the exit stat of this kid
- if ($kidfailed != 0) {
- $retval = $kidfailed;
- }
-
- print $logh "Renderer exit stat: $retval\n";
- # Wait for renderer child
- waitpid($rendererpid, 0);
- print $logh "Renderer process finished\n";
- return ($retval);
-}
-
-
-
-## This function is only used when the input data is not
-## PostScript. Then it runs a filter which converts non-PostScript
-## files into PostScript. The user can choose which filter he wants
-## to use. The filter command line is provided by $fileconverter.
-
-sub getfileconverterhandle {
-
- # Already read data must be converted, too
- my ($dat, $alreadyread) = @_;
-
- print $logh "${added_lf}Starting converter for non-PostScript files\n";
-
- # Determine with which command non-PostScript files are converted
- # to PostScript
- if ($fileconverter eq "") {
- if ($spoolerfileconverters->{$spooler}) {
- $fileconverter = $spoolerfileconverters->{$spooler};
- } else {
- for my $c (@fileconverters) {
- ($c =~ m/^\s*(\S+)\s+/) || ($c = m/^\s*(\S+)$/);
- my $command = $1;
- if( -x $command ){
- $fileconverter = $command;
- } else {
- for (split(':', $ENV{'PATH'})) {
- if (-x "$_/$command") {
- $fileconverter = $c;
- last;
- }
- }
- }
- if ($fileconverter ne "") {
- last;
- }
- }
- }
- if ($fileconverter eq "") {
- $fileconverter = "echo \"Cannot convert file to " .
- "PostScript!\" 1>&2";
- }
- }
-
- # Insert the page size into the $fileconverter
- if ($fileconverter =~ /\@\@([^@]+)\@\@PAGESIZE\@\@/) {
- # We always use the "header" option swt here, with a
- # non-PostScript file we have no "currentpage"
- my $optstr = $1;
- my $arg;
- my $sizestr = (($arg = $dat->{'args_byname'}{'PageSize'})
- ? $arg->{'header'}
- : "");
- if ($sizestr) {
- # Use wider margins so that the pages come out completely on
- # every printer model (especially HP inkjets)
- if ($fileconverter =~ /^\s*(a2ps)\s+/) {
- if (lc($sizestr) eq "letter") {
- $sizestr = "Letterdj";
- } elsif (lc($sizestr) eq "a4") {
- $sizestr = "A4dj";
- }
- }
- $optstr .= $sizestr;
- } else {
- $optstr = "";
- }
- $fileconverter =~ s/\@\@([^@]+)\@\@PAGESIZE\@\@/$optstr/;
- }
-
- # Insert the job title into the $fileconverter
- if ($fileconverter =~ /\@\@([^@]+)\@\@JOBTITLE\@\@/) {
- if ($do_docs) {
- $jobtitle =
- "Documentation for the $model";
- }
- my $titlearg = $1;
- my ($arg, $optstr);
- ($arg = $jobtitle) =~ s/\"/\\\"/g;
- if (($titlearg =~ /\"/) || $arg) {
- $optstr = $titlearg . ($titlearg =~ /\"/ ? '' : '"') .
- ($arg ? "$arg\"" : '"');
- } else {
- $optstr = "";
- }
- $fileconverter =~ s/\@\@([^@]+)\@\@JOBTITLE\@\@/$optstr/;
- }
-
- # Apply "pstops" when having used a file converter under CUPS, so
- # CUPS can stuff the default settings into the PostScript output
- # of the file converter (so all CUPS settings get also applied when
- # one prints the documentation pages (all other files we get
- # already converted to PostScript by CUPS).
- if ($spooler eq 'cups') {
- $fileconverter .=
- " | ${programdir}pstops '$rargs[0]' '$rargs[1]' '$rargs[2]' " .
- "'$rargs[3]' '$rargs[4]'";
- }
-
- # Variables for the kid processes reporting their state
-
- # Set up a pipe for the kids to pass their exit stat to the main process
- pipe KID_MESSAGE_CONV, KID_MESSAGE_CONV_IN;
-
- # When one kid fails put the exit stat here
- $convkidfailed = 0;
-
- # When a kid exits successfully, mark it here
- $kid1finished = 0;
- $kid2finished = 0;
-
- use IO::Handle;
- pipe KID1_IN, KID1;
- KID1->autoflush(1);
- my $kid1 = fork();
- if (!defined($kid1)) {
- close KID1;
- close KID1_IN;
- print $logh "$0: cannot fork for kid1!\n";
- rip_die ("can't fork for kid1",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
-
- if ($kid1) {
-
- # we are the parent; return a glob to the filehandle
- close KID1;
-
- return ( *KID1_IN, $kid1 );
-
- } else {
- # We go on reading the job data and stuff it into the file
- # converter
- close KID1_IN;
-
- pipe KID2_IN, KID2;
- KID2->autoflush(1);
- $kid2 = fork();
- if (!defined($kid2)) {
- print $logh "$0: cannot fork for kid2!\n";
- close KID1;
- close KID2;
- close KID2_IN;
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN
- "1 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- rip_die ("can't fork for kid2",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
-
- if ($kid2) {
- # parent, child of primary task; we are |$fileconverter|
- close KID2;
-
- print $logh "file converter PID kid2=$kid2\n";
- if (($debug) || ($spooler ne 'cups')) {
- print $logh "file converter command: $fileconverter\n";
- }
-
- if (!close STDIN && $! != $ESPIPE) {
- close KID1;
- close KID2_IN;
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN
- "1 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_CONV_IN;
- rip_die ("Couldn't close STDIN in $kid2",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!open (STDIN, "<&KID2_IN")) {
- close KID1;
- close KID2_IN;
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN
- "1 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_CONV_IN;
- rip_die ("Couldn't dup KID2_IN",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!close STDOUT) {
- close KID1;
- close KID2_IN;
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN
- "1 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_CONV_IN;
- rip_die ("Couldn't close STDOUT in $kid2",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if (!open (STDOUT, ">&KID1")) {
- close KID1;
- close KID2_IN;
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN
- "1 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_CONV_IN;
- rip_die ("Couldn't dup KID1",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- if ($debug) {
- if (!open (STDERR, ">&$logh")) {
- close KID1;
- close KID2_IN;
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN
- "1 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_CONV_IN;
- rip_die ("Couldn't dup logh to stderr",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- }
-
- # Actually run the thing...
- modern_system("$fileconverter");
- if ($? != 0) {
- my $fileconverterretval = $? >> 8;
- print $logh "file converter return value: " .
- "$fileconverterretval\n";
- my $fileconvertersignal = $? & 127;
- print $logh "file converter received signal: ".
- "$fileconverterretval\n";
- close STDOUT;
- close KID1;
- close STDIN;
- close KID2_IN;
- # Handle signals
- if ($fileconvertersignal == SIGUSR1) {
- $retval = $EXIT_PRNERR;
- } elsif ($fileconvertersignal == SIGUSR2) {
- $retval = $EXIT_PRNERR_NORETRY;
- } elsif ($fileconvertersignal == SIGTTIN) {
- $retval = $EXIT_ENGAGED;
- }
- if ($retval != $EXIT_PRINTED) {
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN "1 $retval\n";
- close KID_MESSAGE_CONV_IN;
- exit $retval;
- }
- # Evaluate fileconverter result
- if ($fileconverterretval == 0) {
- # Success, exit with 0 and inform main process
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN "1 $EXIT_PRINTED\n";
- close KID_MESSAGE_CONV_IN;
- exit $EXIT_PRINTED;
- } else {
- # Unknown error
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN "1 $EXIT_PRNERR\n";
- close KID_MESSAGE_CONV_IN;
- rip_die ("The file converter command line returned " .
- "an unrecognized error code " .
- "$fileconverterretval.",
- $EXIT_PRNERR);
- }
- }
- close STDOUT;
- close KID1;
- close STDIN;
- close KID2_IN;
- # When arrived here the fileconverter command line was
- # successful.
- # So exit with zero exit value here and inform the main process
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN "1 $EXIT_PRINTED\n";
- close KID_MESSAGE_CONV_IN;
- # Wait for input child
- waitpid($kid1, 0);
- print $logh "KID1 finished\n";
- exit $EXIT_PRINTED;
- } else {
- # child, first part of the pipe, reading in the data from
- # standard input and stuffing it into the file converter
- # after putting in the already read data (in $alreadyread)
- close KID1;
- close KID2_IN;
-
- # At first pass the data which we have already read to the
- # filter
- print KID2 $alreadyread;
- # Then read the rest from standard input
- my $buf;
- while (read(STDIN, $buf, 1024)) {
- print KID2 $buf;
- }
-
- if (!close STDIN && $! != $ESPIPE) {
- close KID2;
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN
- "2 $EXIT_PRNERR_NORETRY_BAD_SETTINGS\n";
- close KID_MESSAGE_CONV_IN;
- rip_die ("error closing STDIN",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- close KID2;
-
- print $logh "tail process done reading data from STDIN\n";
-
- # Successful exit, inform main process
- close KID_MESSAGE_CONV;
- print KID_MESSAGE_CONV_IN "2 $EXIT_PRINTED\n";
- close KID_MESSAGE_CONV_IN;
-
- print $logh "KID2 finished\n";
- exit($EXIT_PRINTED);
- }
- }
-}
-
-
-
-## Close the file conversion process and wait until all kid processes
-## finish.
-
-sub closefileconverterhandle {
-
- my ($fileconverterhandle, $fileconverterpid) = @_;
-
- print $logh "${added_lf}Closing file converter\n";
-
- # Do it!
- close $fileconverterhandle;
-
- # Wait for all kid processes to finish or one kid process to fail
- close KID_MESSAGE_CONV_IN;
- while ((!$convkidfailed) &&
- !(($kid1finished) &&
- ($kid2finished))) {
- my $message = <KID_MESSAGE_CONV>;
- chomp $message;
- if ($message =~ /(\d+)\s+(\d+)/) {
- my $kid_id = $1;
- my $exitstat = $2;
- print $logh "KID$kid_id exited with status $exitstat\n";
- if ($exitstat > 0) {
- $convkidfailed = $exitstat;
- } elsif ($kid_id == 1) {
- $kid1finished = 1;
- } elsif ($kid_id == 2) {
- $kid2finished = 1;
- }
- }
- }
-
- close KID_MESSAGE_CONV;
-
- # If a kid failed, return the exit stat of this kid
- if ($convkidfailed != 0) {
- $retval = $convkidfailed;
- }
-
- print $logh "File converter exit stat: $retval\n";
- # Wait for fileconverter child
- waitpid($fileconverterpid, 0);
- print $logh "File converter process finished\n";
- return ($retval);
-}
-
-
-
-## Generate the documentation page and return a filehandle to get it
-
-sub getdocgeneratorhandle {
-
- # The data structure with the options
- my ($dat) = @_;
-
- print $logh "${added_lf}Generating documentation page for the $model\n";
-
- # Printer queue name
- my $printerstr;
- if ($printer) {
- $printerstr = $printer;
- } else {
- $printerstr = "<printer>";
- }
-
- # Spooler-specific differences
- my ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize);
- if ($spooler eq 'cups') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("lpr -P $printerstr ",
- "-o ", "", "=", "",
- "-o ", "no", "", "=", "",
- "-o ", "", "=", "",
- "-o ", "", "=", "",
- " "," <file>",
- "\n Custom size: -o PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: -o PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'lpd') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("lpr -P $printerstr -J \"",
- "", "", "=", "",
- "", "", "", "=", "",
- "", "", "=", "",
- "", "", "=", "",
- " ", "\" <file>",
- "\n Custom size: PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'gnulpr') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("lpr -P $printerstr ",
- "-o ", "", "=", "",
- "-o ", "", "", "=", "",
- "-o ", "", "=", "",
- "-o ", "", "=", "",
- " "," <file>",
- "\n Custom size: -o PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: -o PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'lprng') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("lpr -P $printerstr ",
- "-Z ", "", "=", "",
- "-Z ", "", "", "=", "",
- "-Z ", "", "=", "",
- "-Z ", "", "=", "",
- " "," <file>",
- "\n Custom size: -Z PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: -Z PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'ppr') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("ppr -d $printerstr --ripopts \"",
- "", "", "=", "",
- "", "", "", "=", "",
- "", "", "=", "",
- "", "", "=", "",
- " ","\" <file>",
- "\n Custom size: PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'ppr-int') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("ppr -d $printerstr -i \"",
- "", "", "=", "",
- "", "", "", "=", "",
- "", "", "=", "",
- "", "", "=", "",
- " ","\" <file>",
- "\n Custom size: PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'cps') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("lpr -P $printerstr ",
- "-o ", "", "=", "",
- "-o ", "", "", "=", "",
- "-o ", "", "=", "",
- "-o ", "", "=", "",
- " "," <file>",
- "\n Custom size: -o PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: -o PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'direct') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("$programname -P $printerstr ",
- "-o ", "", "=", "",
- "-o ", "", "", "=", "",
- "-o ", "", "=", "",
- "-o ", "", "=", "",
- " "," <file>",
- "\n Custom size: -o PageSize=Custom." .
- "<width>x<height>[<unit>]\n" .
- " Units: pt (default), in, cm, mm\n" .
- " Example: -o PageSize=Custom.4.0x6.0in\n");
- } elsif ($spooler eq 'pdq') {
- ($command,
- $enumopt, $enumoptleft, $enumoptequal, $enumoptright,
- $boolopt, $booloptfalseprefix, $booloptleft, $booloptequal,
- $booloptright,
- $numopt, $numoptleft, $numoptequal, $numoptright,
- $stropt, $stroptleft, $stroptequal, $stroptright,
- $optsep, $trailer, $custompagesize) =
- ("pdq -P $printerstr ",
- "-o", "", "_", "",
- "-o", "no", "", "_", "",
- "-a", "", "=", "",
- "-a", "", "=", "",
- " "," <file>",
- "\n" .
- "Option 'PageWidth':\n".
- " Page Width (for \"Custom\" page size)\n" .
- " A floating point number argument\n" .
- " Range: 0 <= x <= 100000\n" .
- " Example: -aPageWidth=123.4\n" .
- "\n" .
- "Option 'PageHeight':\n" .
- " Page Height (for \"Custom\" page size)\n" .
- " A floating point number argument\n" .
- " Range: 0 <= x <= 100000\n" .
- " Example: -aPageHeight=234.5\n" .
- "\n" .
- "Option 'PageSizeUnit':\n" .
- " Unit (for \"Custom\" page size)\n" .
- " An enumerated choice argument\n" .
- " Possible choices:\n" .
- " o -oPageSizeUnit_pt: Points (1/72 inch)\n" .
- " o -oPageSizeUnit_in: Inches\n" .
- " o -oPageSizeUnit_cm: cm\n" .
- " o -oPageSizeUnit_mm: mm\n" .
- " Example: -oPageSizeUnit_mm\n");
- }
-
- # Variables for the kid processes reporting their state
-
- # Set up a pipe for the kids to pass their exit stat to the main process
- pipe KID_MESSAGE_DOC, KID_MESSAGE_DOC_IN;
-
- # When the kid fails put the exit stat here
- $dockidfailed = 0;
-
- # When the kid exits successfully, mark it here
- $kid0finished = 0;
-
- use IO::Handle;
- pipe KID0_IN, KID0;
- KID0->autoflush(1);
- my $kid0 = fork();
- if (!defined($kid0)) {
- close KID0;
- close KID0_IN;
- print $logh "$0: cannot fork for kid0!\n";
- rip_die ("can't fork for kid0",
- $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
-
- if ($kid0) {
- # we are the parent; return a glob to the filehandle
- close KID0;
- print $logh "Documentation page generator PID kid0=$kid0\n";
- return ( *KID0_IN, $kid0 );
- }
-
- # we are the kid; we generate the documentation page
-
- close KID0_IN;
-
- # Kill data on STDIN to satisfy PPR
- if (($spooler eq 'ppr_int') || ($spooler eq 'ppr')) {
- while (my $dummy = <STDIN>) {};
- }
- close STDIN
- or print $logh "Error closing STDIN for docs print\n";
-
- # write the job into KID0
- select KID0;
-
- print "\nInvokation summary for the $model\n\n";
- print "Use the following command line:\n\n";
- if ($booloptfalseprefix) {
- # I think that what you want to indicate is that the prefix for a false
- # boolean has this form: xxx [no]<switch> or something similar
- print " ${command}${enumopt}${enumoptleft}<option>" .
- "${enumoptequal}<choice>${enumoptright}${optsep}" .
- "${boolopt}${booloptleft}\[${booloptfalseprefix}\]<switch>" .
- "${booloptright}${optsep}" .
- "${numopt}${numoptleft}<num. option>${numoptequal}" .
- "<value>${numoptright}${optsep}" .
- "${stropt}${stroptleft}<string option>${stroptequal}" .
- "<string>${stroptright}" .
- "${trailer}\n\n";
- } else {
- print " ${command}${enumopt}${enumoptleft}<option>" .
- "${enumoptequal}<choice>${enumoptright}${optsep}" .
- "${boolopt}${booloptleft}<switch>${booloptequal}" .
- "<True/False>${booloptright}${optsep}" .
- "${numopt}${numoptleft}<num. option>${numoptequal}" .
- "<value>${numoptright}${optsep}" .
- "${stropt}${stroptleft}<string option>${stroptequal}" .
- "<string>${stroptright}" .
- "${trailer}\n\n";
- }
-
- print "The following options are available for this printer:\n\n";
-
- for my $arg (@{$dat->{'args'}}) {
- my ($name,
- $type,
- $comment,
- $spot,
- $default) = ($arg->{'name'},
- $arg->{'type'},
- $arg->{'comment'},
- $arg->{'spot'},
- $arg->{'default'});
-
- # Is this really an option? Otherwise skip it.
- next if (!$type);
-
- # We don't need "PageRegion", we have "PageSize"
- next if ($name eq "PageRegion");
-
- # Skip enumerated choice options with only one choice
- next if (($type eq 'enum') && ($#{$arg->{'vals'}} < 1));
-
- my $commentstr = "";
- if ($comment) {
- $commentstr = " $comment\n";
- }
-
- my $typestr;
- if ($type eq "enum") {
- $typestr = "An enumerated choice";
- } elsif ($type eq "bool") {
- $typestr = "A boolean";
- } elsif ($type eq "int") {
- $typestr = "An integer number";
- } elsif ($type eq "float") {
- $typestr = "A floating point number";
- } elsif (($type eq "string") || ($type eq "password")) {
- $typestr = "A string";
- }
-
- print "Option '$name':\n$commentstr $typestr argument\n";
- print " This options corresponds to a JCL command\n" if ($arg->{'style'} eq 'J');
-
- if ($type eq 'bool') {
- print " Possible choices:\n";
- if ($booloptfalseprefix) {
- print " o $name: $arg->{'comment_true'}\n";
- print " o $booloptfalseprefix$name: " .
- "$arg->{'comment_false'}\n";
- if (defined($default)) {
- my $defstr = ($default ? "" : "$booloptfalseprefix");
- print " Default: $defstr$name\n";
- }
- print " Example: ${boolopt}${booloptleft}${name}" .
- "${booloptright}\n";
- } else {
- print " o True: $arg->{'comment_true'}\n";
- print " o False: $arg->{'comment_false'}\n";
- if (defined($default)) {
- my $defstr = ($default ? "True" : "False");
- print " Default: $defstr\n";
- }
- print " Example: ${boolopt}${booloptleft}${name}" .
- "${booloptequal}True${booloptright}\n";
- }
- } elsif ($type eq 'enum') {
- print " Possible choices:\n";
- my $exarg;
- my $havecustomsize = 0;
- for (@{$arg->{'vals'}}) {
- my ($choice, $comment) = ($_->{'value'}, $_->{'comment'});
- print " o $choice: $comment\n";
- if (($name eq "PageSize") && ($choice eq "Custom")) {
- $havecustomsize = 1;
- }
- $exarg=$choice;
- }
- if (defined($default)) {
- print " Default: $default\n";
- }
- print " Example: ${enumopt}${enumoptleft}${name}" .
- "${enumoptequal}${exarg}${enumoptright}\n";
- if ($havecustomsize) {
- print $custompagesize;
- }
- } elsif ($type eq 'int' or $type eq 'float') {
- my ($max, $min) = ($arg->{'max'}, $arg->{'min'});
- my $exarg;
- if (defined($max)) {
- print " Range: $min <= x <= $max\n";
- $exarg=$max;
- }
- if (defined($default)) {
- print " Default: $default\n";
- $exarg=$default;
- }
- if (!$exarg) { $exarg=0; }
- print " Example: ${numopt}${numoptleft}${name}" .
- "${numoptequal}${exarg}${numoptright}\n";
- } elsif ($type eq 'string' or $type eq 'password') {
- my $maxlength = $arg->{'maxlength'};
- if (defined($maxlength)) {
- print " Maximum length: $maxlength characters\n";
- }
- if (defined($default)) {
- print " Default: $default\n";
- }
- print " Examples/special settings:\n";
- for (@{$arg->{'vals'}}) {
- my ($value, $comment, $driverval, $proto) =
- ($_->{'value'}, $_->{'comment'}, $_->{'driverval'},
- $arg->{'proto'});
- # Retrieve the original string from the prototype
- # and the driverval
- my $string;
- if ($proto) {
- my $s = index($proto, '%s');
- my $l = length($driverval) - length($proto) + 2;
- if (($s < 0) || ($l < 0)) {
- $string = $driverval;
- } else {
- $string = substr($driverval, $s, $l);
- }
- } else {
- $string = $driverval;
- }
- print " o ${stropt}${stroptleft}${name}" .
- "${stroptequal}${value}${stroptright}";
- if (($value ne $string) || ($comment ne $value)) {
- print " (";
- }
- if ($value ne $string) {
- if ($string eq '') {
- print "blank string";
- } else {
- print "\"$string\"";
- }
- }
- if (($value ne $string) && ($comment ne $value)) {
- print ", ";
- }
- if ($value ne $comment) {
- print "$comment";
- }
- if (($value ne $string) || ($comment ne $value)) {
- print ")";
- }
- print "\n";
- }
- }
-
- print "\n";
- }
-
- select STDOUT;
- close KID0
- or print $logh "Error closing KID0 for docs print\n";
- close STDOUT
- or print $logh "Error closing STDOUT for docs print\n";
-
- # Finished successfully, inform main process
- close KID_MESSAGE_DOC;
- print KID_MESSAGE_DOC_IN "0 $EXIT_PRINTED\n";
- close KID_MESSAGE_DOC_IN;
-
- print $logh "KID0 finished\n";
- exit($EXIT_PRINTED);
-
-}
-
-
-
-## Close the documentation page generation process and wait until the
-## kid process finishes.
-
-sub closedocgeneratorhandle {
-
- my ($handle, $pid) = @_;
-
- print $logh "${added_lf}Closing documentation page generator\n";
-
- # Do it!
- close $handle;
-
- # Wait for the kid process to finish or the kid process to fail
- close KID_MESSAGE_DOC_IN;
- while ((!$dockidfailed) &&
- (!$kid0finished)) {
- my $message = <KID_MESSAGE_DOC>;
- chomp $message;
- if ($message =~ /(\d+)\s+(\d+)/) {
- my $kid_id = $1;
- my $exitstat = $2;
- print $logh "KID$kid_id exited with status $exitstat\n";
- if ($exitstat > 0) {
- $dockidfailed = $exitstat;
- } elsif ($kid_id eq "0") {
- $kid0finished = 1;
- }
- }
- }
-
- close KID_MESSAGE_DOC;
-
- # If the kid failed, return the exit stat of the kid
- if ($dockidfailed != 0) {
- $retval = $dockidfailed;
- }
-
- print $logh "Documentation page generator exit stat: $retval\n";
- # Wait for fileconverter child
- waitpid($pid, 0);
- print $logh "Documentation page generator process finished\n";
- return ($retval);
-}
-
-
-
-# Find an argument by name in a case-insensitive way
-sub argbyname {
- my $name = $_[0];
-
- for my $arg (@{$dat->{'args'}}) {
- return $arg if (lc($name) eq lc($arg->{'name'}));
- }
-
- return undef;
-}
-
-sub valbyname {
- my ($arg,$name) = @_;
-
- for my $val (@{$arg->{'vals'}}) {
- return $val if (lc($name) eq lc($val->{'value'}));
- }
-
- return undef;
-}
-
-# Write a Good-Bye letter and clean up before committing suicide (send
-# error message to caller)
-
-sub rip_die {
- my ($message, $exitstat) = @_;
- my $errmsg = "$!";
- my $errcod = $! + 0;
-
- # Close the documentation page generator (if it was used)
- if ($docgeneratorpid) {
- if ($kid0) {
- print $logh "Killing process $kid0 (KID0)\n";
- kill(9, $kid0);
- }
- $docgeneratorpid = 0;
- }
-
- # Close the file converter (if it was used)
- if ($fileconverterpid) {
- if ($kid2) {
- print $logh "Killing process $kid2 (KID2)\n";
- kill(9, $kid2);
- }
- if ($kid1) {
- print $logh "Killing process $kid1 (KID1)\n";
- kill(9, $kid1);
- }
- $fileconverterpid = 0;
- }
-
- # Close the renderer
- if ($rendererpid) {
- if ($kid4) {
- print $logh "Killing process $kid4 (KID4)\n";
- kill(9, $kid4);
- }
- if ($kid3) {
- print $logh "Killing process $kid3 (KID3)\n";
- kill(9, $kid3);
- }
- $rendererpid = 0;
- }
-
- print $logh "Process dying with \"$message\", exit stat: $exitstat\n\terror: $errmsg ($errcod)\n";
- if ($spooler eq 'ppr_int') {
- # Special error handling for PPR intefaces
- $message =~ s/\\/\\\\/;
- $message =~ s/\"/\\\"/;
- my @messagelines = split("\n", $message);
- my $firstline = "TRUE";
- for my $line (@messagelines) {
- modern_system("lib/alert $printer $firstline \"$line\"");
- $firstline = "FALSE";
- }
- } else {
- print STDERR $message . "\n";
- }
- if ($debug) {
- use Data::Dumper;
- local $Data::Dumper::Purity=1;
- local $Data::Dumper::Indent=1;
- print $logh Dumper($dat);
- }
- exit $exitstat;
-}
-
-# Signal handling routines
-
-sub set_exit_prnerr {
- $retval = $EXIT_PRNERR;
-}
-
-sub set_exit_prnerr_noretry {
- $retval = $EXIT_PRNERR_NORETRY;
-}
-
-sub set_exit_engaged {
- $retval = $EXIT_ENGAGED;
-}
-
-# Read the config file
-
-sub readConfFile {
- my ($file) = @_;
-
- my %conf;
- # Read config file if present
- if (open CONF, "< $file") {
- while (<CONF>)
- {
- $conf{$1}="$2" if (m/^\s*([^\#\s]\S*)\s*:\s*(.*?)\s*$/);
- }
- close CONF;
- }
-
- return %conf;
-}
-
-sub removeunprintables {
- # Remove unprintable characters
- my $str = $_[0];
- $str =~ s/[\x00-\x1f]//g;
- return $str;
-}
-
-sub removeshellescapes {
- # Remove shell escape characters
- my $str = $_[0];
- $str =~ s/[\|<>&!\$\'\"\#\*\?\(\)\[\]\{\}]//g;
- return $str;
-}
-
-sub removespecialchars {
- # Remove unprintable and shell escape characters
- return removeshellescapes(removeunprintables($_[0]));
-}
-
-sub unhtmlify {
- my $str = $_[0];
-
- # Replace HTML/XML entities by the original characters
- $str =~ s/\&apos;/\'/g;
- $str =~ s/\&quot;/\"/g;
- $str =~ s/\&gt;/\>/g;
- $str =~ s/\&lt;/\</g;
- $str =~ s/\&amp;/\&/g;
-
- # Replace special entities by job data
- $str =~ s/\&job;/$jobid/g;
- $str =~ s/\&user;/$jobuser/g;
- $str =~ s/\&host;/$jobhost/g;
- $str =~ s/\&title;/$jobtitle/g;
- $str =~ s/\&copies;/$copies/g;
- $str =~ s/\&options;/$optstr/g;
-
- my ($sec, $min, $hour, $mday, $mon, $year) = (localtime)[0..5];
- my $yearstr = sprintf("%04d", $year + 1900);
- my $monstr = sprintf("%02d", $mon + 1);
- my $mdaystr = sprintf("%02d", $mday);
- my $hourstr = sprintf("%02d", $hour);
- my $minstr = sprintf("%02d", $min);
- my $secstr = sprintf("%02d", $sec);
-
- $str =~ s/\&year;/$yearstr/g;
- $str =~ s/\&month;/$monstr/g;
- $str =~ s/\&date;/$mdaystr/g;
- $str =~ s/\&hour;/$hourstr/g;
- $str =~ s/\&min;/$minstr/g;
- $str =~ s/\&sec;/$secstr/g;
-
- return $str;
-}
-
-sub unhexify {
- # Replace hex notation for unprintable characters in PPD files
- # by the actual characters ex: "<0A>" --> chr(hex("0A"))
- my ($input) = @_;
- my $output = "";
- my $hexmode = 0;
- my $firstdigit = "";
- for (my $i = 0; $i < length($input); $i ++) {
- my $c = substr($input, $i, 1);
- if ($hexmode) {
- if ($c eq ">") {
- # End of hex string
- $hexmode = 0;
- } elsif ($c =~ /^[0-9a-fA-F]$/) {
- # Hexadecimal digit, two of them give a character
- if ($firstdigit ne "") {
- $output .= chr(hex("$firstdigit$c"));
- $firstdigit = "";
- } else {
- $firstdigit = $c;
- }
- }
- } else {
- if ($c eq "<") {
- # Beginning of hex string
- $hexmode = 1;
- } else {
- # Normal character
- $output .= $c;
- }
- }
- }
- return $output;
-}
-
-sub undossify( $ ) {
- # Remove "dossy" line ends ("\r\n") from a string
- my $str = $_[0];
- $str =~ s/\r\n/\n/gs;
- $str =~ s/\r$//s;
- return( $str );
-}
-
-sub checkarg {
- # Check if there is already an argument record $argname in $dat, if not,
- # create one
- my ($dat, $argname) = @_;
- return if defined($dat->{'args_byname'}{$argname});
- # argument record
- my $rec;
- $rec->{'name'} = $argname;
- # Insert record in 'args' array for browsing all arguments
- push(@{$dat->{'args'}}, $rec);
- # 'args_byname' hash for looking up arguments by name
- $dat->{'args_byname'}{$argname} = $dat->{'args'}[$#{$dat->{'args'}}];
- # Default execution style is 'G' (PostScript) since all arguments for
- # which we don't find "*Foomatic..." keywords are usual PostScript
- # options
- $dat->{'args_byname'}{$argname}{'style'} = 'G';
- # Default prototype for code to insert, used by enum options
- $dat->{'args_byname'}{$argname}{'proto'} = '%s';
- # stop Perl nattering about undefined to string comparisons
- $dat->{'args_byname'}{$argname}{'type'} = '';
- print $logh "Added option $argname\n";
-}
-
-sub checksetting {
- # Check if there is already an choice record $setting in the $argname
- # argument in $dat, if not, create one
- my ($dat, $argname, $setting) = @_;
- return if
- defined($dat->{'args_byname'}{$argname}{'vals_byname'}{$setting});
- # setting record
- my $rec;
- $rec->{'value'} = $setting;
- # Insert record in 'vals' array for browsing all settings
- push(@{$dat->{'args_byname'}{$argname}{'vals'}}, $rec);
- # 'vals_byname' hash for looking up settings by name
- $dat->{'args_byname'}{$argname}{'vals_byname'}{$setting} =
- $dat->{'args_byname'}{$argname}{'vals'}[$#{$dat->{'args_byname'}{$argname}{'vals'}}];
-}
-
-sub removearg {
- # remove the argument record $argname from $dat
- my ($dat, $argname) = @_;
- return if !defined($dat->{'args_byname'}{$argname});
- # Remove 'args_byname' hash for looking up arguments by name
- delete $dat->{'args_byname'}{$argname};
- # Remove argument itself
- for (my $i = 0; $i <= $#{$dat->{'args'}}; $i ++) {
- if ($dat->{'args'}[$i]{'name'} eq $argname) {
- print $logh "Removing option " .
- $argname . "\n";
- splice(@{$dat->{'args'}}, $i, 1);
- last;
- }
- }
-}
-
-sub removepsargs {
- # remove all records of PostScript arguments from $dat
- my ($dat) = @_;
- return if !defined($dat);
- for (my $i = 0; $i <= $#{$dat->{'args'}}; $i ++) {
- if ($dat->{'args'}[$i]{'style'} eq 'G') {
- print $logh "Removing PostScript option " .
- $dat->{'args'}[$i]{'name'} . "\n";
- # Remove 'args_byname' hash for looking up arguments by name
- delete $dat->{'args_byname'}{$dat->{'args'}[$i]{'name'}};
- # Remove argument itself
- splice(@{$dat->{'args'}}, $i, 1);
- $i --;
- }
- }
-}
-
-sub checkoptionvalue {
-
- ## This function checks whether a given value is valid for a given
- ## option. If yes, it returns a cleaned value (e. g. always 0 or 1
- ## for boolean options), otherwise "undef". If $forcevalue is set,
- ## we always determine a corrected value to insert (we never return
- ## "undef").
-
- # Is $value valid for the option named $argname?
- my ($dat, $argname, $value, $forcevalue) = @_;
-
- # Record for option $argname
- my $arg = $dat->{'args_byname'}{$argname};
- $arg->{'type'} = '' if not defined $arg->{'type'};
-
- if ($arg->{'type'} eq 'bool') {
- my $lcvalue = lc($value);
- if ((($lcvalue) eq 'true') ||
- (($lcvalue) eq 'on') ||
- (($lcvalue) eq 'yes') ||
- (($lcvalue) eq '1')) {
- return 1;
- } elsif ((($lcvalue) eq 'false') ||
- (($lcvalue) eq 'off') ||
- (($lcvalue) eq 'no') ||
- (($lcvalue) eq '0')) {
- return 0;
- } elsif ($forcevalue) {
- # This maps Unknown to mean False. Good? Bad?
- # It was done so in Foomatic 2.0.x, too.
- my $name = $arg->{'name'};
- print $logh
- "The value $value for $name is not a " .
- "choice!\n" .
- " --> Using False instead!\n";
- return 0;
- }
- } elsif ($arg->{'type'} eq 'enum') {
- if ($value =~ /^None$/i) {
- return 'None';
- } elsif (defined($arg->{'vals_byname'}{$value})) {
- return $value;
- } elsif ((($arg->{'name'} eq "PageSize") ||
- ($arg->{'name'} eq "PageRegion")) &&
- (defined($arg->{'vals_byname'}{'Custom'})) &&
- ($value =~ m!^Custom\.([\d\.]+)x([\d\.]+)([A-Za-z]*)$!)) {
- # Custom paper size
- return $value;
- } elsif ($forcevalue) {
- # wtf!? that's not a choice!
- my $name = $arg->{'name'};
- # Return the first entry of the list
- my $firstentry = $arg->{'vals'}[0]{'value'};
- print $logh
- "The value $value for $name is not a " .
- "choice!\n" .
- " --> Using $firstentry instead!\n";
- return $firstentry;
- }
- } elsif (($arg->{'type'} eq 'int') ||
- ($arg->{'type'} eq 'float')) {
- if (($value <= $arg->{'max'}) &&
- ($value >= $arg->{'min'})) {
- if ($arg->{'type'} eq 'int') {
- return POSIX::floor($value);
- } else {
- return $value;
- }
- } elsif ($forcevalue) {
- my $name = $arg->{'name'};
- my $newvalue;
- if ($value > $arg->{'max'}) {
- $newvalue = $arg->{'max'}
- } elsif ($value < $arg->{'min'}) {
- $newvalue = $arg->{'min'}
- }
- print $logh
- "The value $value for $name is out of " .
- "range!\n" .
- " --> Using $newvalue instead!\n";
- return $newvalue;
- }
- } elsif (($arg->{'type'} eq 'string') ||
- ($arg->{'type'} eq 'password')) {
- if (defined($arg->{'vals_byname'}{$value})) {
- my $name = $arg->{'name'};
- print $logh
- "The value $value for $name is a predefined choice\n";
- return $value;
- } elsif (stringvalid($dat, $argname, $value)) {
- # Check whether the string is one of the enumerated choices
- my $sprintfproto = $arg->{'proto'};
- $sprintfproto =~ s/\%(?!s)/\%\%/g;
- my $driverval = sprintf($sprintfproto, $value);
- for my $val (@{$arg->{'vals'}}) {
- if (($val->{'driverval'} eq $driverval) ||
- ($val->{'driverval'} eq $value)) {
- my $name = $arg->{'name'};
- print $logh
- "The string $value for $name is the predefined " .
- "choice $val->{value}\n";
- return $val->{value};
- }
- }
- # "None" is mapped to the empty string
- if ($value eq 'None') {
- my $name = $arg->{'name'};
- print $logh
- "Option $name: 'None' is the mapped to the " .
- "empty string\n";
- return '';
- }
- # No matching choice? Return the original string
- return $value;
- } elsif ($forcevalue) {
- my $name = $arg->{'name'};
- my $str = substr($value, 0, $arg->{'maxlength'});
- if (stringvalid($dat, $argname, $str)) {
- print $logh
- "The string $value for $name is longer than " .
- "$arg->{'maxlength'}, string shortened to $str\n";
- return $str;
- } elsif ($#{$arg->{'vals'}} >= 0) {
- # First list item
- my $firstentry = $arg->{'vals'}[0]{'value'};
- print $logh
- "The string $value for $name contains forbidden " .
- "characters or does not match the regular expression " .
- "defined for this option, using predefined choice " .
- "$firstentry instead\n";
- return $firstentry;
- } else {
- # We should not get here
- rip_die("Option $name incorrectly defined in the " .
- "PPD file!\n", $EXIT_PRNERR_NORETRY_BAD_SETTINGS);
- }
- }
- }
- return undef;
-}
-
-sub stringvalid {
-
- ## Checks whether a user-supplied value for a string option is valid
- ## It must be within the length limit, should only contain allowed
- ## characters and match the given regexp
-
- # Option and string
- my ($dat, $argname, $value) = @_;
-
- my $arg = $dat->{'args_byname'}{$argname};
-
- # Maximum length
- return 0 if (defined($arg->{'maxlength'}) &&
- (length($value) > $arg->{'maxlength'}));
-
- # Allowed characters
- if ($arg->{'allowedchars'}) {
- my $chars = $arg->{'allowedchars'};
- # Quote the slashes (if a slash is preceeded by an even number of
- # backslashes, it is not already quoted)
- $chars =~ s/(?<!\\)((\\\\)*)\//$2\\\//g;
- return 0 if $value !~ /^[$chars]*$/;
- }
-
- # Regular expression
- if ($arg->{'allowedregexp'}) {
- my $regexp = $arg->{'allowedregexp'};
- # Quote the slashes (if a slash is preceeded by an even number of
- # backslashes, it is not already quoted)
- $regexp =~ s/(?<!\\)((\\\\)*)\//$2\\\//g;
- return 0 if $value !~ /$regexp/;
- }
-
- # All checks passed
- return 1;
-}
-
-sub checkoptions {
-
- ## Let the values of a boolean option being 0 or 1 instead of
- ## "True" or "False", range-check the defaults of all options and
- ## issue warnings if the values are not valid
-
- # Option set to be examined
- my ($dat, $optionset) = @_;
-
- for my $arg (@{$dat->{'args'}}) {
- if (defined($arg->{$optionset})) {
- $arg->{$optionset} =
- checkoptionvalue
- ($dat, $arg->{'name'}, $arg->{$optionset}, 1);
- }
- }
-
- # If the settings for "PageSize" and "PageRegion" are different,
- # set the one for "PageRegion" to the one for "PageSize" and issue
- # a warning.
- if ($dat->{'args_byname'}{'PageSize'}{$optionset} ne
- $dat->{'args_byname'}{'PageRegion'}{$optionset}) {
- print $logh "Settings for \"PageSize\" and \"PageRegion\" are " .
- "different:\n" .
- " PageSize: $dat->{'args_byname'}{'PageSize'}{$optionset}\n" .
- " PageRegion: ".
- "$dat->{'args_byname'}{'PageRegion'}{$optionset}\n" .
- "Using the \"PageSize\" value " .
- "\"$dat->{'args_byname'}{'PageSize'}{$optionset}\"," .
- " for both.\n";
- $dat->{'args_byname'}{'PageRegion'}{$optionset} =
- $dat->{'args_byname'}{'PageSize'}{$optionset};
- }
-}
-
-# If the PageSize or PageRegion was changed, also change the other
-
-sub syncpagesize {
-
- # Name and value of the option we set, and the option set where we
- # did the change
- my ($dat, $name, $value, $optionset) = @_;
-
- # Don't do anything if we were called with an option other than
- # "PageSize" or "PageRegion"
- return if (($name ne "PageSize") && ($name ne "PageRegion"));
-
- # Don't do anything if not both "PageSize" and "PageRegion" exist
- return if ((!defined($dat->{'args_byname'}{'PageSize'})) ||
- (!defined($dat->{'args_byname'}{'PageRegion'})));
-
- my $dest;
-
- # "PageSize" --> "PageRegion"
- if ($name eq "PageSize") {
- $dest = "PageRegion";
- }
-
- # "PageRegion" --> "PageSize"
- if ($name eq "PageRegion") {
- $dest = "PageSize";
- }
-
- # Do it!
- my $val;
- if ($val=valbyname($dat->{'args_byname'}{$dest}, $value)) {
- # Standard paper size
- $dat->{'args_byname'}{$dest}{$optionset} = $val->{'value'};
- } elsif ($val=valbyname($dat->{'args_byname'}{$dest}, "Custom")) {
- # Custom paper size
- $dat->{'args_byname'}{$dest}{$optionset} = $value;
- }
-}
-
-sub copyoptions {
-
- ## Copy one option set into another one
-
- # Source and destination option sets
- my ($dat, $srcoptionset, $destoptionset) = @_;
-
- for my $arg (@{$dat->{'args'}}) {
- if (defined($arg->{$srcoptionset})) {
- $arg->{$destoptionset} = $arg->{$srcoptionset};
- }
- }
-}
-
-sub deleteoptions {
-
- ## Delete an option set
-
- # option set to be removed
- my ($dat, $optionset) = @_;
-
- for my $arg (@{$dat->{'args'}}) {
- if (defined($arg->{$optionset})) {
- delete($arg->{$optionset});
- }
- }
-}
-
-sub optionsequal {
-
- ## Compare two option sets, if they are equal, return 1, otherwise 0
-
- # Option sets to be compared, flag to compare only command line and JCL
- # options
- my ($dat, $firstoptionset, $secondoptionset, $exceptPS) = @_;
-
- for my $arg (@{$dat->{'args'}}) {
- next if ($exceptPS && ($arg->{'style'} eq 'G'));
- if ((defined($arg->{$firstoptionset})) &&
- (defined($arg->{$secondoptionset}))) {
- # Both entries exist
- return 0 if $arg->{$firstoptionset} ne $arg->{$secondoptionset};
- } elsif ((defined($arg->{$firstoptionset})) ||
- (defined($arg->{$secondoptionset}))) {
- # One entry exists
- return 0;
- }
- # If no entry exists, the non-existing entries are considered as
- # equal
- }
- return 1;
-}
-
-sub makeprologsection {
-
- # option set to be used,
- # $comments = 1: Add "%%BeginProlog...%%EndProlog"
- my ($dat, $optionset, $comments) = @_;
-
- # Collect data to be inserted here
- my @output;
-
- # Start comment
- if ($comments) {
- print $logh "\"Prolog\" section is missing, inserting it.\n";
- push(@output, "%%BeginProlog\n");
- }
-
- # Generate the option code (not necessary when CUPS is spooler)
- if ($spooler ne 'cups') {
- print $logh "Inserting option code into \"Prolog\" section.\n";
- buildcommandline ($dat, $optionset);
- push(@output, @{$dat->{'prologprepend'}});
- }
-
- # End comment
- if ($comments) {
- push(@output, "%%EndProlog\n");
- }
-
- return join('', @output);
-}
-
-sub makesetupsection {
-
- # option set to be used, $comments = 1: Add "%%BeginSetup...%%EndSetup"
- my ($dat, $optionset, $comments) = @_;
-
- # Collect data to be inserted here
- my @output;
-
- # Start comment
- if ($comments) {
- print $logh "\"Setup\" section is missing, inserting it.\n";
- push(@output, "%%BeginSetup\n");
- }
-
- # PostScript code to generate accounting messages for CUPS
- if ($spooler eq 'cups') {
- print $logh "Inserting PostScript code for CUPS' page accounting\n";
- push(@output, $accounting_prolog);
- }
-
- # Generate the option code (not necessary when CUPS is spooler)
- if ($spooler ne 'cups') {
- print $logh "Inserting option code into \"Setup\" section.\n";
- buildcommandline ($dat, $optionset);
- push(@output, @{$dat->{'setupprepend'}});
- }
-
- # End comment
- if ($comments) {
- push(@output, "%%EndSetup\n");
- }
-
- return join('', @output);
-}
-
-sub makepagesetupsection {
-
- # option set to be used,
- # $comments = 1: Add "%%BeginPageSetup...%%EndPageSetup"
- my ($dat, $optionset, $comments) = @_;
-
- # Collect data to be inserted here
- my @output;
-
- # Start comment
- if ($comments) {
- push(@output, "%%BeginPageSetup\n");
- print $logh "\"PageSetup\" section is missing, inserting it.\n";
- }
-
- # Generate the option code (not necessary when CUPS is spooler)
- print $logh "Inserting option code into \"PageSetup\" section.\n";
- buildcommandline ($dat, $optionset);
- if ($spooler ne 'cups') {
- push(@output, @{$dat->{'pagesetupprepend'}});
- } else {
- push(@output, @{$dat->{'cupspagesetupprepend'}});
- }
-
- # End comment
- if ($comments) {
- push(@output, "%%EndPageSetup\n");
- }
-
- return join('', @output);
-}
-
-sub parsepageranges {
-
- ## Parse a string containing page ranges and either check whether a
- ## given page is in the ranges or, if the given page number is zero,
- ## determine the score how specific this page range string is.
-
- # String with page ranges and number of current page (0 for score)
- my ($ranges, $page) = @_;
-
- my $currentnumber = 0;
- my $rangestart = 0;
- my $currentkeyword = '';
- my $invalidrange = 0;
- my $totalscore = 0;
- my $pageinside = 0;
- my $currentrange = '';
-
- my $evaluaterange = sub {
- # evaluate the current range: determine its score and whether the
- # current page is member of it.
- if ($invalidrange) {
- # Range is invalid, issue a warning
- print $logh " Invalid range: $currentrange\n";
- } else {
- # We have a valid range, evaluate it
- if ($currentkeyword) {
- if ($currentkeyword =~ /^even/i) {
- # All even-numbered pages
- $totalscore += 50000;
- $pageinside = 1 if (($page % 2) == 0);
- } elsif ($currentkeyword =~ /^odd/i) {
- # All odd-numbered pages
- $totalscore += 50000;
- $pageinside = 1 if (($page % 2) == 1);
- } else {
- # Invalid range
- print $logh " Invalid range: $currentrange\n";
- }
- } elsif (($rangestart == 0) && ($currentnumber > 0)) {
- # Page range is a single page
- $totalscore += 1;
- $pageinside = 1 if ($page == $currentnumber);
- } elsif (($rangestart > 0) && ($currentnumber > 0)) {
- # Page range is a sequence of pages
- $totalscore += (abs($currentnumber - $rangestart) + 1);
- if ($currentnumber < $rangestart) {
- my $tmp = $currentnumber;
- $currentnumber = $rangestart;
- $rangestart = $tmp;
- }
- $pageinside = 1 if (($page <= $currentnumber) &&
- ($page >= $rangestart));
- } elsif ($rangestart > 0) {
- # Page range goes to the end of the document
- $totalscore += 100000;
- $pageinside = 1 if ($page >= $rangestart);
- } else {
- # Invalid range
- print $logh " Invalid range: $currentrange\n";
- }
- }
- # Range is evaluated, remove all recordings of the current range
- $rangestart = 0;
- $currentnumber = 0;
- $currentkeyword = '';
- $invalidrange = 0;
- $currentrange = '';
- };
-
- for (my $i = 0; $i < length($ranges); $i ++) {
- my $c = substr($ranges, $i, 1);
- if (!$invalidrange) {
- if ($c =~ /\d/) {
- # Digit
- if ($currentkeyword) {
- # Add to keyword
- $currentkeyword .= $c;
- } else {
- # Build a page number
- $currentnumber *= 10;
- $currentnumber += $c;
- }
- } elsif ($c =~ /[a-z_]/i) {
- # Letter or underscore
- if (($rangestart > 0) || ($currentnumber > 0)) {
- # Keyword not allowed after a page number or a
- # page range
- $invalidrange = 1;
- } else {
- # Build a keyword
- $currentkeyword .= $c;
- }
- } elsif ($c eq '-') {
- # Page range
- if (($rangestart > 0) || ($currentkeyword)) {
- # Keyword or two '-' not allowed in page range
- $invalidrange = 1;
- } else {
- # Save start of range, reset page number
- $rangestart = $currentnumber;
- if ($rangestart == 0) {
- $rangestart = 1;
- }
- $currentnumber = 0;
- }
- }
- }
- if ($c eq ',') {
- # End of a range
- &$evaluaterange();
- } else {
- # Make a string of the current range, for warnings
- $currentrange .= $c;
- }
- }
- # End of input string
- &$evaluaterange();
- # Return value
- if (($page == 0) || ($pageinside)) {
- return $totalscore;
- } else {
- return 0;
- }
-}
-
-sub setoptionsforpage {
-
- ## Set the options for a given page
-
- # Foomatic data, name of the option set where to apply the options, and
- # number of the page
- my ($dat, $optionset, $page) = @_;
-
- my $value;
- for my $arg (@{$dat->{'args'}}) {
- $value = '';
- my $bestscore = 10000000;
- for my $key (keys %{$arg}) {
- next if $key !~ /^pages:(.*)$/;
- my $pageranges = $1;
- if (my $score = parsepageranges($pageranges, $page)) {
- if ($score <= $bestscore) {
- $bestscore = $score;
- $value = $arg->{$key};
- }
- }
- }
- if ($value) {
- $arg->{$optionset} = $value;
- }
- }
-}
-
-sub buildcommandline {
-
- ## Build a renderer command line, based on the given option set
-
- # Foomatic data and name of the option set to apply
- my ($dat, $optionset) = @_;
-
- # Construct the proper command line.
- $dat->{'currentcmd'} = $dat->{'cmd'};
- my @prologprepend;
- my @setupprepend;
- my @pagesetupprepend;
- my @cupspagesetupprepend;
- my @jclprepend;
- my @jclappend;
-
- # At first search for composite options and determine how they
- # set their member options
- for my $arg (@{$dat->{'args'}}) { $arg->{'order'} = 0 if !defined $arg->{'order'}; }
- for my $arg (sort { $a->{'order'} <=> $b->{'order'} }
- @{$dat->{'args'}}) {
-
- # Here we are only interested in composite options, skip the others
- next if $arg->{'style'} ne 'X';
-
- my $name = $arg->{'name'};
- # Check whether this composite option is controlled by another
- # composite option, so nested composite options are possible.
- my $userval = ($arg->{'fromcomposite'} ?
- $arg->{'fromcomposite'} : $arg->{$optionset});
-
- # Get the current setting
- my $v = $arg->{'vals_byname'}{$userval};
- my @settings = split(/\s+/s, $v->{'driverval'});
- for my $s (@settings) {
- my ($key, $value);
- if ($s =~ /^([^=]+)=(.+)$/) {
- $key = $1;
- $value = $2;
- } elsif ($s =~ /^no([^=]+)$/) {
- $key = $1;
- $value = 0;
- } elsif ($s =~ /^([^=]+)$/) {
- $key = $1;
- $value = 1;
- }
- $a = $dat->{'args_byname'}{$key};
- if ($a->{$optionset} eq "From$name") {
- # We must set this option according to the
- # composite option
- $a->{'fromcomposite'} = $value;
- # Mark the option telling by which composite option
- # it is controlled
- $a->{'controlledby'} = $name;
- } else {
- $a->{'fromcomposite'} = "";
- }
- }
- # Remove PostScript code to be inserted after an appearance of the
- # Composite option in the PostScript code.
- undef $arg->{'jclsetup'};
- undef $arg->{'prolog'};
- undef $arg->{'setup'};
- undef $arg->{'pagesetup'};
- }
-
- for my $arg (sort { $a->{'order'} <=> $b->{'order'} }
- @{$dat->{'args'}}) {
-
- # Composite options have no direct influence on the command
- # line, skip them here
- next if $arg->{'style'} eq 'X';
-
- my $name = $arg->{'name'};
- my $spot = $arg->{'spot'};
- my $cmd = $arg->{'proto'};
- my $cmdf = $arg->{'protof'};
- my $type = ($arg->{'type'} || "");
- my $section = $arg->{'section'};
- my $userval = ($arg->{'fromcomposite'} ?
- $arg->{'fromcomposite'} : $arg->{$optionset});
- my $cmdvar = "";
-
- # If we have both "PageSize" and "PageRegion" options, we kept
- # them all the time in sync, so we don't need to insert the settings
- # of both options. So skip "PageRegion".
- next if (($name eq "PageRegion") &&
- (defined($dat->{'args_byname'}{'PageSize'})) &&
- (defined($dat->{'args_byname'}{'PageRegion'})));
-
- # Build the command line snippet/PostScript/JCL code for the current
- # option
- if ($type eq 'bool') {
-
- # If true, stick the proto into the command line, if false
- # and we have a proto for false, stick that in
- if (defined($userval) && $userval == 1) {
- $cmdvar = $cmd;
- } elsif ($cmdf) {
- $userval = 0;
- $cmdvar = $cmdf;
- }
-
- } elsif ($type eq 'int' or $type eq 'float') {
-
- # If defined, process the proto and stick the result into
- # the command line or postscript queue.
- if (defined($userval)) {
- my $min = $arg->{'min'};
- my $max = $arg->{'max'};
- # We have already range-checked, correct only
- # floating point inaccuricies here
- if ($userval < $min) {
- $userval = $min;
- }
- if ($userval > $max) {
- $userval = $max;
- }
- my $sprintfcmd = $cmd;
- $sprintfcmd =~ s/\%(?!s)/\%\%/g;
- $cmdvar = sprintf($sprintfcmd,
- ($type eq 'int'
- ? sprintf("%d", $userval)
- : sprintf("%f", $userval)));
- } else {
- $userval = 'None';
- }
-
- } elsif ($type eq 'enum') {
-
- # If defined, stick the selected value into the proto and
- # thence into the commandline
- if (defined($userval)) {
- # CUPS assumes that options with the choices "Yes", "No",
- # "On", "Off", "True", or "False" are boolean options and
- # maps "-o Option=On" to "-o Option" and "-o Option=Off"
- # to "-o noOption", which foomatic-rip maps to "0" and "1".
- # So when "0" or "1" is unavailable in the option, we try
- # "Yes", "No", "On", "Off", "True", and "False".
- my $val;
- my $found = 0;
- if ($val=valbyname($arg,$userval)) {
- $found = 1;
- } elsif ($userval =~ /^Custom\.[\d\.]+x[\d\.]+[A-Za-z]*$/) {
- # Custom paper size
- $val = valbyname($arg,"Custom");
- $found = 1;
- } elsif ($userval =~ /^(0|No|Off|False)$/i) {
- foreach (qw(0 No Off False None)) {
- if ($val=valbyname($arg,$_)) {
- $userval = $_;
- $arg->{$optionset} = $userval;
- $found = 1;
- last;
- }
- }
- } elsif ($userval =~ /^(1|Yes|On|True)$/i) {
- foreach (qw(1 Yes On True)) {
- if ($val=valbyname($arg,$_)) {
- $userval = $_;
- $arg->{$optionset} = $userval;
- $found = 1;
- last;
- }
- }
- } elsif ($userval =~ /^(LongEdge|DuplexNoTumble)$/i) {
- # Handle different names for the choices of the
- # "Duplex" option
- foreach (qw(LongEdge DuplexNoTumble)) {
- if ($val=valbyname($arg,$_)) {
- $userval = $_;
- $arg->{$optionset} = $userval;
- $found = 1;
- last;
- }
- }
- } elsif ($userval =~ /^(ShortEdge|DuplexTumble)$/i) {
- foreach (qw(ShortEdge DuplexTumble)) {
- if ($val=valbyname($arg,$_)) {
- $userval = $_;
- $arg->{$optionset} = $userval;
- $found = 1;
- last;
- }
- }
- }
- if ($found) {
- my $sprintfcmd = $cmd;
- $sprintfcmd =~ s/\%(?!s)/\%\%/g;
- $cmdvar = sprintf($sprintfcmd,
- (defined($val->{'driverval'})
- ? $val->{'driverval'}
- : $val->{'value'}));
- # Custom paper size
- if ($userval =~ /^Custom\.([\d\.]+)x([\d\.]+)([A-Za-z]*)$/) {
- my $width = $1;
- my $height = $2;
- my $unit = $3;
- # convert width and height to PostScript points
- if (lc($unit) eq "in") {
- $width *= 72.0;
- $height *= 72.0;
- } elsif (lc($unit) eq "cm") {
- $width *= (72.0/2.54);
- $height *= (72.0/2.54);
- } elsif (lc($unit) eq "mm") {
- $width *= (72.0/25.4);
- $height *= (72.0/25.4);
- }
- # Round width and height
- $width =~ s/\.[0-4].*$// or
- $width =~ s/\.[5-9].*$// and $width += 1;
- $height =~ s/\.[0-4].*$// or
- $height =~ s/\.[5-9].*$// and $height += 1;
- # Insert width and height into the prototype
- if ($cmdvar =~ /^\s*pop\W/s) {
- # Custom page size for PostScript printers
- $cmdvar = "$width $height 0 0 0\n$cmdvar";
- } else {
- # Custom page size for Foomatic/Gutenprint/
- # Gimp-Print
- $cmdvar =~ s/\%0/$width/ or
- $cmdvar =~ s/(\W)0(\W)/$1$width$2/ or
- $cmdvar =~ s/^0(\W)/$width$1/m or
- $cmdvar =~ s/(\W)0$/$1$width/m or
- $cmdvar =~ s/^0$/$width/m;
- $cmdvar =~ s/\%1/$height/ or
- $cmdvar =~ s/(\W)0(\W)/$1$height$2/ or
- $cmdvar =~ s/^0(\W)/$height$1/m or
- $cmdvar =~ s/(\W)0$/$1$height/m or
- $cmdvar =~ s/^0$/$height/m;
- }
- }
- } else {
- # User gave unknown value?
- $userval = 'None';
- print $logh "Value $userval for $name is not a valid choice.\n";
- }
- } else {
- $userval = 'None';
- }
-
- } elsif (($type eq 'string') || ($type eq 'password')) {
- # Stick the entered value into the proto and
- # thence into the commandline
- if (defined($userval)) {
- my $val;
- if ($val=valbyname($arg,$userval)) {
- $userval = $val->{'value'};
- $cmdvar = (defined($val->{'driverval'})
- ? $val->{'driverval'}
- : $val->{'value'});
- } else {
- my $sprintfcmd = $cmd;
- $sprintfcmd =~ s/\%(?!s)/\%\%/g;
- $cmdvar = sprintf($sprintfcmd, $userval);
- }
- } else {
- $userval = 'None';
- }
-
- } else {
- # Ignore unknown option types silently
- }
-
- # Insert the built snippet at the correct place
- if ($arg->{'style'} eq 'G') {
- # Place this Postscript command onto the prepend queue
- # for the appropriate section.
- if ($cmdvar) {
- my $open = "[{\n%%BeginFeature: *$name ";
- if ($type eq 'bool') {
- $open .= ($userval == 1 ? "True" : "False") . "\n";
- } else {
- $open .= "$userval\n";
- }
- my $close = "\n%%EndFeature\n} stopped cleartomark\n";
- if ($section eq "Prolog") {
- push (@prologprepend, "$open$cmdvar$close");
- my $a = $arg;
- while ($a->{'controlledby'}) {
- # Collect option PostScript code to be inserted when
- # the composite option which controls this option
- # is found in the PostScript code
- $a = $dat->{'args_byname'}{$a->{'controlledby'}};
- $a->{'prolog'} .= "$cmdvar\n";
- }
- } elsif ($section eq "AnySetup") {
- if ($optionset ne 'currentpage') {
- push (@setupprepend, "$open$cmdvar$close");
- } elsif ($arg->{'header'} ne $userval) {
- push (@pagesetupprepend, "$open$cmdvar$close");
- push (@cupspagesetupprepend, "$open$cmdvar$close");
- }
- my $a = $arg;
- while ($a->{'controlledby'}) {
- # Collect option PostScript code to be inserted when
- # the composite option which controls this option
- # is found in the PostScript code
- $a = $dat->{'args_byname'}{$a->{'controlledby'}};
- $a->{'setup'} .= "$cmdvar\n";
- $a->{'pagesetup'} .= "$cmdvar\n";
- }
- } elsif ($section eq "DocumentSetup") {
- push (@setupprepend, "$open$cmdvar$close");
- my $a = $arg;
- while ($a->{'controlledby'}) {
- # Collect option PostScript code to be inserted when
- # the composite option which controls this option
- # is found in the PostScript code
- $a = $dat->{'args_byname'}{$a->{'controlledby'}};
- $a->{'setup'} .= "$cmdvar\n";
- }
- } elsif ($section eq "PageSetup") {
- push (@pagesetupprepend, "$open$cmdvar$close");
- my $a = $arg;
- while ($a->{'controlledby'}) {
- # Collect option PostScript code to be inserted when
- # the composite option which controls this option
- # is found in the PostScript code
- $a = $dat->{'args_byname'}{$a->{'controlledby'}};
- $a->{'pagesetup'} .= "$cmdvar\n";
- }
- } elsif ($section eq "JCLSetup") {
- # PJL/JCL argument
- $dat->{'jcl'} = 1;
- push (@jclprepend, unhexify($cmdvar));
- my $a = $arg;
- while ($a->{'controlledby'}) {
- # Collect option PostScript code to be inserted when
- # the composite option which controls this option
- # is found in the PostScript code
- $a = $dat->{'args_byname'}{$a->{'controlledby'}};
- $a->{'jclsetup'} .= "$cmdvar\n";
- }
- } else {
- push (@setupprepend, "$open$cmdvar$close");
- my $a = $arg;
- while ($a->{'controlledby'}) {
- # Collect option PostScript code to be inserted when
- # the composite option which controls this option
- # is found in the PostScript code
- $a = $dat->{'args_byname'}{$a->{'controlledby'}};
- $a->{'setup'} .= "$cmdvar\n";
- }
- }
- }
- # Do we have an option which is set to "Controlled by
- # '<Composite>'"? Then make PostScript code available
- # for substitution of "%% FoomaticRIPOptionSetting: ..."
- if ($arg->{'fromcomposite'}) {
- $arg->{'compositesubst'} = "$cmdvar\n";
- }
- } elsif ($arg->{'style'} eq 'J') {
- # JCL argument
- $dat->{'jcl'} = 1;
- # put JCL commands onto JCL stack...
- push (@jclprepend, "$jclprefix$cmdvar\n") if $cmdvar;
- } elsif ($arg->{'style'} eq 'C') {
- # command-line argument
-
- # Insert the processed argument in the commandline
- # just before every occurance of the spot marker.
- $dat->{'currentcmd'} =~ s!\%$spot!$cmdvar\%$spot!g;
- }
- # Insert option into command line of CUPS raster driver
- if ($dat->{'currentcmd'} =~ m!\%Y!) {
- next if !defined($userval) or $userval eq "";
- $dat->{'currentcmd'} =~ s!\%Y!$name=$userval \%Y!g;
- }
- # Remove the marks telling that this option is currently controlled
- # by a composite option (setting "From<composite>")
- undef $arg->{'fromcomposite'};
- undef $arg->{'controlledby'};
- }
-
-
- ### Tidy up after computing option statements for all of P, J, and
- ### C types:
-
- ## C type finishing
- # Pluck out all of the %n's from the command line prototype
- my @letters = qw/A B C D E F G H I J K L M W X Y Z/;
- for my $spot (@letters) {
- # Remove the letter markers from the commandline
- $dat->{'currentcmd'} =~ s!\%$spot!!g;
- }
-
- ## J type finishing
- # Compute the proper stuff to say around the job
-
- if ((defined($dat->{'jcl'})) && (!$jobhasjcl)) {
-
- # Stick beginning of job cruft on the front of the jcl stuff...
- unshift (@jclprepend, $jclbegin);
-
- # Command to switch to the interpreter
- push (@jclprepend, $jcltointerpreter);
-
- # Arrange for JCL RESET command at end of job
- push (@jclappend, $jclend);
-
- # Put the JCL stuff into the data structure
- @{$dat->{'jclprepend'}} = @jclprepend;
- @{$dat->{'jclappend'}} = @jclappend;
- }
-
- ## G type finishing
- # Save PostScript options
- @{$dat->{'prologprepend'}} = @prologprepend;
- @{$dat->{'setupprepend'}} = @setupprepend;
- @{$dat->{'pagesetupprepend'}} = @pagesetupprepend;
- @{$dat->{'cupspagesetupprepend'}} = @cupspagesetupprepend;
-}
-
-sub buildpdqdriver {
-
- # Build a PDQ driver description file to use the given PPD file
- # together with foomatic-rip with the PDQ printing system
-
- # Foomatic data and name of the option set for the default settings
- my ($dat, $optionset) = @_;
-
- # Construct structure with driver information
- my @pdqdriver = ();
-
- # Construct option list
- my @driveropts = ();
-
- # Do we have a "Custom" setting for the page size?
- # Then we have to insert the following into the "filter_exec" script.
- my @setcustompagesize = ();
-
- # Fata for a custom page size, to allow a custom size as default
- my $pagewidth = 612;
- my $pageheight = 792;
- my $pageunit = "pt";
-
-
-
- ## First, compute the various option/value clauses
- for my $arg (@{$dat->{'args'}}) {
-
- if ($arg->{'type'} eq "enum") {
-
- # Option with only one choice, omit it, foomatic-rip will set
- # this choice anyway.
- next if ($#{$arg->{'vals'}} < 1);
-
- my $nam = $arg->{'name'};
-
- # Omit "PageRegion" option, it does the same as "PageSize".
- next if $nam eq "PageRegion";
-
- my $com = $arg->{'comment'};
-
- # Assure that the comment is not empty
- if (!$com) {
- $com = $nam;
- }
-
- my $def = $arg->{$optionset};
- $arg->{'varname'} = "$nam";
- $arg->{'varname'} =~ s![\-\/\.]!\_!g;
- my $varn = $arg->{'varname'};
-
- # 1, if setting "PageSize=Custom" was found
- # Then we must add options for page width and height
- my $custompagesize = 0;
-
- # If the default is a custom size we have to set also
- # defaults for the width, height, and units of the page
- if (($nam eq "PageSize") &&
- ($def =~ /^Custom\.([\d\.]+)x([\d\.]+)([A-Za-z]*)$/)) {
- $def = "Custom";
- $pagewidth = $1;
- $pageheight = $2;
- $pageunit = $3;
- }
-
- # No quotes, thank you.
- $com =~ s!\"!\\\"!g;
-
- push(@driveropts,
- " option {\n",
- " var = \"$varn\"\n",
- " desc = \"$com\"\n");
-
- # get enumeration values for each enum arg
- my ($ev, @vals, @valstmp);
- for $ev (@{$arg->{'vals'}}) {
- my $choiceshortname = $ev->{'value'};
- my $choicename = "${nam}_${choiceshortname}";
- my $val = " -o ${nam}=${choiceshortname}";
- my $com = $ev->{'comment'};
-
- # Assure that the comment is not empty
- if (!$com) {
- $com = $choiceshortname;
- }
-
- # stick another choice on driveropts
- push(@valstmp,
- " choice \"$choicename\" {\n",
- " desc = \"$com\"\n",
- " value = \"$val\"\n",
- " }\n");
- if (($nam eq "PageSize") &&
- ($choiceshortname eq "Custom")) {
- $custompagesize = 1;
- if ($#setcustompagesize < 0) {
- push(@setcustompagesize,
- " # Custom page size settings\n",
- " # We aren't really checking for " .
- "legal vals.\n",
- " if [ \"x\${$varn}\" = 'x$val' ]; " .
- "then\n",
- " $varn=\"\${$varn}.\${PageWidth}" .
- "x\${PageHeight}\${PageSizeUnit}\"\n",
- " fi\n\n");
- }
- }
- }
-
- push(@driveropts,
- " default_choice \"" . $nam . "_" . $def . "\"\n",
- @valstmp,
- " }\n\n");
-
- if ($custompagesize) {
- # Add options to set the custom page size
- push(@driveropts,
- " argument {\n",
- " var = \"PageWidth\"\n",
- " desc = \"Page Width (for \\\"Custom\\\" page " .
- "size)\"\n",
- " def_value \"$pagewidth\"\n",
- " help = \"Minimum value: 0, Maximum value: " .
- "100000\"\n",
- " }\n\n",
- " argument {\n",
- " var = \"PageHeight\"\n",
- " desc = \"Page Height (for \\\"Custom\\\" page " .
- "size)\"\n",
- " def_value \"$pageheight\"\n",
- " help = \"Minimum value: 0, Maximum value: " .
- "100000\"\n",
- " }\n\n",
- " option {\n",
- " var = \"PageSizeUnit\"\n",
- " desc = \"Unit (for \\\"Custom\\\" page size)\"\n",
- " default_choice \"PageSizeUnit_$pageunit\"\n",
- " choice \"PageSizeUnit_pt\" {\n",
- " desc = \"Points (1/72 inch)\"\n",
- " value = \"pt\"\n",
- " }\n",
- " choice \"PageSizeUnit_in\" {\n",
- " desc = \"Inches\"\n",
- " value = \"in\"\n",
- " }\n",
- " choice \"PageSizeUnit_cm\" {\n",
- " desc = \"cm\"\n",
- " value = \"cm\"\n",
- " }\n",
- " choice \"PageSizeUnit_mm\" {\n",
- " desc = \"mm\"\n",
- " value = \"mm\"\n",
- " }\n",
- " }\n\n");
- }
-
- } elsif ($arg->{'type'} eq 'int' or $arg->{'type'} eq 'float') {
-
- my $nam = $arg->{'name'};
- my $com = $arg->{'comment'};
-
- # Assure that the comment is not empty
- if (!$com) {
- $com = $nam;
- }
-
- my $def = $arg->{$optionset};
- my $max = $arg->{'max'};
- my $min = $arg->{'min'};
- $arg->{'varname'} = "$nam";
- $arg->{'varname'} =~ s![\-\/\.]!\_!g;
- my $varn = $arg->{'varname'};
- my $legal = $arg->{'legal'} =
- "Minimum value: $min, Maximum value: $max";
-
- my $defstr = "";
- if ($def) {
- $defstr = sprintf(" def_value \"%s\"\n", $def);
- }
-
- push(@driveropts,
- " argument {\n",
- " var = \"$varn\"\n",
- " desc = \"$com\"\n",
- $defstr,
- " help = \"$legal\"\n",
- " }\n\n");
-
- } elsif ($arg->{'type'} eq 'bool') {
-
- my $nam = $arg->{'name'};
- my $com = $arg->{'comment'};
-
- # Assure that the comment is not empty
- if (!$com) {
- $com = $nam;
- }
-
- my $tcom = $arg->{'comment_true'};
- my $fcom = $arg->{'comment_false'};
- my $def = $arg->{$optionset};
- $arg->{'legal'} = "Value is a boolean flag";
- $arg->{'varname'} = "$nam";
- $arg->{'varname'} =~ s![\-\/\.]!\_!g;
- my $varn = $arg->{'varname'};
-
- my $defstr = "";
- if ($def) {
- $defstr = sprintf(" default_choice \"%s\"\n",
- $def ? "$nam" : "no$nam");
- } else {
- $defstr = sprintf(" default_choice \"%s\"\n", "no$nam");
- }
- push(@driveropts,
- " option {\n",
- " var = \"$varn\"\n",
- " desc = \"$com\"\n",
- $defstr,
- " choice \"$nam\" {\n",
- " desc = \"$tcom\"\n",
- " value = \" -o $nam=True\"\n",
- " }\n",
- " choice \"no$nam\" {\n",
- " desc = \"$fcom\"\n",
- " value = \" -o $nam=False\"\n",
- " }\n",
- " }\n\n");
-
- } elsif ($arg->{'type'} eq 'string' or $arg->{'type'} eq 'password') {
-
- my $nam = $arg->{'name'};
- my $com = $arg->{'comment'};
-
- # Assure that the comment is not empty
- if (!$com) {
- $com = $nam;
- }
-
- my $def = $arg->{$optionset};
- my $maxlength = $arg->{'maxlength'};
- my $proto = $arg->{'proto'};
- $arg->{'varname'} = "$nam";
- $arg->{'varname'} =~ s![\-\/\.]!\_!g;
- my $varn = $arg->{'varname'};
-
- my $legal;
- if (defined($maxlength)) {
- $legal .= "Maximum length: $maxlength characters, ";
- }
- $legal .= "Examples/special settings: ";
- for (@{$arg->{'vals'}}) {
- my ($value, $comment, $driverval) =
- ($_->{'value'}, $_->{'comment'}, $_->{'driverval'});
- # Retrieve the original string from the prototype
- # and the driverval
- my $string;
- if ($proto) {
- my $s = index($proto, '%s');
- my $l = length($driverval) - length($proto) + 2;
- if (($s < 0) || ($l < 0)) {
- $string = $driverval;
- } else {
- $string = substr($driverval, $s, $l);
- }
- } else {
- $string = $driverval;
- }
- if ($value ne $string) {
- $legal .= "${value}: \\\"$string\\\"";
- } else {
- $legal .= "\\\"$value\\\"";
- }
- if ($comment && ($value ne $comment) &&
- ($string ne $comment) &&
- (($value ne 'None') || ($comment ne '(None)'))) {
- $legal .= " ($comment)";
- }
- $legal .= "; ";
- }
- $legal =~ s/; $//;
-
- $arg->{'legal'} = $legal;
-
- my $defstr = "";
- if ($def) {
- $defstr = sprintf(" def_value \"%s\"\n", $def);
- }
-
- push(@driveropts,
- " argument {\n",
- " var = \"$varn\"\n",
- " desc = \"$com\"\n",
- $defstr,
- " help = \"$legal\"\n",
- " }\n\n");
-
- }
-
- }
-
-
-
- ## Define the "docs" option to print the driver documentation page
-
- push(@driveropts,
- " option {\n",
- " var = \"DRIVERDOCS\"\n",
- " desc = \"Print driver usage information\"\n",
- " default_choice \"nodocs\"\n",
- " choice \"docs\" {\n",
- " desc = \"Yes\"\n",
- " value = \" -o docs\"\n",
- " }\n",
- " choice \"nodocs\" {\n",
- " desc = \"No\"\n",
- " value = \"\"\n",
- " }\n",
- " }\n\n");
-
-
-
- ## Build the "foomatic-rip" command line
- my $commandline = "foomatic-rip --pdq";
- if ($printer) {
- $commandline .= " -P $printer";
- } else {
- # Make sure that the PPD file is entered with an absolute path
- if ($ppdfile !~ m!^/!) {
- my $pwd = cwd;
- $ppdfile = "$pwd/$ppdfile";
- }
- $commandline .= " --ppd=$ppdfile";
- }
- for my $arg (@{$dat->{'args'}}) {
- if ($arg->{'varname'}) {
- $commandline .= "\${$arg->{'varname'}}";
- }
- }
- $commandline .= "\${DRIVERDOCS} \$INPUT > \$OUTPUT";
-
-
-
- ## Now we generate code to build the command line snippets for the
- ## numerical options
-
- my @psfilter;
- for my $arg (@{$dat->{'args'}}) {
-
- # Only numerical and string options need to be treated here
- next if (($arg->{'type'} ne 'int') &&
- ($arg->{'type'} ne 'float') &&
- ($arg->{'type'} ne 'string') &&
- ($arg->{'type'} ne 'password'));
-
- my $comment = $arg->{'comment'};
- my $name = $arg->{'name'};
- my $varname = $arg->{'varname'};
-
- # If the option's variable is non-null, put in the
- # argument. Otherwise this option is the empty
- # string. Error checking?
-
- push(@psfilter,
- " # $comment\n",
- (($arg->{'type'} eq 'int') || ($arg->{'type'} eq 'float') ?
- (" # We aren't really checking for max/min,\n",
- " # this is done by foomatic-rip\n",
- " if [ \"x\${$varname}\" != 'x' ]; then\n ") : ""),
- #" $varname=`echo \${$varname} | perl -p -e \"s/'/'\\\\\\\\\\\\\\\\''/g\"`\n",
- " $varname=\" -o $name='\${$varname}'\"\n",
- (($arg->{'type'} eq 'int') || ($arg->{'type'} eq 'float') ?
- " fi\n" : ""),
- "\n");
- }
-
- # Command execution
-
- push(@psfilter,
- " if ! test -e \$INPUT.ok; then\n",
- " sh -c \"$commandline\"\n",
- " if ! test -e \$OUTPUT; then \n",
- " echo 'Error running foomatic-rip; no output!'\n",
- " exit 1\n",
- " fi\n",
- " else\n",
- " ln -s \$INPUT \$OUTPUT\n",
- " fi\n\n");
-
- my $version = time();
- my $name = "$model-$version";
- $name =~ s/\W/\-/g;
- $name =~ s/\-+/\-/g;
-
- my $pname = $model;
-
- push (@pdqdriver,
- "driver \"$name\" {\n\n",
- " # This PDQ driver declaration file was generated " .
- "automatically by\n",
- " # foomatic-rip from information in the file $ppdfile.\n",
- " # It allows printing with PDQ on the $pname.\n",
- "\n",
- " requires \"foomatic-rip\"\n\n",
- @driveropts,
- " language_driver all {\n",
- " # We accept all file types and pass them to foomatic-rip\n",
- " # (invoked in \"filter_exec {}\" section) without\n",
- " # pre-filtering\n",
- " filetype_regx \"\"\n",
- " convert_exec {\n",
- " ln -s \$INPUT \$OUTPUT\n",
- " }\n",
- " }\n\n",
- " filter_exec {\n",
- @setcustompagesize,
- @psfilter,
- " }\n",
- "}\n");
-
- return @pdqdriver;
-
-}
-
-#
-# Convert lp or ipp based attribute names (and values) to something that matches# PPD file options.
-#
-sub option_to_ppd {
- my ($ipp_attribute) = @_;
- my ($key, $value, $result) = ();
-
- if (/([^=]+)=[\'\"]?(.*}[\'\"]?)/) { # key=value
- ($key, $value) = ($1, $2);
- } elsif (/no(.+)/) { # BOOLEAN: no{key} (false)
- ($key, $value) = ($1, 'false');
- } else { # BOOLEAN: {key} (true)
- ($key, $value) = ($1, 'true');
- }
-
- if (($key =~ /^job-/) || ($key =~ /^copies/) ||
- ($key =~ /^multiple-document-handling/) || ($key =~ /^number-up/) ||
- ($key =~ /^orientation-requested/) ||
- ($key =~ /^dest/) || ($key =~ /^protocol/) || ($key =~ /^banner/) ||
- ($key =~ /^page-ranges/)) {
- # Ignored:
- # job-*, multiple-document-handling are not supported by this
- # filter
- # dest, protocol, banner, number-up, orientation-requested are
- # handled by the LP filtering or interface script
- # NOTE - page-ranges should probably be handled here, but
- # ignore it until we decide how to handle it.
- } elsif (/^printer-resolution/) {
- # value match on "123, 457" or on "123, 457, 8"
- if (/([\d]+),([\s]*)([\d]+)((,([\s]*)([\d]+))??)/) {
- $result = '$1x$2$3 '; # (width)x(height)(units)
- }
- } elsif (/^print-quality/) {
- ($value == 3) &&
- ($result = 'PrintoutMode=Draft');
- ($value == 4) &&
- ($result = 'PrintoutMode=Normal');
- ($value == 5) &&
- ($result = 'PrintoutMode=High');
- } else {
- # NOTE - if key == 'media', we may need to convert the values at some
- # point. (see RFC2911, Section 14 for values)
- $result = '$key=\"$value\"';
- }
-
- return ($result);
-}
-
-#
-# Read the attributes file containing the various job meta-data, including
-# requested capabilities
-#
-sub read_attribute_file {
- my ($file) = @_;
- my $result = "";
-
- open (AFP, "<$file") ||
- (print $logh "Unable to open IPP Attribute file ".$file.", ignored: ".$!);
-
- while(<AFP>) {
- $result .= option_to_ppd($_);
- }
-
- close (AFP);
-
- return ($result);
-}
-
-sub modern_system {
- my (@list) = @_;
-
- if (($modern_shell =~ /.+/) && ($modern_shell ne '/bin/sh')) {
- # a "modern" shell other than the default shell was specified
- my $pid = fork();
-
- ($pid < 0) && die "failed to fork()";
-
- if ($pid == 0) { # child, execute the commands under a modern shell
- exec($modern_shell, "-c", @list);
- die "exec($modern_shell, \"-c\", @list);";
- } else { # parent, wait for the child
- waitpid($pid, 0);
- }
- } else { # the system shell is "modern" enough.
- system(@list);
- }
-}
-
-# Emacs tabulator/indentation
-
-### Local Variables:
-### tab-width: 8
-### perl-indent-level: 4
-### End: