diff options
Diffstat (limited to 'net-print/cups/files/cupsomatic')
-rw-r--r-- | net-print/cups/files/cupsomatic | 1107 |
1 files changed, 1107 insertions, 0 deletions
diff --git a/net-print/cups/files/cupsomatic b/net-print/cups/files/cupsomatic new file mode 100644 index 000000000000..fc6bb342e7da --- /dev/null +++ b/net-print/cups/files/cupsomatic @@ -0,0 +1,1107 @@ +#! /usr/bin/perl + +# The above Perl path may vary on your system; fix it!!! + +# Cupsomatic is intended to be used as a CUPS filter for printers +# defined in a PPD file (CUPS-O-Matic or PPD-O-Matic) obtained from +# the Linux Printing Database. +# +# See http://www.linuxprinting.org/cups-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 Ghostscript, $enscriptcommand, and +# possibly other stuff. The default path is often fine on Linux, but +# may not be on other systems. +# +my $execpath = "/usr/local/bin:/usr/bin:/bin"; + +# Location of the configuration file "filter.conf", this file can be +# used for settings which should apply to all filters (lpdomatic, +# cupsomatic, ...). +# This variable must contain the full pathname of the directory which +# contains the configuration file, usually "/etc/foomatic". +my $configpath = "/etc/foomatic"; + +# For the stuff below, the settings in the configuration file have priority. + +# 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 $enscriptcommand = ""; + +# Set to 1 to insert postscript code for accounting. +my $ps_accounting = 1; + +# Set debug to 1 to enable the debug logfile for this filter; it will +# appear as /tmp/prnlog It will contain status from this filter, plus +# Ghostscript 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 in case you have enabled debugging +# above. +my $logfile = "/tmp/prnlog"; + +# End interesting enduser options + +# ========================================================================== +# +# cupsomatic Perl Foomatic filter script for CUPS +# +# Copyright 2000-2001 Grant Taylor <gtaylor@picante.com> +# & Till Kamppeter <till.kamppeter@gmx.net> +# +# 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. +# + +# Read config file if present +%conf = readConfFile("$configpath/filter.conf"); + +# Determine which filter to use for non-PostScript files to be converted +# to PostScript + +my @enscriptcommands = + ('a2ps -1 @@--medium=@@PAGESIZE@@ @@--center-title=@@JOBTITLE@@ -o - 2>/dev/null', + 'enscript -G @@-M @@PAGESIZE@@ @@-b "Page $%|@@JOBTITLE@@ -p- 2>/dev/null', + 'mpage -o -1 @@-b @@PAGESIZE@@ @@-H -h @@JOBTITLE@@ -P- -'); + +if (defined(%conf) and $conf{textfilter}) +{ + $enscriptcommand = $conf{textfilter}; + $enscriptcommand eq 'a2ps' and $enscriptcommand = $enscriptcommands[0]; + $enscriptcommand eq 'enscript' and $enscriptcommand = $enscriptcommands[1]; + $enscriptcommand eq 'mpage' and $enscriptcommand = $enscriptcommands[2]; +} + +if ($execpath) {$ENV{'PATH'} = $execpath;} + +# Set debug mode +$debug = $conf{debug} if defined(%conf) and defined $conf{debug}; + +# Where to send debugging log output to +if ($debug) { + # Grotesquely unsecure; use for debugging only + open LOG, ">$logfile"; + $logh = *LOG; + + use IO::Handle; + $logh->autoflush(1); +} else { + $logh=*STDERR; +} + +# This piece of PostScript code (created 2001 by Michael Allerhand +# (michael.allerhand@ed.ac.uk) lets GhostScript output the page +# accounting information which CUPS needs on standard error. + +$ps_accounting = $conf{ps_accounting} + if defined(%conf) and defined $conf{ps_accounting}; +my $accounting_prolog = $ps_accounting ? "[{ +%% Code for writing CUPS accounting tags on standard error +/cupsWrite { + (%stderr) (w) file + exch writestring +} bind def +/cupsEndPage { + (PAGE: ) cupsWrite + pop % ignore reason code + 1 add 40 string cvs cupsWrite + ( ) cupsWrite + #copies 40 string cvs cupsWrite + (\\n) cupsWrite + true +} bind def +<</EndPage{cupsEndPage}>>setpagedevice +} stopped cleartomark +" : ""; + + +# Flush everything immediately. +$|=1; + +my $comversion='$Revision: 1.1 $'; +#'# Fix emacs syntax highlighting +print $logh "Cupsomatic backend version $comversion running...\n"; +print $logh "called with arguments: '",join("','",@ARGV),"'\n"; +my $jobtitle = $ARGV[2]; +# Check for and handle inputfile vs stdin +my $inputfile = $ARGV[5]; +if ($inputfile and $inputfile ne '-') { + print $logh 'inputfile handling is broken!!!'; + warn 'inputfile handling is broken!!!'; +} + +# We get the PPD filename in environment variable PPD. +# Load the cups-o-matic data structure from it +# Load also the defaults from the PPD syntax... +my $ppdfile = $ENV{'PPD'}; + +print $logh "ppd=$ppdfile\n"; + +open PPD, "$ppdfile" || do { + print $logh "error opening $ppdfile.\n"; + die "unable to open ppd file $ppdfile"; +}; + +my @datablob; # embedded data +my %ppddefaults; # defaults from PPD +while(<PPD>) { + if (s!^\*\% COMDATA \#!!) { + push (@datablob, $_); + } elsif (m!^\*Default(\w+): ([\w\.\+]+)!) { + # The dot in the default value is needed to support float + # options + $ppddefaults{$1} = $2; + } +} +close PPD; + +# OK, we have the datablob +eval join('',@datablob) || do { + print $logh "unable to evaluate datablob\n"; + die "error in datablob eval"; +}; + +$dat = $VAR1; + +# Accounting does not work with the "Postscript" driver, there one would +# get an extra blank page with every job. +if ($dat->{'driver'} eq "Postscript") { + $accounting_prolog = ""; +} + +# Determine with which command non-PostScript files are converted to PostScript +if ($enscriptcommand eq "") { + for my $c (@enscriptcommands) { + ($c =~ m/^\s*(\S+)\s+/) || ($c = m/^\s*(\S+)$/); + $command = $1; + for (split(':', $ENV{'PATH'})) { + if (-x "$_/$command") { + $enscriptcommand = $c; + last; + } + } + if ($enscriptcommand ne "") { + last; + } + } + if ($enscriptcommand eq "") { + $enscriptcommand = "echo \"Cannot convert file to PostScript!\" 1>&2"; + } +} + +## First, for arguments with a default, stick the default in as the +## userval. First take the defaults from the embedded data, then take +## the defaults as found in the PPD file: some people modify the PPD +## file directly to set new system-wide defaults. + +# from metadata +for $arg (@{$dat->{'args'}}) { + if ($arg->{'default'}) { + $arg->{'userval'} = $arg->{'default'}; + } +} + +# from ppd file; these overwrite the standard defaults +for $arg (@{$dat->{'args'}}) { + my $ppddef = $ppddefaults{$arg->{'name'}}; + if (defined($ppddef)) { + my $name = $arg->{'name'}; + if ($arg->{'type'} eq 'bool') { + # This maps Unknown to mean False. Good? Bad? + $arg->{'userval'} = ($ppddef eq 'True' ? '1' : '0'); + } elsif ($arg->{'type'} eq 'enum') { + if (defined($arg->{'vals_byname'}{$ppddef})) { + $arg->{'userval'} = $ppddef; + } else { + # wtf!? that's not a choice! + my $name=$arg->{'name'}; + print $logh + "PPD default value $ppddef for $name is not a choice!\n"; + } + } elsif (($arg->{'type'} eq 'int') || + ($arg->{'type'} eq 'float')) { + if (($ppddef <= $arg->{'max'}) && + ($ppddef >= $arg->{'min'})) { + $arg->{'userval'} = $ppddef; + } else { + print $logh + "PPD default value $ppddef for $name is out of range!\n"; + } + } + } +} + +# so now what were the defaults? print them if debugging... +if ($debug) { + for $arg (@{$dat->{'args'}}) { + my ($name, $val) = ($arg->{'name'}, $arg->{'userval'}); + print $logh "Default for option $name is $val\n"; + } +} + +## Next, examine the postscript job itself for traces of command-line +## and pjl options. Sometimes these don't show up in the CUPS filter +## 'options' argument! + +# Examination strategy: read some lines from STDIN. Put those lines +# onto the stack @examined_stuff, which will be stuffed into +# Ghostscript/whatever later on. + +print $logh "Seaerching job for option settings ...\n"; +my $maxlines = 1000; # how many lines to examine? +my $more_stuff = 1; # there is more stuff in stdin. +my $linect = 0; # how many lines have we examined? +my $last_setpagedevice = 0; # Find the last line with "setpagedevice" + # and insert the accounting code afterwards. + # If there is a "setpagedevice" after the + # accounting code, an empty page would be + # printed (and even accounted). +my $insertprepend = 1; # number of the line where the PostScript + # commands of numerical options should be + # inserted. This must be after the option + # settings inserted by CUPS, because when one + # uses a PPD-O-Matic PPD file and sets a + # numerical option to a value which is not + # under the choices of the PPD representation + # of the option CUPS inserts the PostScript + # code for the default setting. +my $inheader = 1; # Are we still in the header formed by the + # option PostScript code inserted by CUPS? +my $infeature = 0; # Are we in a "[{ %%BeginFeature ... } stopped + # cleartomark" structure? + +do { + my $line; + if ($line=<STDIN>) { + if ($linect == 0) { + # Line zero should be postscript leader + die 'job does not start with Postscript %! thing' + if $line !~ m/^.?%!/; # There can be a Windows control char + # before "%!" + + } else { + if (($line =~ m/\%\%BeginFeature:\s+\*?([^\s=]+)\s+(\S.*)$/) || + ($line =~ m/\%\%\s*FoomaticOpt:\s*([^\s=]+)\s*=\s*(\S.*)$/)) { + my ($option, $value) = ($1, $2); + + # OK, we have an option. If it's not a + # *ostscript-style option (ie, it's command-line or + # PJL) then we should note that fact, since the + # attribute-to-filteroption passing in CUPS is kind of + # funky, especially wrt boolean options. + + print $logh "Found: $line"; + if ($arg=argbyname($option)) { + print $logh " Option: $option=$value"; + if ($arg->{'style'} ne 'G') { + print $logh " --> Setting option\n"; + if ($arg->{'type'} eq 'bool') { + # Boolean options are 1 or 0 + if ($value eq 'True') { + $arg->{'userval'} = 1; + } elsif ($value eq 'False') { + $arg->{'userval'} = 0; + } else { + warn "job contained boolean option", + " with neither True nor False value!?"; + } + } elsif (($arg->{'type'} eq 'enum') || + ($arg->{'type'} eq 'int') || + ($arg->{'type'} eq 'float')) { + # enum options go as the value, unless + # they were Unknown... + # Same with numerical options, they can appear + # here when the client has used the Adobe- + # compliant PPD-O-MATIC PPD file. + + if (lc($value) eq 'unknown') { + $arg->{'userval'} = undef; + } else { + $arg->{'userval'} = $value; + } + } + } else { + # it is a postscript style option, presuemably + # all applied for us and such... + print $logh " --> Option will be set by PostScript interpreter\n"; + } + } else { + # This option is unknown to us. WTF? + warn "unknown option $option=$value found in the job"; + } + + } elsif ($line =~ /^[^\%]*setpagedevice/) { + # When "setpagedevice" is in the line, update the line number + # of the last "setpagedevice" + $last_setpagedevice = $linect; + } elsif ($line =~ /^\s*\[\{/) { + # Initial line ("[{") of an option setting inserted by CUPS + $infeature ++; + } elsif ($line =~ /\}\s*stopped\s+cleartomark\s*$/) { + # Final line ("} stopped cleartomark") of an option setting + # inserted by CUPS + $infeature --; + } elsif (($inheader) && (!$infeature) && ($line !~ /^\s*$/)) { + # Found end of option settings inserted by CUPS + $inheader = 0; + $insertprepend = $linect; + } + } + + # Push the line onto the stack for later spitting up... + push (@examined_stuff, $line); + $linect++; + + } else { + # EOF! + $more_stuff = 0; + } + # CUPS has inserted more than 1000 ($maxlines) lines with option + # settings, so increase $maxlines. + if (($linect >= $maxlines) && ($inheader)) { + $maxlines += 200; + } +} while (($linect < $maxlines) and ($more_stuff != 0)); + +# Insert accounting code after the line with the last "setpagedevice" and +# after the point where the PostScript code for the numerical options will +# be inserted +splice(@examined_stuff, + $last_setpagedevice >= $insertprepend ? + $last_setpagedevice + 1 : $insertprepend, + 0, + $accounting_prolog); + +## We get various options as argument 5. Parse these out. User-set +## values get stored as 'userval' in the argument's structure +my $optstr = $ARGV[4]; + +print $logh "options: ->$optstr<-\n"; + +# Parse them. They're foo='bar nut', or foo, or 'bar nut', or +# foo:'bar nut' (when GPR was used) all with spaces between... + +my @opts; + +# foo='bar nut' +while ($optstr =~ s!(\w+=\'.+?\') ?!!) { + push (@opts, $1); +} + +# foo:'bar nut' (GPR separates option and setting with a colon ":") +while ($optstr =~ s!(\w+:\'.+?\') ?!!) { + push (@opts, $1); +} + +# 'bar nut' +while ($optstr =~ s!(\'.+?\') ?!!) { + push (@opts, $1); +} + +# foo +push(@opts, split(/ /,$optstr)); + + +# Now actually process those pesky options... + +for (@opts) { + print $logh "Pondering option `$_'\n"; + + if (lc($_) eq 'docs') { + $do_docs = 1; + last; + } + + my $arg; + if ((m!(.+)=\'?(.+)\'?!) || (m!(.+):\'?(.+)\'?!)) { + # GPR separates option and setting with a colon ":", all other + # frontends use "=". + my ($aname, $avalue) = ($1, $2); + + # Standard arguments? + # media=x,y,z + # sides=one|two-sided-long|short-edge + + # handled by cups for us? + # page-ranges= + # page-set= + # number-up= + + # brightness= gamma= these probably collide with printer-specific + # options. Hmm. CUPS has a stupid design for option + # handling; everything gets all muddled together. + + # 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. + + if ($aname =~ m!^media$!i) { + my @values = split(',',$avalue); + for (@values) { + if ($dat->{'args_byname'}{'PageSize'} + and $val=valbyname($dat->{'args_byname'}{'PageSize'},$_)) { + $dat->{'args_byname'}{'PageSize'}{'userval'} = + $val->{'value'}; + } elsif ($dat->{'args_byname'}{'MediaType'} + and $val=valbyname($dat->{'args_byname'}{'MediaType'},$_)) { + $dat->{'args_byname'}{'MediaType'}{'userval'} = + $val->{'value'}; + } elsif ($dat->{'args_byname'}{'InputSlot'} + and $val=valbyname($dat->{'args_byname'}{'InputSlot'},$_)) { + $dat->{'args_byname'}{'InputSlot'}{'userval'} = + $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'}{'userval'} = 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'})) { + # We set "Duplex" to '1' here, the real argument setting + # will be done later + $dat->{'args_byname'}{'Duplex'}{'userval'} = '1'; + # Check the binding: "long edge" or "short edge" + if ($avalue =~ m!long-edge!i) { + if (defined($dat->{'args_byname'}{'Binding'})) { + $dat->{'args_byname'}{'Binding'}{'userval'} = + $dat->{'args_byname'}{'Binding'}{'vals_byname'}{'LongEdge'}{'value'}; + } else { + $dat->{'args_byname'}{'Duplex'}{'userval'} = + 'LongEdge'; + } + } elsif ($avalue =~ m!short-edge!i) { + if (defined($dat->{'args_byname'}{'Binding'})) { + $dat->{'args_byname'}{'Binding'}{'userval'} = + $dat->{'args_byname'}{'Binding'}{'vals_byname'}{'ShortEdge'}{'value'}; + } else { + $dat->{'args_byname'}{'Duplex'}{'userval'} = + 'ShortEdge'; + } + } + } + } elsif ($avalue =~ m!^one-sided!i) { + if (defined($dat->{'args_byname'}{'Duplex'})) { + # We set "Duplex" to '0' here, the real argument setting + # will be done later + $dat->{'args_byname'}{'Duplex'}{'userval'} = '0'; + } + } + + # 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)) { + $arg->{'userval'} = $avalue; + + # Special case for PPD undef in required defaults; etc. + # The user himself should never be specifying 'Unknown'. + if (lc($avalue) eq 'unknown') { + $arg->{'userval'} = undef; + } + } else { + print $logh "Unknown option $aname=$avalue.\n"; + } + } + } elsif (m!no(.+)!i) { + # standard bool args: + # landscape; what to do here? + # duplex; we should just handle this one OK now? + + if ($arg=argbyname($1)) { + $arg->{'userval'} = 0; + } else { + print $logh "Unknown bool option $1.\n"; + } + } elsif (m!(.+)!) { + if ($arg=argbyname($1)) { + $arg->{'userval'} = 1; + } else { + print $logh "Unknown bool? option $1.\n"; + } + } +} + + +#### Everything below here ought to be generic for any printing +#### system? It just uses the $dat structure, with user values filled +#### in, and turns postscript into printer data. + + +# Construct the proper command line. +my $commandline = $dat->{'cmd'}; +my $arg; + argument: + for $arg (sort { $a->{'order'} <=> $b->{'order'} } + @{$dat->{'args'}}) { + + # Only do command-line and postscript style arguments. + # I think PJL options may break some drivers? Uncomment if so + # next argument if ($arg->{'style'} eq 'J'); + + my $name = $arg->{'name'}; + my $spot = $arg->{'spot'}; + my $varname = $arg->{'varname'}; + my $cmd = $arg->{'proto'}; + my $comment = $arg->{'comment'}; + my $type = $arg->{'type'}; + my $cmdvar = ""; + my $userval = $arg->{'userval'}; + + my $psarg = ($arg->{'style'} eq 'G' ? 1 : 0); + + if ($type eq 'bool') { + + if ($psarg && !$do_docs) { + # CUPS handles bools for us, only for docs printing we must do + # it again, because we do not print the file which CUPS has + # filtered + } else { + # If true, stick the proto into the command line + if (defined($userval) && $userval == 1) { + $cmdvar = $cmd; + } + } + + } 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'}; + if ($userval >= $min and $userval <= $max) { + my $sprintfcmd = $cmd; + $sprintfcmd =~ s!\%([^s])!\%\%$1!g; + $cmdvar = sprintf($sprintfcmd, + ($type eq 'int' + ? sprintf("%d", $userval) + : sprintf("%f", $userval))); + } else { + print $logh "Value $userval for $name is out of range $min<=x<=$max.\n"; + } + } + + } elsif ($type eq 'enum') { + + if ($psarg && !$do_docs) { + # CUPS handles enums for us, only for docs printing we must do + # it again, because we do not print the file which CUPS has + # filtered + } else { + # If defined, stick the selected value into the proto and + # thence into the commandline + if (defined($userval)) { + # CUPS assumes that options with the choises "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 cupsomatic 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 $found = 0; + my $val; + if ($val=valbyname($arg,$userval)) { + $found = 1; + } elsif ($userval eq '0') { + foreach (qw(No Off False None)) { + if ($val=valbyname($arg,$_)) { + $userval = $_; + $arg->{'userval'} = $userval; + $found = 1; + last; + } + } + } elsif ($userval eq '1') { + foreach (qw(Yes On True)) { + if ($val=valbyname($arg,$_)) { + $userval = $_; + $arg->{'userval'} = $userval; + $found = 1; + last; + } + } + } elsif ($userval eq 'LongEdge') { + # Handle different names for the choices of the + # "Duplex" option + foreach (qw(LongEdge DuplexNoTumble)) { + if ($val=valbyname($arg,$_)) { + $userval = $_; + $arg->{'userval'} = $userval; + $found = 1; + last; + } + } + } elsif ($userval eq 'ShortEdge') { + foreach (qw(ShortEdge DuplexTumble)) { + if ($val=valbyname($arg,$_)) { + $userval = $_; + $arg->{'userval'} = $userval; + $found = 1; + last; + } + } + } + if ($found) { + my $sprintfcmd = $cmd; + $sprintfcmd =~ s!\%([^s])!\%\%$1!g; + $cmdvar = sprintf($sprintfcmd, + (defined($val->{'driverval'}) + ? $val->{'driverval'} + : $val->{'value'})); + } else { + # User gave unknown value? + print $logh "Value $userval for $name is not a valid choice.\n"; + } + } + } + + } else { + + print $logh "unknown type for argument $name!?\n"; + # die "evil type!?"; + + } + + if ($arg->{'style'} eq 'G') { + if ($type eq 'int' or $type eq 'float' or $do_docs) { + # Place this Postscript command onto the prepend queue. + push (@prepend, "$cmdvar\n") if $cmdvar; + } else { + # non numeric arguments are done for us by cups (except + # for docs printing, here the file prepared by CUPS will + # be replaced by the docs file which is generated by this + # script + } + + } elsif ($arg->{'style'} eq 'J') { + + if (defined($dat->{'pjl'})) { + # put PJL commands onto PJL stack... + push (@pjlprepend, "\@PJL $cmdvar\n") if $cmdvar; + } + + } elsif ($arg->{'style'} eq 'C') { + # command-line argument + + # Insert the processed argument in the commandline + # just before the spot marker. + $commandline =~ s!\%$spot!$cmdvar\%$spot!; + } + + } + + +### 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 Z/; +for $spot (@letters) { + # Remove the letter marker from the commandline + $commandline =~ s!\%$spot!!; +} + +## G type finishing +# Insert PostScript code of the numerical options after the options settings +# which CUPS has inserted +splice(@examined_stuff, $insertprepend, 0, @prepend); +print $logh "$0: inserted option PS code:\n", @prepend; + +## J type finishing +# Compute the proper stuff to say around the job + +if (defined($dat->{'pjl'})) { + + # Stick beginning of job cruft on the front of the pjl stuff... + unshift (@pjlprepend, + "\033%-12345X\@PJL JOB NAME=\"CUPSOMATIC\"\n"); + + # Arrange for PJL EOJ command at end of job + push (@pjlappend, + "\33%-12345X\@PJL RESET\n\@PJL EOJ\n"); + + print $logh "PJL: ", @pjlprepend, "<job data>\n", @pjlappend; +} + +# Insert the page size into the $enscriptcommand +if ($enscriptcommand =~ /\@\@([^@]+)\@\@PAGESIZE\@\@/) { + my $optstr = ((($arg = argbyname('PageSize'))) + ? $1 . $arg->{'userval'} + : ""); + $enscriptcommand =~ s/\@\@([^@]+)\@\@PAGESIZE\@\@/$optstr/; +} + +# Insert the job title into the $enscriptcommand +if ($enscriptcommand =~ /\@\@([^@]+)\@\@JOBTITLE\@\@/) { + if ($do_docs) { + $jobtitle = "Documentation for the $dat->{'make'} $dat->{'model'}"; + } + my $titlearg = $1; + my ($arg, $optstr); + ($arg = $jobtitle) =~ s/\"/\\\"/g; + if (($titlearg =~ /\"/) || $arg) { + $optstr = $titlearg . ($titlearg =~ /\"/ ? '' : '"') . + ($arg ? "$arg\"" : '"'); + } else { + $optstr = ""; + } + $enscriptcommand =~ s/\@\@([^@]+)\@\@JOBTITLE\@\@/$optstr/; +} + +# Debugging printout of all option values +if ($debug) { + for $arg (@{$dat->{'args'}}) { + my ($name, $val) = ($arg->{'name'}, $arg->{'userval'}); + print $logh "Final value for option $name is $val\n"; + } +} + +# Now print the darned thing! +if (! $do_docs) { + # Run the proper command line. + my ($driverh, $driverpid) = getdriverhandle(); + + print $driverh @examined_stuff; # first 1000 lines or so + if ($debug != 0) { + open DRIVERINPUT, "> /tmp/prnjob" + or die "error opening /tmp/prnjob"; + print DRIVERINPUT @examined_stuff; + } + if ($more_stuff) { + while (<STDIN>) { + print $driverh $_; + if ($debug != 0) { + print DRIVERINPUT $_; + } + } + } + + close $driverh + or die "error closing $driverh"; + if ($debug != 0) { + close DRIVERINPUT + or die "error closing /tmp/prnjob"; + } + + # Wait for driver child + waitpid($driverpid, 0); + print $logh "Main process finished\n"; + exit(0); + + ### End of non-doc processing... + +} else { + + print $logh "printing docs\n"; + + my $pid, $sleep_count=0; + do { + $pid = open(KID1, "|-"); + unless (defined $pid) { + warn "cannot fork: $!"; + die "bailing out" if $sleep_count++ > 6; + sleep 10; + } + } until defined $pid; + + if (! $pid) { + # child/driver; exec enscript... + + my ($driverhandle, $driverpid) = getdriverhandle(); + + print $logh "setting STDOUT to be $driverhandle and spawning $enscriptcommand\n"; + + open (STDOUT, ">&$driverhandle") + or die "Couldn't dup driverhandle"; + # PostScript option settings, as "PageSize" + print $logh "Prepending option PS code:\n@prepend"; + print "@prepend"; + system "$enscriptcommand" + and die "Couldn't exec $enscriptcommand"; + + close STDOUT; + close $driverhandle; + + # Wait for driver child + waitpid($driverpid, 0); + print $logh "KID1 finished\n"; + exit(0); + } + + # parent; write the job into KID1 aka $enscriptcommand + select KID1; + + my ($make, $model, $driver) + = ($dat->{'make'}, $dat->{'model'}, $dat->{'driver'}); + + my $optstr = ("Specify each option with a -o argument to lp/lpr ie\n", + "% lpr -o duplex -o two=2 -o three=3\n"); + + print "Invokation summary for your $make $model printer as driven by +the $driver driver. + +$optstr + +The following options are available for this printer: + +"; + + for $arg (@{$dat->{'args'}}) { + my ($name, + $required, + $type, + $comment, + $spot, + $default) = ($arg->{'name'}, + $arg->{'required'}, + $arg->{'type'}, + $arg->{'comment'}, + $arg->{'spot'}, + $arg->{'default'}); + + my $reqstr = ($required ? " required" : "n optional"); + print "Option `$name':\n A$reqstr $type argument.\n $comment\n"; + print " This options corresponds to a PJL command.\n" if ($arg->{'style'} eq 'J'); + + if ($type eq 'bool') { + if (defined($default)) { + my $defstr = ($default ? "True" : "False"); + print " Default: $defstr\n"; + } + print " Example: `$name'\n"; + } elsif ($type eq 'enum') { + print " Possible choices:\n"; + my $exarg; + for (@{$arg->{'vals'}}) { + my ($choice, $comment) = ($_->{'value'}, $_->{'comment'}); + print " o $choice: $comment\n"; + $exarg=$choice; + } + if (defined($default)) { + print " Default: $default\n"; + } + print " Example: $name=$exarg\n"; + } 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: $name=$exarg\n"; + } + + print "\n"; + } + + select STDOUT; + close KID1 + or warn "error closign KID1/enscript for docs print"; +} + +# Wait for enscript child +waitpid($pid, 0); +print $logh "Main process finished\n"; +close $logh; +exit(0); + + +## Everything below here *is* the same in lpdomatic and cupsomatic +## KEEP IT THAT WAY! + +# return glob ref to "| commandline | self(pjlstuffer) | $postpipe" +# ugly, we use $commandline, $postpipe, @prepend, @pjlprepend, @pjlappend globals +sub getdriverhandle { + + pipe KID3_IN, KID3; + my $pid3 = fork(); + if (!defined($pid3)) { + print $logh "$0: cannot fork for kid3!\n"; + die "can't for for kid3\n"; + } + if ($pid3) { + + # we are the parent; return a glob to the filehandle + close KID3_IN; + + KID3->flush(); + return ( *KID3, $pid3 ); + + } else { + close KID3; + + pipe KID4_IN, KID4; + my $pid2 = fork(); + if (!defined($pid2)) { + print $logh "$0: cannot fork for kid4!\n"; + die "can't fork for kid4\n"; + } + + if ($pid2) { + # parent, child of primary task; we are |commandline| + close KID4_IN; + + print $logh "gs PID pid2=$pid2\n"; + print $logh "gs command: $commandline\n"; + + close STDIN or die "couldn't close STDIN in $pid2"; + open (STDIN, "<&KID3_IN") or die "Couldn't dup KID3_IN"; + open (STDOUT, ">&KID4") or die "Couldn't dup KID4"; + if ($debug) { + open (STDERR, ">&$logh") + or die "Couldn't dup logh to stderr"; + } + + # 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 !foomatic-gswrapper !; + $commandline =~ s!(\|\s*)gs !\|foomatic-gswrapper !; + $commandline =~ s!(;\s*)gs !; foomatic-gswrapper !; + } + + # Actually run the thing... + system "$commandline" + and die "Couldn't exec $commandline"; + close STDOUT; + close KID4; + close STDIN; + close KID3_IN; + # Wait for output child + waitpid($pid2, 0); + print $logh "KID3 finished\n"; + exit(0); + } else { + # child, trailing task on the pipe; we write pjl stuff + close KID4; + + my $fileh = *STDOUT; + if ($postpipe) { + open PIPE,$postpipe + or "die cannot open postpipe $postpipe"; + $fileh = *PIPE; + } + + # wrap the PJL around the job data... + # wrap the PJL around the job data, if there are any + # options specified... + if ( @pjlprepend > 1 ) { + print $fileh @pjlprepend; + } + while (<KID4_IN>) { + print $fileh $_; + } + if ( @pjlprepend > 1 ) { + print $fileh @pjlappend; + } + + close $fileh or die "error closing $fileh"; + close KID4_IN; + + print $logh "tail process done writing data to $fileh\n"; + + print $logh "KID4 finished\n"; + exit(0); + } + } +} + +# Find an argument by name in a case-insensitive way +sub argbyname { + my $name = @_[0]; + + my $arg; + for $arg (@{$dat->{'args'}}) { + return $arg if (lc($name) eq lc($arg->{'name'})); + } + + return undef; +} + +sub valbyname { + my ($arg,$name) = @_; + + my $val; + for $val (@{$arg->{'vals'}}) { + return $val if (lc($name) eq lc($val->{'value'})); + } + + return undef; +} + +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; +} + + +# Emacs tabulator/indentation + +### Local Variables: +### tab-width: 8 +### perl-indent-level: 4 +### End: |