1045 lines
25 KiB
Plaintext
Executable File
1045 lines
25 KiB
Plaintext
Executable File
#ident "@(#)standard 1.10 94/03/31 SMI" /* SVr4.0 1.26 */
|
|
|
|
###########
|
|
##
|
|
## Standard printer interface program.
|
|
###########
|
|
|
|
#####
|
|
#
|
|
# Until we get to the point below where the printer port
|
|
# and physical printer are initialized, we can't do much
|
|
# except exit if the Spooler/Scheduler cancels us.
|
|
#####
|
|
trap 'exit' 15
|
|
|
|
#####
|
|
#
|
|
# We can be clever about getting a hangup or interrupt, though, at least
|
|
# until the filter runs. Do this early, even though $LPTELL
|
|
# isn't defined, so that we're covered.
|
|
#####
|
|
catch_hangup () {
|
|
if [ -n "${LPTELL}" ]
|
|
then
|
|
echo \
|
|
"The connection to the printer dropped; perhaps the printer went off-line?" \
|
|
| ${LPTELL} ${printer}
|
|
fi
|
|
return 0
|
|
}
|
|
catch_interrupt () {
|
|
if [ -n "${LPTELL}" ]
|
|
then
|
|
echo \
|
|
"Received an interrupt from the printer. The reason is unknown,
|
|
although a common cause is that the baud rate is too high." \
|
|
| ${LPTELL} ${printer}
|
|
fi
|
|
return 0
|
|
}
|
|
trap 'catch_hangup; exit_code=129 exit 129' 1
|
|
trap 'catch_interrupt; exit_code=129 exit 129' 2 3
|
|
|
|
#####
|
|
#
|
|
# Most of the time we don't want the standard error to be captured
|
|
# by the Spooler, mainly to avoid "Terminated" messages that the
|
|
# shell puts out when we get a SIGTERM. We'll save the standard
|
|
# error channel under another number, so we can use it when it
|
|
# should be captured.
|
|
#
|
|
# Open another channel to the printer port, for use when the
|
|
# regular standard output won't be directed there, such as in
|
|
# command substitution (`cmd`).
|
|
#####
|
|
exec 5>&2 2>/dev/null 3>&1
|
|
|
|
#####
|
|
#
|
|
# Set some globally used variables and functions.
|
|
#####
|
|
|
|
: ${TMPDIR:=/tmp}
|
|
: ${SPOOLDIR:=/usr/spool/lp}
|
|
: ${TERMINFO:=/usr/lib/terminfo}
|
|
: ${CHARSETDIR:=/usr/lib/charsets}
|
|
|
|
: ${LOCALPATH:=${SPOOLDIR}/bin}
|
|
PATH="/bin:/usr/bin:${LOCALPATH}"
|
|
|
|
MAX_COLS_SMALL_BANNER=40
|
|
|
|
#####
|
|
#
|
|
# On the 3.2 release of the 386unix product, the parallel port does
|
|
# not support any ioctl calls. As a result, we cannot set the opost
|
|
# and onlcr attributes to have <NL>'s expanded to <CR><NL>. This
|
|
# "filter" gets the job done for us.
|
|
#####
|
|
: ${FIX386BD:=${LOCALPATH}/386parallel}
|
|
if [ -n "${FIX386BD}" -a -x "${FIX386BD}" ]
|
|
then
|
|
FIX386BD="| ${FIX386BD}"
|
|
else
|
|
FIX386BD=""
|
|
fi
|
|
|
|
#####
|
|
# Use ${TMPPREFIX} as the prefix for all temporary files, so
|
|
# that cleanup is easy. The prefix may be up to 13 characters
|
|
# long, so you only have space for one more character to make
|
|
# a file name. If necessary, make a directory using this prefix
|
|
# for better management of unique temporary file names.
|
|
#####
|
|
TMPPREFIX=${TMPDIR}/`uname -n`$$
|
|
|
|
#####
|
|
# Before exiting, set ${exit_code} to the value with which to exit.
|
|
# Otherwise, the exit from this script will be 0.
|
|
#####
|
|
trap 'rm -fr ${TMPPREFIX}*; exit ${exit_code}' 0
|
|
|
|
#####
|
|
# ${LPTELL} is the name of a program that will send its
|
|
# standard input to the Spooler. It is used to forward
|
|
# the description of a printer fault to the Spooler,
|
|
# which uses it in an alert to the administrator.
|
|
#####
|
|
if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ]
|
|
then
|
|
fake_lptell () {
|
|
header="no"
|
|
while read line
|
|
do
|
|
if [ "no" = "${header}" ]
|
|
then
|
|
errmsg ERROR ${E_IP_UNKNOWN} \
|
|
"unknown printer/interface failure" \
|
|
"consult your system administrator;
|
|
reasons for failure (if any) follow:"
|
|
header=yes
|
|
fi
|
|
echo "${line}" >&2
|
|
done
|
|
return 1
|
|
}
|
|
LPTELL=fake_lptell
|
|
fi
|
|
|
|
#####
|
|
# ${DRAIN} is the name of a program that will wait
|
|
# long enough for data sent to the printer to print.
|
|
#####
|
|
if [ -x "${LOCALPATH}/drain.output" ]
|
|
then
|
|
DRAIN="${LOCALPATH}/drain.output 5" # wait only five seconds
|
|
else
|
|
DRAIN=
|
|
fi
|
|
|
|
#####
|
|
# ${LPCAT} is the name of a program to use as a default
|
|
# filter. Minimally it should copy its standard input to
|
|
# the standard output, but it should also trap printer
|
|
# faults. The current LPCAT traps hangups (DCD dropping, SIGHUP),
|
|
# interrupts (SIGINT, SIGQUIT), broken pipe (SIGPIPE), and
|
|
# excess delays in sending data to the printer, interpreting all
|
|
# as printer faults.
|
|
#####
|
|
if [ ! -x "${LPCAT:=${LOCALPATH}/lp.cat}" ]
|
|
then
|
|
LPCAT="cat"
|
|
fi
|
|
|
|
#####
|
|
# ${LPSET} is the name of a program that will set the
|
|
# character pitch, line pitch, page width, page length,
|
|
# and character set. It helps to have this in a single
|
|
# binary program so that (1) it's faster than calls
|
|
# to "tput"; and (2) it can access the new Terminfo
|
|
# capabilities for printers (on pre SVR3.2 machines, tput can't).
|
|
#####
|
|
if [ ! -x "${LPSET:=${LOCALPATH}/lp.set}" ]
|
|
then
|
|
fake_lpset () {
|
|
echo H V W L S >&2
|
|
false
|
|
}
|
|
LPSET=fake_lpset
|
|
fi
|
|
|
|
internal_lpset () {
|
|
#####
|
|
#
|
|
# The funny business with the "2>&1 1>&3" is to let us capture
|
|
# the standard ERROR, not the standard OUTPUT as is the usual case
|
|
# with foo=`cmd`. The standard output will go to the printer.
|
|
#####
|
|
[ -n "${stty1}" ] && stty ${stty1} 0<&1
|
|
chk=`${LPSET} "$1" "$2" "$3" "$4" "$5" 2>&1 1>&3`
|
|
[ -n "${stty2}" ] && stty ${stty2} 0<&1
|
|
|
|
#####
|
|
#
|
|
# The standard error of the delivered ${LPSET} program
|
|
# is a string of letters, H, V, W, L, S, which correspond
|
|
# to cpi, lpi, width, length, and character set. A letter
|
|
# is present only if the corresponding attribute could not
|
|
# be set.
|
|
#####
|
|
for err in ${chk}
|
|
do
|
|
case ${err} in
|
|
H )
|
|
errmsg WARNING ${E_IP_BADCPI} \
|
|
"can't select the character pitch \"${cpi}\"" \
|
|
"check the valid pitches for the printer,
|
|
or consult your system administrator;
|
|
printing continues"
|
|
;;
|
|
V )
|
|
errmsg WARNING ${E_IP_BADLPI} \
|
|
"can't select the line pitch \"${lpi}\"" \
|
|
"check the valid pitches for the printer,
|
|
or consult your system administrator;
|
|
printing continues"
|
|
;;
|
|
W )
|
|
width=${cols}
|
|
errmsg WARNING ${E_IP_BADWIDTH} \
|
|
"can't select the page width \"${width}\"" \
|
|
"check the valid widths for the printer,
|
|
or consult your system administrator;
|
|
printing continues"
|
|
;;
|
|
L )
|
|
length=${lines}
|
|
errmsg WARNING ${E_IP_BADLENGTH} \
|
|
"can't select the page length \"${length}\"" \
|
|
"check the valid lengths for the printer,
|
|
or consult your system administrator;
|
|
printing continues"
|
|
;;
|
|
S )
|
|
errmsg WARNING ${E_IP_BADCHARSET} \
|
|
"can't select the character set \"${CHARSET}\"" \
|
|
"check the name given in the -S option,
|
|
or consult your system administrator;
|
|
printing continues"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
|
|
#####
|
|
# ${TPUT} is "tput" IF it works. We'll disable it if we get an
|
|
# ugly error message the first time we use it. See the TERM variable
|
|
# later in the script.
|
|
#
|
|
# NOTE: The check we use to see if "tput" works is to use an OLD
|
|
# Terminfo capability, like "lines". If it works with that it may
|
|
# still fail with some of the newer capabilities like "init" (SVR3.0)
|
|
# or "swidm" (SVR3.2), because the version of "tput" we have on your
|
|
# machine is older. Thus, on some of the code where ${TPUT} is used
|
|
# you'll see "2>/dev/null" being used to avoid ugly error messages.
|
|
#####
|
|
TPUT=tput
|
|
|
|
#####
|
|
# Error message formatter:
|
|
#
|
|
# Invoke as
|
|
#
|
|
# errmsg severity message-number problem help
|
|
#
|
|
# where severity is "ERROR" or "WARNING", message-number is
|
|
# a unique identifier, problem is a short description of the
|
|
# problem, and help is a short suggestion for fixing the problem.
|
|
#####
|
|
|
|
LP_ERR_LABEL="UX:lp"
|
|
|
|
E_IP_ARGS=1
|
|
E_IP_OPTS=2
|
|
#E_IP_FILTER=3
|
|
E_IP_STTY=4
|
|
E_IP_UNKNOWN=5
|
|
E_IP_BADFILE=6
|
|
E_IP_BADCHARSET=7
|
|
E_IP_BADCPI=8
|
|
E_IP_BADLPI=9
|
|
E_IP_BADWIDTH=10
|
|
E_IP_BADLENGTH=11
|
|
E_IP_ERRORS=12 # (in slow.filter)
|
|
|
|
errmsg () {
|
|
case $1 in
|
|
ERROR )
|
|
sev=" ERROR";
|
|
;;
|
|
WARNING )
|
|
sev="WARNING";
|
|
;;
|
|
esac
|
|
# tag=`expr "${LP_ERR_LABEL}" : "\(.*\):"``expr "${LP_ERR_LABEL}" : ".*:\(.*\)"`
|
|
echo "${LP_ERR_LABEL}: ${sev}: $3
|
|
TO FIX: $4" >&5
|
|
}
|
|
|
|
|
|
###########
|
|
##
|
|
## Check arguments
|
|
###########
|
|
|
|
parse () {
|
|
echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
|
|
}
|
|
|
|
#####
|
|
#
|
|
# This program is invoked as
|
|
#
|
|
# ${SPOOLDIR}/.../printer request-id user title copies options files...
|
|
#
|
|
# The first three arguments are simply reprinted on the banner page,
|
|
# the fourth (copies) is used to control the number of copies to print,
|
|
# the fifth (options) is a blank separated list (in a single argument)
|
|
# of user or Spooler supplied options (without the -o prefix),
|
|
# and the last arguments are the files to print.
|
|
#####
|
|
|
|
if [ $# -lt 5 ]
|
|
then
|
|
errmsg ERROR ${E_IP_ARGS} \
|
|
"wrong number of arguments to interface program" \
|
|
"consult your system administrator"
|
|
exit 1
|
|
fi
|
|
|
|
printer=`basename $0`
|
|
request_id=$1
|
|
user_name=$2
|
|
title=$3
|
|
copies=$4
|
|
option_list=$5
|
|
|
|
shift 5
|
|
files="$*"
|
|
|
|
nobanner="no"
|
|
nofilebreak="no"
|
|
stty=
|
|
|
|
inlist=
|
|
for i in ${option_list}
|
|
do
|
|
case "${inlist}${i}" in
|
|
|
|
|
|
nobanner )
|
|
nobanner="yes"
|
|
;;
|
|
|
|
nofilebreak )
|
|
nofilebreak="yes"
|
|
;;
|
|
|
|
#####
|
|
#
|
|
# If you want to add simple options (e.g. -o simple)
|
|
# identify them here.
|
|
#####
|
|
# simple )
|
|
# simple="yes"
|
|
# ;;
|
|
|
|
|
|
cpi=pica )
|
|
cpi=10
|
|
;;
|
|
cpi=elite )
|
|
cpi=12
|
|
;;
|
|
cpi=* )
|
|
cpi=`parse ${i}`
|
|
;;
|
|
|
|
lpi=* )
|
|
lpi=`parse ${i}`
|
|
;;
|
|
|
|
length=* )
|
|
length=`parse ${i}`
|
|
;;
|
|
|
|
width=* )
|
|
width=`parse ${i}`
|
|
;;
|
|
|
|
#####
|
|
#
|
|
# If you want to add simple-value options (e.g. -o value=a)
|
|
# identify them here.
|
|
#####
|
|
# value=* )
|
|
# value=`parse ${i}`
|
|
# ;;
|
|
|
|
|
|
#####
|
|
#
|
|
# If you want to add options that, like "stty",
|
|
# take a list (e.g. -o lopt='a b c'), identify
|
|
# them here and below (look for LOPT).
|
|
#####
|
|
stty=* | flist=* | lpd=* )
|
|
#LOPT stty=* | flist=* | lpd=* | lopt=* )
|
|
|
|
inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"`
|
|
case "${i}" in
|
|
${inlist}\'*\' )
|
|
item=`expr "${i}" : "^[^=]*='*\(.*\)'\$"`
|
|
;;
|
|
${inlist}\' )
|
|
continue
|
|
;;
|
|
${inlist}\'* )
|
|
item=`expr "${i}" : "^[^=]*='*\(.*\)\$"`
|
|
;;
|
|
${inlist}* )
|
|
item=`expr "${i}" : "^[^=]*=\(.*\)\$"`
|
|
;;
|
|
*\' )
|
|
item=`expr "${i}" : "^\(.*\)'\$"`
|
|
;;
|
|
* )
|
|
item="${i}"
|
|
;;
|
|
esac
|
|
|
|
#####
|
|
#
|
|
# We don't dare use "eval" because a clever user could
|
|
# put something in an option value that we'd end up
|
|
# exec'ing.
|
|
#####
|
|
case "${inlist}" in
|
|
stty= )
|
|
stty="${stty} ${item}"
|
|
;;
|
|
flist= )
|
|
flist="${flist} ${item}"
|
|
;;
|
|
lpd= )
|
|
lpd="${lpd} ${item}"
|
|
;;
|
|
#LOPT lopt= )
|
|
#LOPT lopt="${lopt} ${item}"
|
|
#LOPT ;;
|
|
esac
|
|
|
|
case "${i}" in
|
|
${inlist}\'*\' )
|
|
inlist=
|
|
;;
|
|
${inlist}\'* )
|
|
;;
|
|
*\' | ${inlist}* )
|
|
inlist=
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
* )
|
|
errmsg WARNING ${E_IP_OPTS} \
|
|
"unrecognized \"-o ${i}\" option" \
|
|
"check the option, resubmit if necessary
|
|
printing continues"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
#####
|
|
#
|
|
# Additional ``parameters'' are passed via Shell environment
|
|
# variables:
|
|
#
|
|
# TERM The printer type (used for Terminfo access)
|
|
# CHARSET The character set to choose
|
|
# FILTER The filter to run
|
|
#####
|
|
|
|
#####
|
|
# Set defaults for unset variables.
|
|
#####
|
|
|
|
: ${TERM:=unknown}
|
|
tput lines 1>/dev/null 2>&1 || TPUT=:
|
|
|
|
: ${CHARSET:=cs0}
|
|
|
|
if [ -z "${FILTER}" ]
|
|
then
|
|
#####
|
|
#
|
|
# If no filter is being used, we have a little routine that
|
|
# will push the data to the printer. It traps hangups (loss
|
|
# of carrier) and checks for excessive delays in sending the
|
|
# data to the printer. The lesser of the print rate of the printer
|
|
# (obtained from Terminfo) or the baud rate is used to compute
|
|
# the expected delay. If neither of these is correct, you
|
|
# may be experiencing false alarms. If so, give the correct
|
|
# rate, in characters per second, as a single argument.
|
|
# An argument of 0 means don't check for delays.
|
|
# Give an -r option to get a printout of actual delays.
|
|
# (QUOTES ARE IMPORTANT!)
|
|
#####
|
|
case "$TERM" in
|
|
PS )
|
|
# make the "postscript" printers use postio to
|
|
# talk to the printer and periodically get a
|
|
# status from them
|
|
FILTER="/usr/lib/lp/postscript/postio"
|
|
;;
|
|
PSR )
|
|
# make the "reverse postscript" printers reverse the
|
|
# output and the use postio to talk to the printer
|
|
FILTER="/usr/lib/lp/postscript/postreverse | \
|
|
/usr/lib/lp/postscript/postio"
|
|
;;
|
|
* )
|
|
# we don't know the type, so just assume that the
|
|
# input and output are the same
|
|
FILTER="${LPCAT} 0" # infinite delays
|
|
# FILTER="${LPCAT} 120" # e.g. 120 CPS
|
|
# FILTER="${LPCAT} -r 0 2>/tmp/delays" # check delays
|
|
# FILTER=${LPCAT}
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
###########
|
|
##
|
|
## Initialize the printer port
|
|
###########
|
|
|
|
#####
|
|
#
|
|
# SERIAL PORTS:
|
|
# Initialize everything.
|
|
#
|
|
# PARALLEL PORTS:
|
|
# Don't initialize baud rate.
|
|
#
|
|
# It's not obvious how to tell if a port is parallel or serial.
|
|
# However, by splitting the initialization into two steps and letting
|
|
# the serial-only part fail nicely, it'll work.
|
|
#
|
|
# Another point: The output must be a ``tty'' device. If not, don't
|
|
# bother with any of this.
|
|
#####
|
|
stty1= stty2=
|
|
tty 0<&1 1>/dev/null 2>&1 && {
|
|
|
|
#####
|
|
#
|
|
# First set the default parameters,
|
|
# then the requested parameters.
|
|
#####
|
|
|
|
stty \
|
|
9600 \
|
|
0<&1 2>/dev/null 1>&2
|
|
stty \
|
|
cs8 -cstopb -parenb -parodd \
|
|
ixon -ixany \
|
|
opost -olcuc onlcr -ocrnl -onocr -onlret -ofill \
|
|
nl0 cr0 tab0 bs0 vt0 ff0 \
|
|
0<&1 2>/dev/null 1>&2
|
|
|
|
if [ -n "${stty}" ]
|
|
then
|
|
if stty ${stty} 0<&1 1>/dev/null 2>&5
|
|
then
|
|
:
|
|
else
|
|
errmsg ERROR ${E_IP_STTY} \
|
|
"stty option list failed" \
|
|
"check the \"-o stty\" option you used,
|
|
or consult your system administrator"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
#####
|
|
#
|
|
# Here you may want to add other port initialization code.
|
|
# Some examples:
|
|
#
|
|
# estty # for printer needing hardware flow control (3B2/EPORTS)
|
|
# fctty # for printer needing hardware flow control (3B15,3B20)
|
|
#####
|
|
#estty 0<&1
|
|
#fctty 0<&1
|
|
|
|
|
|
##########
|
|
#
|
|
# Find out if we have to turn off opost before initializing the
|
|
# printer and on after. Likewise, check clocal.
|
|
#
|
|
# Turning OFF opost (output postprocessing) keeps the UNIX system
|
|
# from changing what we try to send to the printer. Turning ON
|
|
# clocal keeps the UNIX system from dropping what we are trying to
|
|
# send if the printer drops DTR. An example of the former is the
|
|
# AT&T 479, which wants to send a linefeed (ASCII 10) when a page
|
|
# width of 10 is set; with opost on, this COULD BE turned into a
|
|
# carriage-return/linefeed pair. An example of the latter is the
|
|
# AT&T 455, which momentarily drops DTR when it gets the
|
|
# initialization string, is2; with clocal off, the UNIX system
|
|
# stops sending the rest of the initialization sequence at that
|
|
# point.
|
|
#
|
|
# THIS CODE MUST FOLLOW THE REST OF THE PORT INITIALIZATION CODE.
|
|
##########
|
|
cur_stty=`stty -a 0<&3`
|
|
expr "${cur_stty}" : '.*-opost' 1>/dev/null 2>&1 \
|
|
|| stty1="${stty1} -opost" stty2="${stty2} opost"
|
|
expr "${cur_stty}" : '.*-clocal' 1>/dev/null 2>&1 \
|
|
&& stty1="${stty1} clocal" stty2="${stty2} -clocal"
|
|
expr "${cur_stty}" : '.* opost.*' 1>/dev/null 2>&1 \
|
|
|| banner_filter=${FIX386BD}
|
|
|
|
}
|
|
|
|
|
|
###########
|
|
##
|
|
## Initialize the physical printer (Part I).
|
|
## Here we bring the printer to a sane state and set the page size.
|
|
###########
|
|
|
|
##########
|
|
#
|
|
# WARNING! The "echo" command will catch backslashes (\) and
|
|
# try to interpret the characters following it. Thus, using
|
|
# "echo" to print string values obtained from "tput" is dangerous.
|
|
##########
|
|
|
|
#####
|
|
# We're confident that most printers don't have backslashes
|
|
# in the control sequences for carriage return and form-feed.
|
|
# We're also confident that these don't contain newlines.
|
|
# We're also confident that most printers have a linefeed
|
|
# in the control sequence for doing a newline (move to beginning
|
|
# of next line), but we can't capture it like we do the
|
|
# carriage return or form-feed. Thus we set it unconditionally.
|
|
# We don't set form-feed if it isn't defined, however, because
|
|
# maybe the printer doesn't have a formfeed. If not set, we're
|
|
# out of luck.
|
|
#####
|
|
|
|
CR=`${TPUT} cr`
|
|
[ -z "${CR}" ] && CR="\r"
|
|
|
|
FF=`${TPUT} ff`
|
|
|
|
NL="${CR}\n"
|
|
|
|
lines=`${TPUT} lines`
|
|
[ -z "${lines}" -o 0 -ge "${lines}" ] && lines=66
|
|
|
|
cols=`${TPUT} cols`
|
|
[ -z "${cols}" -o 0 -ge "${cols}" ] && cols=132
|
|
|
|
#####
|
|
#
|
|
# Basic initialization. The ``else'' clause is equivalent,
|
|
# but covers cases where old Terminal Information Utilities are present.
|
|
#####
|
|
[ -n "${stty1}" ] && stty ${stty1} 0<&1
|
|
if ${TPUT} init 2>/dev/null
|
|
then
|
|
:
|
|
else
|
|
pgm=`${TPUT} iprog`
|
|
if [ -x "${pgm}" ]
|
|
then
|
|
eval ${pgm}
|
|
fi
|
|
|
|
${TPUT} is1
|
|
${TPUT} is2
|
|
|
|
tabset=
|
|
if [ "8" != "`${TPUT} it`" ]
|
|
then
|
|
stty tab3 0<&1 1>/dev/null 2>&1
|
|
|
|
elif `${TPUT} ht >/dev/null`
|
|
then
|
|
tabset="/usr/lib/tabset/${TERM}"
|
|
if [ -r ${tabset} ]
|
|
then
|
|
cat -s ${tabset}
|
|
fi
|
|
stty tab3 0<&1 1>/dev/null 2>&1
|
|
fi
|
|
|
|
file=`${TPUT} if`
|
|
if [ "${tabset}" != "${file}" -a -r "${file}" ]
|
|
then
|
|
cat -s "${file}"
|
|
fi
|
|
|
|
${TPUT} is3
|
|
echo "${CR}\c"
|
|
fi
|
|
[ -n "${stty2}" ] && stty ${stty2} 0<&1
|
|
|
|
#####
|
|
#
|
|
# Set the page size and print spacing, but not the character set.
|
|
# We will be doing the character set later (after the header).
|
|
#####
|
|
internal_lpset "${cpi}" "${lpi}" "${width}" "${length}" ""
|
|
|
|
#####
|
|
#
|
|
# The banner page (and cancellation page) will
|
|
# use double width characters if they're available.
|
|
#####
|
|
WIDE_CS=`${TPUT} swidm 2>/dev/null` && NORM_CS=`${TPUT} rwidm 2>/dev/null`
|
|
PAD="#####${NL}"
|
|
|
|
#####
|
|
#
|
|
# Some printers need to have the banner page filtered.
|
|
#####
|
|
case "${TERM}" in
|
|
|
|
PS | PSR )
|
|
banner_filter="/usr/lib/lp/postscript/postprint | /usr/lib/lp/postscript/postio"
|
|
LPTELL_OPTS="-l"
|
|
;;
|
|
|
|
esac
|
|
if [ -n "${banner_filter}" ]
|
|
then
|
|
banner_filter="| ${banner_filter}"
|
|
fi
|
|
|
|
#####
|
|
#
|
|
# Now that the printer is ready for printing, we're able
|
|
# to record on paper a cancellation.
|
|
#####
|
|
|
|
cancel_banner () {
|
|
echo "${PAD}${PAD}\c"
|
|
echo "#####${WIDE_CS} Job ${request_id}${NORM_CS}${NL}\c"
|
|
echo "#####${WIDE_CS} suspended or canceled${NORM_CS}${NL}\c"
|
|
echo "${PAD}${PAD}\c"
|
|
}
|
|
|
|
canceled () {
|
|
${TPUT} scs 0 2>/dev/null
|
|
echo "${CR}\c"
|
|
if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ]
|
|
then
|
|
WIDE_CS= NORM_CS=
|
|
fi
|
|
cancel_banner
|
|
if [ -n "${FF}" ]
|
|
then
|
|
echo "${CR}${FF}\c"
|
|
fi
|
|
}
|
|
|
|
trap 'eval canceled ${banner_filter}; exit_code=0 exit' 15
|
|
|
|
|
|
###########
|
|
##
|
|
## Print the banner page
|
|
###########
|
|
|
|
#####
|
|
#
|
|
# You may want to change the following code to get a custom banner.
|
|
#####
|
|
|
|
regular_banner () {
|
|
echo "${CR}\c"
|
|
echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c"
|
|
echo "#####${WIDE_CS} User: ${user_name}${NORM_CS}${NL}\c"
|
|
if [ -n "$ALIAS_USERNAME" ]
|
|
then
|
|
echo "${PAD}\c"
|
|
echo "#####${WIDE_CS} Alias: ${ALIAS_USERNAME}${NORM_CS}${NL}\c"
|
|
fi
|
|
if [ -n "${title}" ]
|
|
then
|
|
echo "${PAD}\c"
|
|
echo "#####${WIDE_CS} Title: ${title}${NORM_CS}${NL}\c"
|
|
fi
|
|
echo "${PAD}\c"
|
|
echo "#####${WIDE_CS} Printed: `date '+%a %H:%M %h %d, 19%y'`${NORM_CS}${NL}\c"
|
|
echo "${PAD}\c"
|
|
echo "#####${WIDE_CS} Job number: ${request_id}${NORM_CS}${NL}\c"
|
|
echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c"
|
|
if [ -n "${FF}" ]
|
|
then
|
|
echo "${CR}${FF}\c"
|
|
fi
|
|
}
|
|
|
|
small_banner () {
|
|
echo "${CR}\c"
|
|
echo "${PAD}\c"
|
|
echo "##### User: ${user_name}${NL}\c"
|
|
if [ -n "${title}" ]
|
|
then
|
|
echo "##### Title: ${title}${NL}\c"
|
|
fi
|
|
echo "##### Date: `date '+%a %H:%M %h %d, 19%y'`${NL}\c"
|
|
echo "##### Job: ${request_id}${NL}\c"
|
|
echo "${PAD}\c"
|
|
if [ -n "${FF}" ]
|
|
then
|
|
echo "${CR}${FF}\c"
|
|
fi
|
|
}
|
|
|
|
if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ]
|
|
then
|
|
banner=small_banner
|
|
else
|
|
banner=regular_banner
|
|
fi
|
|
|
|
if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" ]
|
|
then
|
|
( eval "${banner} ${banner_filter}" 2>&1 1>&3 ) \
|
|
| ${LPTELL} ${LPTELL_OPTS} ${printer}
|
|
fi
|
|
|
|
|
|
###########
|
|
##
|
|
## Initialize the physical printer (Part II)
|
|
## Here we select the character set.
|
|
## One could argue that this should be done before the banner is printed,
|
|
## but we don't, to keep the banner page looking consistent for the
|
|
## operator. You can move this code before the banner code if you
|
|
## disagree. If you do, combine it with the other call to "internal_lpset"
|
|
## to do everything in one shot.
|
|
###########
|
|
internal_lpset "" "" "" "" "${CHARSET}"
|
|
|
|
###########
|
|
##
|
|
## Print some copies of the file(s)
|
|
###########
|
|
|
|
#####
|
|
#
|
|
# The protocol between the interface program and the Spooler
|
|
# is fairly simple:
|
|
#
|
|
# All standard error output is assumed to indicate a
|
|
# fault WITH THE REQUEST. The output is mailed to the
|
|
# user who submitted the print request and the print
|
|
# request is finished.
|
|
#
|
|
# If the interface program sets a zero exit code,
|
|
# it is assumed that the file printed correctly.
|
|
# If the interface program sets a non-zero exit code
|
|
# less than 128, it is assumed that the file did not
|
|
# print correctly, and the user will be notified.
|
|
# In either case the print request is finished.
|
|
#
|
|
# If the interface program sets an exit code greater
|
|
# than 128, it is assumed that the file did not print
|
|
# because of a printer fault. If an alert isn't already
|
|
# active (see below) one will be activated. (Exit code
|
|
# 128 should not be used at all. The shell, which executes
|
|
# this program, turns SIGTERM, used to kill this program
|
|
# for a cancellation or disabling, into exit 128. The
|
|
# Spooler thus interpretes 128 as SIGTERM.)
|
|
#
|
|
# A message sent to the standard input of the ${LPTELL}
|
|
# program is assumed to describe a fault WITH THE PRINTER.
|
|
# The output is used in an alert (if alerts are defined).
|
|
# If the fault recovery is "wait" or "begin", the printer
|
|
# is disabled (killing the interface program if need be),
|
|
# and the print request is left on the queue.
|
|
# If the fault recovery is "continue", the interface program
|
|
# is allowed to wait for the printer fault to be cleared so
|
|
# it can resume printing.
|
|
#
|
|
# This interface program relies on filters to detect printer faults.
|
|
# In absence of a filter provided by the customer, it uses a simple
|
|
# filter (${LPCAT}) to detect the class of faults that cause DCD
|
|
# (``carrier'') drop. The protocol between the interface program and
|
|
# the filter:
|
|
#
|
|
# The filter should exit with zero if printing was
|
|
# successful and non-zero if printing failed because
|
|
# of a printer fault. This interface program turns a
|
|
# non-zero exit of the filter into an "exit 129" from
|
|
# itself, thus telling the Spooler that a printer fault
|
|
# (still) exists.
|
|
#
|
|
# The filter should report printer faults via a message
|
|
# to its standard error. This interface program takes all
|
|
# standard error output from the filter and feeds it as
|
|
# standard input to the ${LPTELL} program.
|
|
#
|
|
# The filter should wait for a printer fault to clear,
|
|
# and should resume printing when the fault clears.
|
|
# Preferably it should resume at the top of the page
|
|
# that was being printed when the fault occurred.
|
|
# If it waits and finishes printing, it should exit
|
|
# with a 0 exit code. If it can't wait, it should exit
|
|
# with a non-zero exit code.
|
|
#
|
|
# The interface program expects that ANY message on the
|
|
# standard error from the filter indicates a printer fault.
|
|
# Therefore, a filter should not put user (input) error
|
|
# messages on the standard error, but on the standard output
|
|
# (where the user can read them when he or she examines
|
|
# the print-out).
|
|
#
|
|
#####
|
|
|
|
badfileyet=
|
|
i=1
|
|
while [ $i -le $copies ]
|
|
do
|
|
for file in ${files}
|
|
do
|
|
if [ -r "${file}" ]
|
|
then
|
|
#####
|
|
#
|
|
# Here's where we set up the $LPTELL program to
|
|
# capture fault messages, and...
|
|
#
|
|
# Here's where we print the file.
|
|
#
|
|
# We set up a pipeline to $LPTELL, but play a trick
|
|
# to get the filter's standard ERROR piped instead of
|
|
# its standard OUTPUT: Divert the standard error (#2) to
|
|
# the standard output (#1) IN THE PIPELINE. The shell
|
|
# will have changed #1 to be the pipe, not the
|
|
# printer, so diverting #2 connects it to the pipe.
|
|
# We then change the filter's #1 to a copy of the real
|
|
# standard output (the printer port) made earlier,
|
|
# so that is connected back to the printer again.
|
|
#
|
|
# We do all this inside a parenthesized expression
|
|
# so that we can get the exit code; this is necessary
|
|
# because the exit code of a pipeline is the exit
|
|
# code of the right-most command, which isn't the
|
|
# filter.
|
|
#
|
|
# These two tricks could be avoided by using a named
|
|
# pipe to connect the standard error to $LPTELL. In
|
|
# fact an early prototype of this script did just
|
|
# that; however, the named pipe introduced a timing
|
|
# problem. The processes that open a named pipe hang
|
|
# until both ends of the pipe are opened. Cancelling
|
|
# a request or disabling the printer often killed one
|
|
# of the processes, causing the other process to hang
|
|
# forever waiting for the other end of the pipe to
|
|
# be opened.
|
|
#####
|
|
EXIT_CODE=${TMPPREFIX}e
|
|
trap '' 1 # Let the filter handle a hangup
|
|
trap '' 2 3 # and interrupts
|
|
(
|
|
#####
|
|
# Put the 0<${file} before the "eval" to keep
|
|
# clever users from giving a file name that
|
|
# evaluates as something to execute.
|
|
#####
|
|
0<${file} eval ${FILTER} 2>&1 1>&3
|
|
echo $? >${EXIT_CODE}
|
|
) | ${LPTELL} ${LPTELL_OPTS} ${printer}
|
|
trap 'catch_hangup; exit_code=129 exit 129' 1
|
|
trap 'catch_interrupt; exit_code=129 exit 129' 2 3
|
|
exit_code=`cat ${EXIT_CODE}`
|
|
|
|
if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ]
|
|
then
|
|
trap '' 15 # Avoid dying from disable
|
|
sleep 4 # Give $LPTELL a chance to tell
|
|
exit ${exit_code}
|
|
fi
|
|
|
|
if [ -n "${FF}" -a "no" = "${nofilebreak}" ]
|
|
then
|
|
echo "${CR}${FF}\c"
|
|
fi
|
|
|
|
else
|
|
|
|
#####
|
|
#
|
|
# Don't complain about not being able to read
|
|
# a file on second and subsequent copies, unless
|
|
# we've not complained yet. This removes repeated
|
|
# messages about the same file yet reduces the
|
|
# chance that the user can remove a file and not
|
|
# know that we had trouble finding it.
|
|
#####
|
|
if [ "${i}" -le 1 -o -z "${badfileyet}" ]
|
|
then
|
|
errmsg WARNING ${E_IP_BADFILE} \
|
|
"cannot read file \"${file}\"" \
|
|
"see if the file still exists and is readable,
|
|
or consult your system administrator;
|
|
printing continues"
|
|
badfileyet=yes
|
|
fi
|
|
|
|
fi
|
|
|
|
done
|
|
i=`expr $i + 1`
|
|
|
|
done
|
|
|
|
if [ "no" = "${nobanner}" -a "${TERM}" = "PSR" ]
|
|
then
|
|
( eval "${banner} ${banner_filter}" 2>&1 1>&3 ) \
|
|
| ${LPTELL} ${LPTELL_OPTS} ${printer}
|
|
fi
|
|
|
|
if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ]
|
|
then
|
|
exit ${exit_code}
|
|
fi
|
|
|
|
#####
|
|
#
|
|
# Always ensure the complete job ends with a ``formfeed'', to
|
|
# let the next job start on a new page. (If someone wants to
|
|
# concatenate files, they can give them in one job.)
|
|
# So, if we haven't been putting out a ``formfeed'' between files,
|
|
# it means we haven't followed the last file with a formfeed,
|
|
# so we do it here.
|
|
#####
|
|
if [ -n "${FF}" -a "yes" = "${nofilebreak}" ]
|
|
then
|
|
echo "${CR}${FF}\c"
|
|
fi
|
|
|
|
${DRAIN}
|
|
|
|
exit_code=0 exit 0
|