diff options
Diffstat (limited to 'foomatic-rip.in')
-rw-r--r-- | foomatic-rip.in | 6617 |
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/\'/\'/g; - $str =~ s/\"/\"/g; - $str =~ s/\>/\>/g; - $str =~ s/\</\</g; - $str =~ s/\&/\&/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: |