A printer usually understands one or more Print Job Languages. Files sent to this printer must be in one of these languages and have the appropriate job format. The most common Print Job Languages are PostScript and PCL. Text files are PCL with no special PCL control sequences.
In order for a printer to reliably print a job it needs to be reset to a known configuration and then at the end of job having it flush all of the output to the printing device. This is done by sending it start of job and end of job commands. These commands differ from printer to printer and depend on the print job language as well. Some vintage line printers also have a set of proprietary escape sequences that are used to set up margins, form size, and other printing characteristics. Usually a setup string with these escape sequences must be sent to the printer before a file can be printed.
When sending a job to the printer the print spooler will first process the job using a print job filter or filter program. This program reads the job file and then produces output in the format required for the printer.
When a print job is created the files in the print job are assigned a format. This format was meant as a guide to the print spooler and was to be used to select the filter program for the files in the job. The format was a lower case letter; the f is the default format and indicates normal processing and the l format indicates a literal or binary file. Job files that are flagged as having literal or binary format are usually passed directly to the printer or have at the most a minimal amount of processing. See Print Job Formats for more information about formats and their use with filters.
There are two ways to specify filters: the default :filter=... option and the more specific Xf=... option. The X is a lower case letter corresponding to a format. Here is a sample printcap entry with a filter specification:
All jobs with formats other than r will be processed using the ifhp program while the jobs with the r format will be processed using the rfilter program.We will set up a very simple filter and use it to demonstrate how filtering is done by the lpd print spooler. First, set up the /tmp/testf file as shown below.
#!/bin/sh # /tmp/testf - test filter for LPRng PATH=/bin:/usr/bin; export PATH echo TESTF $0 "$@" >&2 echo TESTF $0 "$@" echo ENV set echo LEADER /bin/cat echo TRAILER exit 0
Let us carefully examine the script line by line. The first
couple of lines are boilerplate. You should always set the
PATH
value in a filter script or use
full pathnames for executable programs. This is a good practice
as it ensures that only the specified directories will be
searched for commands.
The next lines echo the command line arguments to file descriptor 2 (STDERR) and to STDOUT. We will soon see how this information is displayed by the LPRng software. We then use the set command to list the shell variables to STDOUT, print LEADER to STDOUT, copy STDIN to STDOUT, and print TRAILER to STDOUT. We exit with a zero result code.
We can test our script, with the results shown below:
h4: {163} % chmod 755 /tmp/testf h4: {164} % echo hi |/tmp/testf -a1 TESTF /tmp/testf -a1 TESTF /tmp/testf -a1 ENV USER=papowell HOSTNAME=h4 ... PATH=/bin:/usr/bin LEADER hi TRAILER
Let's now use this filter. Edit the lp printcap entry so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.
Execute the following commands to print the /tmp/hi file and observe the results:
h4: {165} % cp /dev/null /tmp/lp h4: {166} % lpr /tmp/hi h4: {167} % lpq -llll Printer: lp@h4 Queue: no printable jobs in queue Status: lp@h4.private: job 'papowell@h4+26593' printed at 21:37:21.312 Status: job 'papowell@h4+26593' removed at 21:37:21.323 Status: subserver pid 26683 starting at 21:39:21.908 Status: accounting at start at 21:39:21.908 Status: opening device '/tmp/lp' at 21:39:21.909 Status: printing job 'papowell@h4+26681' at 21:39:21.909 Status: no banner at 21:39:21.909 Status: printing data file 'dfA026681h4.private', size 3, \ IF filter 'testf' at 21:39:21.909 Status: IF filter msg - 'TESTF /tmp/testf -Apapowell@h4+26681 \ -CA -D2000-04 -11-21:39:21.877 -Ff -Hh4.private -J/tmp/hi \ -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \ -edfA026681h4.private -f/tmp/hi -hh4.private -j026681 \ -kcfA026681h4.private -l66 -npapowell -sstatus \ -t2000-04-11-21:39:21.000 -w80 -x0 -y0 acct' \ at 21:39:21.914 Status: IF filter finished at 21:39:22.070 Status: printing done 'papowell@h4+26681' at 21:39:22.070 Status: accounting at end at 21:39:22.070 Status: finished 'papowell@h4+26681', status 'JSUCC' at 21:39:22.070 Status: subserver pid 26683 exit status 'JSUCC' at 21:39:22.072 Status: lp@h4.private: job 'papowell@h4+26681' printed at 21:39:22.072 Status: job 'papowell@h4+26681' removed at 21:39:22.085 h4: {168} % more /tmp/lp TESTF /tmp/testf -Apapowell@h4+26681 -CA -D2000-04-11-21:39:21.877 \ -Ff -Hh4.private -J/tmp/hi -Lpapowell -Plp -Qlp -aacct -b3 \ -d/var/tmp/LPD/lp -edfA026681h4.private -f/tmp/hi -hh4.private \ -j026681 -kcfA026681h4.private -l66 -npapowell -sstatus \ -t2000-04-11-21:39:21.000 -w80 -x0 -y0 acct ENV USER=papowell LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib HOME=/home/papowell PRINTCAP_ENTRY=lp :force_localhost :filter=/tmp/testf :lp=/var/tmp/lp :sd=/var/tmp/LPD/lp PS1=$ OPTIND=1 PS2=> SPOOL_DIR=/var/tmp/LPD/lp LOGNAME=papowell CONTROL=Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+15850 D2000-04-26-18:13:55.505 Qlp N/tmp/hi fdfA015850h4.private UdfA015850h4.private PATH=/bin:/usr/bin SHELL=/bin/sh LOGDIR=/home/papowell IFS= PRINTER=lp LEADER test Test TRAILER
The cp command clears out the /tmp/lp file we are using as a dummy output device. The lpr command prints the /tmp/hi file and the lpq -llll command shows the status information. The status information now contains the line that the testf script wrote to STDERR. The lpd server captures filter STDERR messages and puts it them in the spool queue status file.
As we see from the lpq status, lpd passes a large number of command line options to our filter. These options and their meanings are discussed in detail in Filter Command Line Options and Environment Variables. We will discuss these in more detail in the next section.
If we look at the /tmp/lp file, we
see the command line options and values of the shell variables.
For a full discussion of the environment variables passed to a
filter see Filter Command Line
Options and Environment Variables. The more interesting
environment variables include PRINTCAP_ENTRY
- the printcap entry for this
printer, CONTROL
- the control file,
and HF
- the hold file.
When you transfer a print job the lpd print spooler will send the job as two or more files: control file that contains information about the job and data files that contain the information to be printed. Here is sample control file:
Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+15850 D2000-04-26-18:13:55.505 Qlp N/tmp/hi fdfA015850h4.private UdfA015850h4.private
Lines starting with upper case letters contain job information such as the user who submitted the job. Lines starting with lower case letters indicate the data file to be printed and the corresponding format. For full details about the exact format of the control file see Job Files.
Table 4-1 shows the correspondence between lines in the control file and lpr command line options. The N values are the names of the files that are printed. The U indicates a data file is in a job and is present to meet RCF1179 and vintage print spooler requirements.
Table 4-1. Filter Options
Control File | Filter Option | Purpose or Value |
---|---|---|
-PPrinter | Print queue name - printcap information | |
H | -HHost | Host Name |
P | -nUser | User Login Name of job originator |
J | -JJob name | lpr -J option or file name |
C | -CClass | Print class (lpr -C option) |
L | -LBanner | Banner page request |
A | -AJobid | Job Id |
D | -DDate | Date or time information |
Q | -QQueue | Original Print queue job was sent to |
N | -NFilename | Filename |
f,l,p,... | -Ff | Datafile format |
U | Datafile (historical) |
When a print filter processes these jobs the values in the control file are passed on the command line as options starting with upper case letters:
Sometimes we want to pass only a small subset of these command line options to a filter or provide them in a specific order in order to be compatible with legacy print filters. LPRng provides several different ways to do this and we will explore how to control command line options.If the filter entry starts with -$, this suppresses the automatic addition of command line options; we can then add our own options to the command line. Modify the printcap entry to have the following form:
lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter= -$ /tmp/testf '$P' $0P -X$-P ${lp} G\072 or \:
Lets print our /tmp/hi test file and then look at the lpq status:
h4: {169} % cp /dev/null /tmp/lp h4: {170} % lpr /tmp/hi h4: {171} % lpq -llll Printer: lp@h4 .... Status: IF filter msg - 'TESTF /tmp/testf -Plp -P lp -Xlp \ -Ylp /tmp/lp G: or :' at 01:20:21.560
The -$ suppresses the adding the default literals to the filter command line. You can pass specific options using $X; if the option has a non-null value then it will be expanded in the following format:
Option Value Expansion $X -X<value> $'X -X'<value>' $0X -X '<value>' $-X <value> $'-X '<value>' ${X} control file X option value $'{X} control file X option value (in quotes) ${name} printcap option value if value nonzero length $'{name} printcap option value if value nonzero length in quotes \nnn single printable character $* all options in control file expanded using $X
Command line options can be grouped and passed as a single argument by enclosing them in single or double quotes. You should be aware that LPRng has an extremely primitive way of handling quotes. When the /bin/sh -c parameter is not used, the the command line is broken on spaces and each unit is passed as an individual argument. If the first character after a space is a quote (single or double), the next quote is found, and then entire element is then used as a single parameter. Substitution of $X parameters is then done. As a special case, when you have a $0X, this causes a split and all of the string previous and including the -X flag is passed as a single option and all of the option value and following are passed as another option. If the result of the expansion is a zero length parameter then it is removed from the parameter list. When the /bin/sh -c is used the command line is not broken, and all non-empty option values are enclosed in single quotes.
The ${name} option is used to pass a printcap option value. For example, you can pass the value of the printcap option form as shown below. You can experiment with this by using the /tmp/testf filter and printcap shown below.
printcap: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=/tmp/testf -F ${form} :form=payroll h4: {172} % cp /dev/null /tmp/lp h4: {173} % lpr /tmp/hi h4: {174} % lpq -llll Printer: lp@h4 ... Status: IF filter msg - 'TESTF /tmp/testf -F payroll' at 09:55:31.276 ...
If we have a legacy print filter that was originally written for the BSD print spooler, then we may find that it requires a small number of command line options in a very specific order. We can use the :bkf (BSD Kompatible Filter or BacKwards compatible Filter) flag to pass suitable options. Modify the printcap entry to have the following form:
Lets print our /tmp/hi test file and then look at the lpq status:
h4: {175} % cp /dev/null /tmp/lp h4: {176} % lpr /tmp/hi h4: {177} % lpq -llll Printer: lp@h4 .... Status: IF filter msg - 'TESTF /tmp/testf -Plp -w80 -l66 \ -x0 -y0 -Ff -Lpapowell -J/tmp/hi -CA -n papowell \ -h h4.private acct' at 08:07:46.583
Finally, there are times when we would like the print filter to be a simple shell command or to chain several programs together in a simple pipeline. While this is possible using a print filter, you can also do this in the filter specification. If your filter specification starts with a parenthesis (() or contains the IO redirection for pipeto (|), input redirection (<), or output redirection (>) then the lpd server will use the :shell configuration option value (default /bin/sh) and execute it using:
If this is done, then no command line options are added to the command. However, expansion of $X parameters are still done. Modify the printcap entry to have the following form:
lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=(echo "PREAMBLE"; /tmp/testf; echo "APPENDIX")
Lets print our /tmp/hi test file and then look at the lpq status:
h4: {178} % cp /dev/null /tmp/lp h4: {179} % lpr /tmp/hi h4: {180} % lpq -llll Printer: lp@h4 .... Status: printing data file 'dfA018881h4.private', size 3, \ IF filter 'echo' at 09:22:11.476 Status: IF filter msg - 'TESTF /tmp/testf' at 09:22:11.510 Status: IF filter finished at 09:22:11.514
If we examine the /tmp/lp file we find:
PREAMBLE TESTF /tmp/testf ENV USER=papowell LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib ... PRINTER=lp LEADER hi TRAILER APPENDIX
As we expected, no options were passed on the command line. If the printcap is modified to have the following contents, then you will see:
lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=(echo "PREAMBLE"; /tmp/testf $*; echo "APPENDIX") h4: {181} % lpr /tmp/hi h4: {182} % lpq -llll Printer: lp@h4 .... Status: IF filter msg - 'TESTF /tmp/testf -Apapowell@h4+18941 \ -CA -D2000-04-29-09:27:30.700 -Ff -Hh4.private -J/tmp/hi \ -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \ -edfA018941h4.private -f/tmp/hi -hh4.private -j018941 \ -kcfA018941h4.private -l66 -npapowell -sstatus \ -t2000-04-29-09:27:30.864 -w80 -x0 -y0 acct' at 09:27:30.879
Using the shell invocation is especially useful when you may have a parameter that has an empty string value, and need to pass this as a command line parameter. Modify the /tmp/testf filter, the printcap, and execute the following commands:
printcap: lp:sd=/var/spool/lpd/%P :force_localhost :lp=/tmp/lp :filter=( /tmp/testf -F '${form}' ) :form= #!/bin/sh # /tmp/testf - test filter for LPRng PATH=/bin:/usr/bin; export PATH echo TESTF $0 "$@" >&2 echo TESTF $0 "$@" while test $# -gt 0 ; do echo "PARM '$1'"; shift; done echo LEADER /bin/cat echo TRAILER exit 0 h4: {183} % cp /dev/null /tmp/lp h4: {184} % lpr /tmp/hi h4: {185} % lpq -llll Printer: lp@h4 ... Status: IF filter msg - 'TESTF /tmp/testf -F' at 09:59:27.365 h4: {186} % more /tmp/lp TESTF /tmp/testf -F PARM '-F' PARM '' LEADER hi TRAILER
As you can see, there are empty parameters passed to the filter. This is due to the combination of the $'{form} and using the :filter=(...) form.
In this section we will look further at the environment variables passed to the filter. We printed the shell variable values for the filter at the start of the file:
h4: {187} % cat /tmp/lp /tmp/testf -Plp -P lp -Xlp -Ylp /tmp/lp G:' at 01:20:21.560 ENV CONTROL=Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+105 D2000-04-12-15:27:26.662 Qlp N/tmp/hi fdfA105h4.private UdfA105h4.private HF=H=h4.private P=papowell J=/tmp/hi C=A L=papowell A=papowell@h4+105 D=2000-04-12-15:27:26.662 Q=lp transfername=cfA105h4.private datafiles=N=/tmp/hi,transfername=fdfA105h4.private, HOME=/home/daemon LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib LOGDIR=/home/daemon LOGNAME=daemon OPTIND=1 PATH=/bin:/usr/bin:/usr/local/bin PRINTCAP_ENTRY=lp :force_localhost :filter=/tmp/testf :lp=/tmp/lp :sd=/tmp/LPD/lp PRINTER=lp PS1=$ PS2=> SHELL=/bin/sh SPOOL_DIR=/tmp/LPD/lp USER=daemon LEADER hi TRAILER
The HOME
, USER
, SHELL
,
PS1
, and PS2
variables are usually set by the shell,
and are reflect the information for the UID of the user running the shell.
The PATH
and LP_LIBRARY_PATH are set by the lpd server to values specified in the
printcap or configuration information. It is recommended that
users set these to site specific values if the defaults are
not suitable for their sites.
The lpd server sets the PRINTER, PRINTCAP_ENTRY, CONTROL and HF environment variables to the printer name, printcap entry, control file, and hold file for the print job. This information is very useful to filters that must make decisions based on values passed to the print server in the control file and which use parameters in the printcap entry to control their actions.
One of the problems commonly encountered problem in
writing a filter is getting the command line values. The UNIX
POSIX Standard provides a C Language getopt
function that can be used for
command line options, and some, but not all shell
implementations have a corresponding shell getopt
function. Also, many times it would
be useful to get the values of the printcap options. These
could be used to specify options or operations that are not
easily done by passing command lines.
Observe that all the command line options are single letters. If we set the shell variables to the corresponding option value, then we could access them by using $x, where x is the option letter. There is an exception to this rule, which is the -c command line literal, which for various historical and compatibility reasons does not take a value. But if it is present, we might as well assign it the value 1.
Observe that by convention all printcap options have lowercase names of two or more letters, and that all environment variables have all upper case letters. If we set shell variables with the corresponding printcap entry values, then we can access them using $literal. If we need to create a local shell variable for use, we can use mIxEd case and not have a conflict.
The decode_args_with_sh script which is in the UTILS directory of the LPRng distribution follows these conventions and sets the appropriate shell variables. We have also include a bit of code that will extract the control file control line values and put them into variables as well.
Save the current /tmp/testf filter file in /tmp/testf.old and replace it with the following:
#!/bin/sh # this is an example of how to use /bin/sh and LPRng # to get the command line and printcap option values # and set shell variables from them # Note that we use a couple of variables #PATH=/bin:/usr/bin Args="" vAr="" vAlue="" vAls="" iI="" Tf="" Debug=1 if -n $Debug ; then set >/tmp/before fi Args="$@" if -n $Debug ; then echo "$@" >>/tmp/before fi while expr "$1" : '-.*' >/dev/null ; do vAr=`expr "$1" : '-\(.\).*'`; vAlue=`expr "$1" : '-.\(.*\)`; case "$vAr" in - ) break;; c ) c=1;; [a-zA-Z] ) if test "X$vAlue" = "X" ; then shift; vAlue=$1; fi; eval $vAr='$vAlue'; #setvar $vAr "$vAlue" ;; esac; shift; done # set shell variables to the printcap options # flag -> flag=1 # flag@ -> flag=0 # option=value -> option='value' # setpcvals () { while test "$#" -gt 0 ; do iI=$1 if expr "$iI" : " *\:" >/dev/null ; then vAr=`expr "$iI" : " *\:\([^=][^=]*\)=.*"`; vAlue=`expr "$iI" : " *\:[^=][^=]*=\(.*\)"`; if test "X$vAr" = "X" ; then vAr=`expr "$iI" : " *:\(.*\)@"`; vAlue=0; fi if test "X$vAr" = "X" ; then vAr=`expr "$iI" : " *:\(.*\)"`; vAlue=1; fi if test "X$vAr" != "X" ; then eval $vAr='$vAlue'; #setvar $vAr "$vAlue" fi else vAr=`expr "$iI" : " *\([^|][^|]*\).*"`; if test "X$vAr" != "X" ; then eval Printer="$vAr" fi fi; shift done } # set shell variables to the printcap options # flag -> flag=1 # flag@ -> flag=0 # option=value -> option='value' # setcontrolvals () { while test "$#" -gt 0 ; do iI=$1 vAr=`expr "$iI" : " *\([A-Z]\).*"`; vAlue=`expr "$iI" : " *[A-Z]\(.*\)"`; if test "X$vAr" != "X" ; then eval $vAr='$vAlue'; #setvar $vAr "$vAlue"; fi; shift done } Tf=$IFS IFS=" " setpcvals $PRINTCAP_ENTRY setcontrolvals $CONTROL IFS=$Tf # # restore argument list set -- $Args Args="" vAr="" vAlue="" vAls="" iI="" Tf="" if test -n "$Debug" ; then set >/tmp/after echo "$@" >>/tmp/after diff /tmp/before /tmp/after fi /bin/cat exit 0
Lets print our /tmp/hi test file and then look at the results in /tmp/lp:
h4: {188} % cp /dev/null /tmp/lp h4: {189} % lpr /tmp/hi h4: {190} % more /tmp/lp 0a1 > e=dfA021771h4.private 2a4,6 > l=66 > s=status > L=papowell 10a15,17 > j=021771 > C=A > J=/tmp/hi 12a20 > a=acct ... 33a58 > Printer=lp ... hi
As we see from the output, shell variables have the values of our command line and printcap options. It is left as an exercise for the reader to add the necessary export statements to cause these values to be exported to subshells. It is not recommended that a wholesale export of the shell variables be done, but only selected ones.
The paranoid and security minded reader will see some possible security problem with this script. The eval $vAr='$vAlue' command sets the value of the shell variable $vAr to the value $vAlue. The $vAr variable is always taken from either a single letter or is the name of an option in the printcap file. Clearly the printcap file must not be modifiable by users, and should have the same security considerations as any other system configuration file. The values of the $vAlue are taken directly from the control file, whose contents are under the control of the originator of the print job request.
For this reason LPRng takes the rather brutal step of sanitizing the control file. Only alphanumerics or a character in the list @/:()=,+-%_ are used in the control file; all others replaced by the underscore (_) character. In addition, all filters are run as the lpd user specified in the lpd.conf configuration file.
The following is an example of how to extract the same information in Perl:
#!/usr/bin/perl eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if $running_under_some_shell; # this emulates #! processing on NIH machines. # (remove #! line above if indigestible) use Getopt::Std; my(%args,%options); # get the arguments getopt( "a:b:cd:e:f:g:h:i:j:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:" . "A:B:C:D:E:F:G:H:I:J:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:", \%args ); # set :key=value -> $option{$key}=$value # set :key@ -> $option{$key}="0" # set :key -> $option{$key}="1" map { if( m/^\s*:([^=]+)=(.*)/ ){ $options{$1}=$2; } elsif( m/^\s*:([^=]+)\@$/ ){ $options{$1}="0"; } elsif( m/^\s*:([^=]+)/ ){ $options{$1}="1"; } elsif( m/^\s*([^|]+)/ ){ $options{"Printer"}=$1; } } split( "\n", $ENV{'PRINTCAP_ENTRY'}); # get the control file entries map { if( m/^\s*([A-Z])(.*)/ ){ $options{$1}=$2; } elsif( m/^\s*([a-z])/ ){ $options{'Format'}=$1; } } split( "\n", $ENV{'CONTROL'});
The Perl Getopt::Std routine
parses the command line options and puts their values in the
%args hash variable where they can
be accessed using $args{'x'}.
Similarly, the map and split functions process the PRINTCAP_ENTRY and
CONTROL environment variable and set %options with the printcap entry options and
the values from the control file. The map
function could be replaced by a
foreach
loop, but this is Perl:
There is more than
one way to do it and no tutorial would be complete
without at least one mind stretching example that has the
reader reaching for the reference manual.
The lpd server uses the exit code of the filter to determine if the filter was successful or unsuccessful. The Filter Exit Codes section discusses these values in detail, but here are the most important:
A JSUCC exit code indicates that the filter was successful in doing its work.
A JFAIL exit code indicates that the filter was unsuccessful in doing its work, possibly due to a transient condition such as out of paper, printer jam, etc., but an additional attempt might be successful. Usually the lpd server will try at most send_try attempts before giving up.
A JABORT exit code indicates that the filter was unsuccessful in doing its work and has detected a condition that would make it impossible to print the job. In addition, the printer may require administrative attention, and the print queue operation may need to be suspended until the problem is rectified.
The JREMOVE exit code will cause the job to be removed from the print queue.
The JHOLD exit code will cause the job to be temporarily prevented from printing until release by the lpc release command.
Usually any other value, including exit due to a signal, is treated as a JABORT exit, and the same action is taken.
It should be obvious that the filter exit code is very important, and that care needs to be taken to return the correct value.
In the previous sections we discussed how a print filter was executed and how it could be used. Now we will look at how the lpd spooler chooses a print filter program. Let us re-examine our example print job control file:
Hh4.private Ppapowell J/tmp/hi CA Lpapowell Apapowell@h4+105 D2000-04-12-15:27:26.662 Qlp N/tmp/hi fdfA105h4.private UdfA105h4.private
Each data file for a print job has a name with the format dfXnnnh4.private. The df is used to indicate that the file is a data file, and the remainder is a unique name for the file in the job. The X part of the name must be an upper or lower case letter, setting a limit of 52 different files in a single print job.
The fdfA105h4.private line in the control file specifies that we are to print the job using the filter for the f format; the file printing information consists of the format assigned to the file and the name of the file. In the legacy BSD print spoolers, this format was used to select the print filter to be used. LPRng has expanded this by providing a default filter specification.
Table 4-2. Job Formats and Filter Selection
lpr command line option | Control File Line | Printcap Option For Filter | Filter Command Line |
---|---|---|---|
(default) | fdfAnnn | :if=/path | /path -Ff ... |
-b or -l | ldfAnnn | :if=/path | /path ... -Ff -c |
-p | pdfAnnn | :if=/path | pr | format f filter |
-c, -d, -n, -r, -t, -v, -FX | XdfAnnn | :Xf=/path ... | /path -FX |
any format | XdfAnnn | :filter=/path ... | /path -FX |
Table 4-2 shows the rather baroque relationship between the format options specified by the lpr command and the way that lpd uses them. The reason for this complexity lies in the various implementations and variations that occurred during the development and deployment of the original BSD print spooling software.
Here is the complete, arcane, and baroque set of rules that are used to select and print filters. The default format used by lpr is f; unless some other format is specified this is used. The lpr -b and lpr -l (binary and literal literals) are a request to lpd to do as little processing as possible of this file before printing it. Lpd use the :if filter for formats f and l; the l literal causes the the -c filter command line flag to be used as well. The lpr -c, -d, -n, -r, -t, and -v options cause the corresponding format to be used, and for lpd to use the filter specified by the printcap option :Xf, where X is the specified format.
The lpr -p (pretty-print literal) selects p format, and lpd is supposed to use the program specified by the :pr printcap option to format the file and then process the output of this program according to format f. Unpredictable results may occur using this facility.
the lpr -FX allows you to explicitly specify format X where X is a lower case letter, and lpd will use the filter specified by printcap option :Xf, where X is the specified format. If there is no :Xf printcap literal value then the printcap :filter literal value will be used as the filter, and if this is undefined then the file will be passed without processing through to the printing device.
If a filter is not specified for the format then the default filter specified by :filter=/path filter is used, and if there is no default, then the output is sent directly to the output device.
If the :fx=formats is present in a printcap entry, it specifies the formats that are allowed. For example, :fx=lfv would allow only formats l, f, and v to be used on a particular spool queue.
Some Xf options have pre-assigned meanings and cannot be used for filter selection.
Printcap Option | Purpose |
---|---|
Printcap Option | Purpose |
:af=/path | Accounting File |
:ff=formfeed | Form Feed String |
:if=/path | filter (l,b,p,f formats) |
:filter=/path | Default filter |
:lf=/path | Log file |
:of=/path | OF filter |
:sf | suppress form feed between job files |
The :of filter is a special case and is used for banner printing and accounting purposes. See OF Filter for details.