view usr/src/cmd/lp/model/tsol_standard_foomatic @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#ident	"@(#)tsol_standard_foomatic	1.1	07/05/24 SMI"
#
# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

###########
##
## 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

#####
# ${LPTSOLSEPARATOR} is the name of a program to put banner and trailer
# pages around the job.
#####
if [ -x ${LOCALPATH}/lp.tsol_separator ]
then
	LPTSOLSEPARATOR=${LOCALPATH}/lp.tsol_separator
else
	echo "${LOCALPATH}/lp.tsol_separator not found." >&2
	exit 1
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"
nolabels="no"
stty=

inlist=
for i in ${option_list}
do
	case "${inlist}${i}" in


	nobanner )
		nobanner="yes"
		;;

	nofilebreak )
		nofilebreak="yes"
		;;

	nolabels )
		nolabels="yes"
		;;

	#
	# The IPP/PAPI attributes are handled by the foomatic-rip filter so
	# all we need to do here is ignore them so that they don't invoke the
	# "unrecognized option" message.
	#

	finishing=* | page-ranges=* | sides=* )
		;;
	number-up=* | orientation-requested=* | media=* )
		;;
	printer-resolution=* | print-quality=* )
		;;

	#####
	#
	# 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}

PPDFILTER=/usr/lib/lp/bin/foomatic-rip
PPDFILTERA="${PPDFILTER} ${request_id} ${user_name} \"${title}\" ${copies} \"${option_list}\""

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
			if [ `basename "${LPCAT}"` = "lp.cat" ] ; then
				FILTER="${LPCAT} 0"	# infinite delays
				# FILTER="${LPCAT} 120"	# e.g. 120 CPS
				# FILTER="${LPCAT} -r 0 2>/tmp/delays"
				# FILTER=${LPCAT}
			fi
		;;
	esac
fi

logger -p lpr.debug -t "tsol_standard_foomatic: ${request_id}" "filter : ${FILTER}"
logger -p lpr.debug -t "tsol_standard_foomatic: ${request_id}" "ppdfilter : ${PPDFILTERA}"

#
# Append the PPD foomatic-rip filter
#
FILTER="${FILTER} | ${PPDFILTERA}"

###########
##
## 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`
BFF=$FF
[ -z "${BFF}" ] && BFF="\f"

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

#
# "tput init" will return an "^M" in many cases to "stdout", i.e., printer!
# This creates problems for some PS printers
#
if [ "${TERM}" = "PS" -o "${TERM}" = "PSR" ]
then
	:
elif ${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 "${BFF}" ]
	then
		echo "${CR}${BFF}\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: `LANG=C date '+%a %H:%M %h %d, %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 "${BFF}" ]
	then
		echo "${CR}${BFF}\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: `LANG=C date '+%a %H:%M %h %d, %Y'`${NL}\c"
	echo "#####   Job: ${request_id}${NL}\c"
	echo "${PAD}\c"
	if [ -n "${BFF}" ]
	then
		echo "${CR}${BFF}\c"
	fi
}

if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ]
then
	banner=small_banner
else
	banner=regular_banner
fi

## Skip this for PS/PSR in TSOL, since lp.tsol_separator handles the banners
if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
then
	( eval "${banner} ${banner_filter}" 2>&1 1>&3 ) \
		| ${LPTELL} ${LPTELL_OPTS} ${printer}
fi

###########
##
## Surround the job by PostScript code to produce banner 
## and trailerpages and page headers and footers.
##
###########

BANNER_EXIT_CODE=${TMPPREFIX}.banner.exit_code
echo 0 > ${BANNER_EXIT_CODE}
TSOLSEPARATOR_LOG=${TMPPREFIX}.banner.errmsg

tsol_bannerize () {
	TSOLSEPARATOR_OPTS="-e ${TSOLSEPARATOR_LOG}"

	if [ "yes" = "${nolabels}" ]
	then
		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -l"
	fi

	if [ "yes" = "${nobanner}" ]
	then
		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -t /dev/null -b /dev/null"
	fi

	if [ "${TERM}" = "PSR" ]
	then
		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -r"
	fi

	# Get rid of the #, TAB and NL characters in the title
	tsol_title=`echo $title`
	tsol_title=`echo $tsol_title | sed 's/#//g'`

	LC_TIME=C ${LPTSOLSEPARATOR} ${TSOLSEPARATOR_OPTS} "${printer}" \
	    "${request_id}" "${user_name}" "${tsol_title}" "${file}"
	echo $? > ${BANNER_EXIT_CODE}
	true
}

bannerize=tsol_bannerize

if [ "yes" = "${nobanner}" -a  "yes" = "${nolabels}" ]
then
	bannerize=cat
fi

if [ "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
then
	bannerize=cat
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} $bannerize | eval ${FILTER} 2>&1 1>&3
				echo $? >${EXIT_CODE}
			) | ${LPTELL} ${LPTELL_OPTS} ${printer}

			# if lp.tsol_separator had an error, send its logged
			# error message to LPTELL.
			banner_exit_code=`cat ${BANNER_EXIT_CODE}`
			if [ -n "${banner_exit_code}" -a \
				0 -ne "${banner_exit_code}" -a \
				 -n "${LPTELL}" -a \
				-r "${TSOLSEPARATOR_LOG}" ]
			then
				cat ${TSOLSEPARATOR_LOG} | ${LPTELL} ${printer}
				echo 77 > ${EXIT_CODE}
			fi

			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

# Skip this for TSOL, since lp.tsol_separator handles the banners
#
# 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