Viewing file: foomatic-rip (199.44 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |#!/usr/bin/perl
# The above Perl path may vary on your system; fix it!!! -*- perl -*-
use strict;
use POSIX;
use Cwd;
my $ripversion='$Revision: 3.43.2.15 $';
#'# 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.linuxprinting.org/cups-doc.html
# http://www.linuxprinting.org/lpd-doc.html
# http://www.linuxprinting.org/ppr-doc.html
# http://www.linuxprinting.org/pdq-doc.html
# http://www.linuxprinting.org/direct-doc.html
# http://www.linuxprinting.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 = "/usr/bin:/usr/local/bin:/usr/bin:/bin";
# CUPS raster drivers are searched here
my $cupsfilterpath = "/usr/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 = "/usr";
my $configpath = "/etc/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 = "";
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';
# 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 - 2004 Grant Taylor
# & Till Kamppeter
# & Helge Blischke
#
# 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
# 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 = "";
# Files to be printed
my @filelist = ();
# 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];
}
## Environment variables;
# "PPD": PPD file name for CUPS 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 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 PPR)
$spooler = 'cups';
}
# "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;
# Where to send debugging log output to
my $logh;
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=" appends the data to the ,
# "--genpdq=" creates/overwrites 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 = ) {
# 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";
}
}
# 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 = ) {
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 \") " .
"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 = ("");
}
# Check file list
my $file;
my $filecnt = 0;
for $file (@filelist) {
if ($file ne "") {
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() {
# foomatic-rip should also work with PPD file downloaded under Windows.
$_ = undossify($_);
# Parse keywords
if (m!^\*NickName:\s*\"(.*)$!) {
# "*NickName: "
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 = ;
chomp $line;
}
$line =~ m!^([^\"]*)\"!;
$cmd .= $1;
$model = unhtmlify($cmd);
} elsif (m!^\*FoomaticIDs:\s*(\S+)\s+(\S+)\s*$!) {
# "*FoomaticIDs: "
my $id = $1;
my $driver = $2;
# Store the values
$dat->{'id'} = $id;
$dat->{'driver'} = $driver;
} elsif (m!^\*FoomaticRIPPostPipe:\s*\"(.*)$!) {
# "*FoomaticRIPPostPipe: "
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 = ;
chomp $line;
}
$line =~ m!^([^\"]*)\"!;
$cmd .= $1;
$postpipe = unhtmlify($cmd);
} elsif (m!^\*FoomaticRIPCommandLine:\s*\"(.*)$!) {
# "*FoomaticRIPCommandLine: "
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 = ;
chomp $line;
}
$line =~ m!^([^\"]*)\"!;
$cmd .= $1;
$dat->{'cmd'} = unhtmlify($cmd);
} elsif (m!^\*cupsFilter:\s*\"(.*)$!) {
# "*cupsFilter: "
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 = ;
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: "
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 = ;
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 *