xref: /illumos-gate/usr/src/cmd/lp/model/tsol_standard_foomatic (revision 9b9d39d2a32ff806d2431dbcc50968ef1e6d46b2)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21#
22#ident	"%Z%%M%	%I%	%E% SMI"
23#
24# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27
28###########
29##
30## Standard printer interface program.
31##
32###########
33
34#####
35#
36# Until we get to the point below where the printer port
37# and physical printer are initialized, we can't do much
38# except exit if the Spooler/Scheduler cancels us.
39#####
40trap 'exit' 15
41
42#####
43#
44# We can be clever about getting a hangup or interrupt, though, at least
45# until the filter runs. Do this early, even though $LPTELL
46# isn't defined, so that we're covered.
47#####
48catch_hangup () {
49	if [ -n "${LPTELL}" ]
50	then
51		echo \
52"The connection to the printer dropped; perhaps the printer went off-line?" \
53		| ${LPTELL} ${printer}
54	fi
55	return 0
56}
57catch_interrupt () {
58	if [ -n "${LPTELL}" ]
59	then
60		echo \
61"Received an interrupt from the printer.  The reason is unknown,
62although a common cause is that the baud rate is too high." \
63		| ${LPTELL} ${printer}
64	fi
65	return 0
66}
67trap 'catch_hangup; exit_code=129 exit 129' 1
68trap 'catch_interrupt; exit_code=129 exit 129' 2 3
69
70#####
71#
72# Most of the time we don't want the standard error to be captured
73# by the Spooler, mainly to avoid "Terminated" messages that the
74# shell puts out when we get a SIGTERM. We'll save the standard
75# error channel under another number, so we can use it when it
76# should be captured.
77#
78# Open another channel to the printer port, for use when the
79# regular standard output won't be directed there, such as in
80# command substitution (`cmd`).
81#####
82exec 5>&2 2>/dev/null 3>&1
83
84#####
85#
86# Set some globally used variables and functions.
87#####
88
89: ${TMPDIR:=/tmp}
90: ${SPOOLDIR:=/usr/spool/lp}
91: ${TERMINFO:=/usr/lib/terminfo}
92: ${CHARSETDIR:=/usr/lib/charsets}
93
94: ${LOCALPATH:=${SPOOLDIR}/bin}
95PATH="/bin:/usr/bin:${LOCALPATH}"
96
97MAX_COLS_SMALL_BANNER=40
98
99#####
100#
101# On the 3.2 release of the 386unix product, the parallel port does
102# not support any ioctl calls.  As a result, we cannot set the opost
103# and onlcr attributes to have <NL>'s expanded to <CR><NL>.  This
104# "filter" gets the job done for us.
105#####
106: ${FIX386BD:=${LOCALPATH}/386parallel}
107if [ -n "${FIX386BD}" -a -x "${FIX386BD}" ]
108then
109	FIX386BD="| ${FIX386BD}"
110else
111	FIX386BD=""
112fi
113
114#####
115# Use ${TMPPREFIX} as the prefix for all temporary files, so
116# that cleanup is easy. The prefix may be up to 13 characters
117# long, so you only have space for one more character to make
118# a file name. If necessary, make a directory using this prefix
119# for better management of unique temporary file names.
120#####
121TMPPREFIX=${TMPDIR}/`uname -n`$$
122
123#####
124# Before exiting, set ${exit_code} to the value with which to exit.
125# Otherwise, the exit from this script will be 0.
126#####
127trap 'rm -fr ${TMPPREFIX}*; exit ${exit_code}' 0
128
129#####
130# ${LPTELL} is the name of a program that will send its
131# standard input to the Spooler. It is used to forward
132# the description of a printer fault to the Spooler,
133# which uses it in an alert to the administrator.
134#####
135if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ]
136then
137	fake_lptell () {
138		header="no"
139		while read line
140		do
141			if [ "no" = "${header}" ]
142			then
143				errmsg ERROR ${E_IP_UNKNOWN} \
144		"unknown printer/interface failure" \
145		"consult your system administrator;
146		reasons for failure (if any) follow:"
147				header=yes
148			fi
149			echo "${line}" >&2
150		done
151		return 1
152	}
153	LPTELL=fake_lptell
154fi
155
156#####
157# ${DRAIN} is the name of a program that will wait
158# long enough for data sent to the printer to print.
159#####
160if [ -x "${LOCALPATH}/drain.output" ]
161then
162	DRAIN="${LOCALPATH}/drain.output 5"	# wait only five seconds
163else
164	DRAIN=
165fi
166
167#####
168# ${LPTSOLSEPARATOR} is the name of a program to put banner and trailer
169# pages around the job.
170#####
171if [ -x ${LOCALPATH}/lp.tsol_separator ]
172then
173	LPTSOLSEPARATOR=${LOCALPATH}/lp.tsol_separator
174else
175	echo "${LOCALPATH}/lp.tsol_separator not found." >&2
176	exit 1
177fi
178
179#####
180# ${LPCAT} is the name of a program to use as a default
181# filter. Minimally it should copy its standard input to
182# the standard output, but it should also trap printer
183# faults. The current LPCAT traps hangups (DCD dropping, SIGHUP),
184# interrupts (SIGINT, SIGQUIT), broken pipe (SIGPIPE), and
185# excess delays in sending data to the printer, interpreting all
186# as printer faults.
187#####
188if [ ! -x "${LPCAT:=${LOCALPATH}/lp.cat}" ]
189then
190	LPCAT="cat"
191fi
192
193#####
194# ${LPSET} is the name of a program that will set the
195# character pitch, line pitch, page width, page length,
196# and character set. It helps to have this in a single
197# binary program so that (1) it's faster than calls
198# to "tput"; and (2) it can access the new Terminfo
199# capabilities for printers (on pre SVR3.2 machines, tput can't).
200#####
201if [ ! -x "${LPSET:=${LOCALPATH}/lp.set}" ]
202then
203	fake_lpset () {
204		echo H V W L S >&2
205		false
206	}
207	LPSET=fake_lpset
208fi
209
210internal_lpset () {
211	#####
212	#
213	# The funny business with the "2>&1 1>&3" is to let us capture
214	# the standard ERROR, not the standard OUTPUT as is the usual case
215	# with foo=`cmd`. The standard output will go to the printer.
216	#####
217	[ -n "${stty1}" ] && stty ${stty1} 0<&1
218	chk=`${LPSET} "$1" "$2" "$3" "$4" "$5" 2>&1 1>&3`
219	[ -n "${stty2}" ] && stty ${stty2} 0<&1
220
221	#####
222	#
223	# The standard error of the delivered ${LPSET} program
224	# is a string of letters, H, V, W, L, S, which correspond
225	# to cpi, lpi, width, length, and character set. A letter
226	# is present only if the corresponding attribute could not
227	# be set.
228	#####
229	for err in ${chk}
230	do
231		case ${err} in
232		H )
233			errmsg WARNING ${E_IP_BADCPI} \
234		"can't select the character pitch \"${cpi}\"" \
235		"check the valid pitches for the printer,
236		or consult your system administrator;
237		printing continues"
238			;;
239		V )
240			errmsg WARNING ${E_IP_BADLPI} \
241		"can't select the line pitch \"${lpi}\"" \
242		"check the valid pitches for the printer,
243		or consult your system administrator;
244		printing continues"
245			;;
246		W )
247			width=${cols}
248			errmsg WARNING ${E_IP_BADWIDTH} \
249		"can't select the page width \"${width}\"" \
250		"check the valid widths for the printer,
251		or consult your system administrator;
252		printing continues"
253			;;
254		L )
255			length=${lines}
256			errmsg WARNING ${E_IP_BADLENGTH} \
257		"can't select the page length \"${length}\"" \
258		"check the valid lengths for the printer,
259		or consult your system administrator;
260		printing continues"
261			;;
262		S )
263			errmsg WARNING ${E_IP_BADCHARSET} \
264		"can't select the character set \"${CHARSET}\"" \
265		"check the name given in the -S option,
266		or consult your system administrator;
267		printing continues"
268			;;
269		esac
270	done
271}
272
273
274#####
275# ${TPUT} is "tput" IF it works. We'll disable it if we get an
276# ugly error message the first time we use it. See the TERM variable
277# later in the script.
278#
279# NOTE: The check we use to see if "tput" works is to use an OLD
280# Terminfo capability, like "lines". If it works with that it may
281# still fail with some of the newer capabilities like "init" (SVR3.0)
282# or "swidm" (SVR3.2), because the version of "tput" we have on your
283# machine is older. Thus, on some of the code where ${TPUT} is used
284# you'll see "2>/dev/null" being used to avoid ugly error messages.
285#####
286TPUT=tput
287
288#####
289# Error message formatter:
290#
291# Invoke as
292#
293#	errmsg severity message-number problem help
294#
295# where severity is "ERROR" or "WARNING", message-number is
296# a unique identifier, problem is a short description of the
297# problem, and help is a short suggestion for fixing the problem.
298#####
299
300LP_ERR_LABEL="UX:lp"
301
302E_IP_ARGS=1
303E_IP_OPTS=2
304#E_IP_FILTER=3
305E_IP_STTY=4
306E_IP_UNKNOWN=5
307E_IP_BADFILE=6
308E_IP_BADCHARSET=7
309E_IP_BADCPI=8
310E_IP_BADLPI=9
311E_IP_BADWIDTH=10
312E_IP_BADLENGTH=11
313E_IP_ERRORS=12		# (in slow.filter)
314
315errmsg () {
316	case $1 in
317	ERROR )
318		sev="  ERROR";
319		;;
320	WARNING )
321		sev="WARNING";
322		;;
323	esac
324#	tag=`expr "${LP_ERR_LABEL}" : "\(.*\):"``expr "${LP_ERR_LABEL}" : ".*:\(.*\)"`
325	echo "${LP_ERR_LABEL}: ${sev}: $3
326        TO FIX: $4" >&5
327}
328
329
330###########
331##
332## Check arguments
333###########
334
335parse () {
336	echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
337}
338
339#####
340#
341# This program is invoked as
342#
343# ${SPOOLDIR}/.../printer request-id user title copies options files...
344#
345# The first three arguments are simply reprinted on the banner page,
346# the fourth (copies) is used to control the number of copies to print,
347# the fifth (options) is a blank separated list (in a single argument)
348# of user or Spooler supplied options (without the -o prefix),
349# and the last arguments are the files to print.
350#####
351
352if [ $# -lt 5 ]
353then
354	errmsg ERROR ${E_IP_ARGS} \
355		"wrong number of arguments to interface program" \
356		"consult your system administrator"
357	exit 1
358fi
359
360printer=`basename $0`
361request_id=$1
362user_name=$2
363title=$3
364copies=$4
365option_list=$5
366
367shift 5
368files="$*"
369
370nobanner="no"
371nofilebreak="no"
372nolabels="no"
373stty=
374
375inlist=
376for i in ${option_list}
377do
378	case "${inlist}${i}" in
379
380
381	nobanner )
382		nobanner="yes"
383		;;
384
385	nofilebreak )
386		nofilebreak="yes"
387		;;
388
389	nolabels )
390		nolabels="yes"
391		;;
392
393	#
394	# The IPP/PAPI attributes are handled by the foomatic-rip filter so
395	# all we need to do here is ignore them so that they don't invoke the
396	# "unrecognized option" message.
397	#
398
399	finishing=* | page-ranges=* | sides=* )
400		;;
401	number-up=* | orientation-requested=* | media=* )
402		;;
403	printer-resolution=* | print-quality=* )
404		;;
405
406	#####
407	#
408	# If you want to add simple options (e.g. -o simple)
409	# identify them here.
410	#####
411#	simple )
412#		simple="yes"
413#		;;
414
415
416	cpi=pica )
417		cpi=10
418		;;
419	cpi=elite )
420		cpi=12
421		;;
422	cpi=* )
423		cpi=`parse ${i}`
424		;;
425
426	lpi=* )
427		lpi=`parse ${i}`
428		;;
429
430	length=* )
431		length=`parse ${i}`
432		;;
433
434	width=* )
435		width=`parse ${i}`
436		;;
437
438	#####
439	#
440	# If you want to add simple-value options (e.g. -o value=a)
441	# identify them here.
442	#####
443#	value=* )
444#		value=`parse ${i}`
445#		;;
446
447
448	#####
449	#
450	# If you want to add options that, like "stty",
451	# take a list (e.g. -o lopt='a b c'), identify
452	# them here and below (look for LOPT).
453	#####
454	stty=* | flist=* | lpd=* )
455#LOPT	stty=* | flist=* | lpd=* | lopt=* )
456
457		inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"`
458		case "${i}" in
459		${inlist}\'*\' )
460			item=`expr "${i}" : "^[^=]*='*\(.*\)'\$"`
461			;;
462		${inlist}\' )
463			continue
464			;;
465		${inlist}\'* )
466			item=`expr "${i}" : "^[^=]*='*\(.*\)\$"`
467			;;
468		${inlist}* )
469			item=`expr "${i}" : "^[^=]*=\(.*\)\$"`
470			;;
471		*\' )
472			item=`expr "${i}" : "^\(.*\)'\$"`
473			;;
474		* )
475			item="${i}"
476			;;
477		esac
478
479		#####
480		#
481		# We don't dare use "eval" because a clever user could
482		# put something in an option value that we'd end up
483		# exec'ing.
484		#####
485		case "${inlist}" in
486		stty= )
487			stty="${stty} ${item}"
488			;;
489		flist= )
490			flist="${flist} ${item}"
491			;;
492		lpd= )
493			lpd="${lpd} ${item}"
494			;;
495#LOPT		lopt= )
496#LOPT			lopt="${lopt} ${item}"
497#LOPT			;;
498		esac
499
500		case "${i}" in
501		${inlist}\'*\' )
502			inlist=
503			;;
504		${inlist}\'* )
505			;;
506		*\' | ${inlist}* )
507			inlist=
508			;;
509		esac
510		;;
511
512	* )
513		errmsg WARNING ${E_IP_OPTS} \
514			"unrecognized \"-o ${i}\" option" \
515			"check the option, resubmit if necessary
516		printing continues"
517		;;
518	esac
519done
520
521#####
522#
523# Additional ``parameters'' are passed via Shell environment
524# variables:
525#
526#	TERM	The printer type (used for Terminfo access)
527#	CHARSET	The character set to choose
528#	FILTER	The filter to run
529#####
530
531#####
532# Set defaults for unset variables.
533#####
534
535: ${TERM:=unknown}
536tput lines 1>/dev/null 2>&1 || TPUT=:
537
538: ${CHARSET:=cs0}
539
540PPDFILTER=/usr/lib/lp/bin/foomatic-rip
541PPDFILTERA="${PPDFILTER} ${request_id} ${user_name} \"${title}\" ${copies} \"${option_list}\""
542
543if [ -z "${FILTER}" ]
544then
545	#####
546	#
547	# If no filter is being used, we have a little routine that
548	# will push the data to the printer. It traps hangups (loss
549	# of carrier) and checks for excessive delays in sending the
550	# data to the printer. The lesser of the print rate of the printer
551	# (obtained from Terminfo) or the baud rate is used to compute
552	# the expected delay. If neither of these is correct, you
553	# may be experiencing false alarms. If so, give the correct
554	# rate, in characters per second, as a single argument.
555	# An argument of 0 means don't check for delays.
556	# Give an -r option to get a printout of actual delays.
557	# (QUOTES ARE IMPORTANT!)
558	#####
559	case "$TERM" in
560		PS )
561			# make the "postscript" printers use postio to
562			# talk to the printer and periodically get a
563			# status from them
564			FILTER="/usr/lib/lp/postscript/postio"
565		;;
566		PSR )
567			# make the "reverse postscript" printers reverse the
568			# output and the use postio to talk to the printer
569			FILTER="/usr/lib/lp/postscript/postreverse | \
570				/usr/lib/lp/postscript/postio"
571		;;
572		* )
573			# we don't know the type, so just assume that the
574			# input and output are the same
575			if [ `basename "${LPCAT}"` = "lp.cat" ] ; then
576				FILTER="${LPCAT} 0"	# infinite delays
577				# FILTER="${LPCAT} 120"	# e.g. 120 CPS
578				# FILTER="${LPCAT} -r 0 2>/tmp/delays"
579				# FILTER=${LPCAT}
580			fi
581		;;
582	esac
583fi
584
585logger -p lpr.debug -t "tsol_standard_foomatic: ${request_id}" "filter : ${FILTER}"
586logger -p lpr.debug -t "tsol_standard_foomatic: ${request_id}" "ppdfilter : ${PPDFILTERA}"
587
588#
589# Append the PPD foomatic-rip filter
590#
591FILTER="${FILTER} | ${PPDFILTERA}"
592
593###########
594##
595## Initialize the printer port
596###########
597
598#####
599#
600# SERIAL PORTS:
601# Initialize everything.
602#
603# PARALLEL PORTS:
604# Don't initialize baud rate.
605#
606# It's not obvious how to tell if a port is parallel or serial.
607# However, by splitting the initialization into two steps and letting
608# the serial-only part fail nicely, it'll work.
609#
610# Another point: The output must be a ``tty'' device. If not, don't
611# bother with any of this.
612#####
613stty1= stty2=
614tty 0<&1 1>/dev/null 2>&1 && {
615
616	#####
617	#
618	# First set the default parameters,
619	# then the requested parameters.
620	#####
621
622	stty \
623		9600 \
624			0<&1 2>/dev/null 1>&2
625	stty \
626		cs8 -cstopb -parenb -parodd \
627		ixon -ixany \
628		opost -olcuc onlcr -ocrnl -onocr -onlret -ofill \
629		nl0 cr0 tab0 bs0 vt0 ff0 \
630			0<&1 2>/dev/null 1>&2
631
632	if [ -n "${stty}" ]
633	then
634		if stty ${stty} 0<&1 1>/dev/null 2>&5
635		then
636			:
637		else
638			errmsg ERROR ${E_IP_STTY} \
639				"stty option list failed" \
640				"check the \"-o stty\" option you used,
641		or consult your system administrator"
642			exit 1
643		fi
644	fi
645
646	#####
647	#
648	# Here you may want to add other port initialization code.
649	# Some examples:
650	#
651	# estty	# for printer needing hardware flow control (3B2/EPORTS)
652	# fctty	# for printer needing hardware flow control (3B15,3B20)
653	#####
654	#estty 0<&1
655	#fctty 0<&1
656
657
658	##########
659	#
660	# Find out if we have to turn off opost before initializing the
661	# printer and on after. Likewise, check clocal.
662	#
663	# Turning OFF opost (output postprocessing) keeps the UNIX system
664	# from changing what we try to send to the printer. Turning ON
665	# clocal keeps the UNIX system from dropping what we are trying to
666	# send if the printer drops DTR. An example of the former is the
667	# AT&T 479, which wants to send a linefeed (ASCII 10) when a page
668	# width of 10 is set; with opost on, this COULD BE turned into a
669	# carriage-return/linefeed pair. An example of the latter is the
670	# AT&T 455, which momentarily drops DTR when it gets the
671	# initialization string, is2; with clocal off, the UNIX system
672	# stops sending the rest of the initialization sequence at that
673	# point.
674	#
675	# THIS CODE MUST FOLLOW THE REST OF THE PORT INITIALIZATION CODE.
676	##########
677	cur_stty=`stty -a 0<&3`
678	expr "${cur_stty}" : '.*-opost' 1>/dev/null 2>&1 \
679		|| stty1="${stty1} -opost" stty2="${stty2} opost"
680	expr "${cur_stty}" : '.*-clocal' 1>/dev/null 2>&1 \
681		&& stty1="${stty1} clocal" stty2="${stty2} -clocal"
682	expr "${cur_stty}" : '.* opost.*' 1>/dev/null 2>&1 \
683		|| banner_filter=${FIX386BD}
684
685}
686
687
688###########
689##
690## Initialize the physical printer (Part I).
691## Here we bring the printer to a sane state and set the page size.
692###########
693
694##########
695#
696# WARNING! The "echo" command will catch backslashes (\) and
697# try to interpret the characters following it. Thus, using
698# "echo" to print string values obtained from "tput" is dangerous.
699##########
700
701#####
702# We're confident that most printers don't have backslashes
703# in the control sequences for carriage return and form-feed.
704# We're also confident that these don't contain newlines.
705# We're also confident that most printers have a linefeed
706# in the control sequence for doing a newline (move to beginning
707# of next line), but we can't capture it like we do the
708# carriage return or form-feed. Thus we set it unconditionally.
709# We don't set form-feed if it isn't defined, however, because
710# maybe the printer doesn't have a formfeed. If not set, we're
711# out of luck.
712#####
713
714CR=`${TPUT} cr`
715[ -z "${CR}" ] && CR="\r"
716
717FF=`${TPUT} ff`
718BFF=$FF
719[ -z "${BFF}" ] && BFF="\f"
720
721NL="${CR}\n"
722
723lines=`${TPUT} lines`
724[ -z "${lines}" -o 0 -ge "${lines}" ] && lines=66
725
726cols=`${TPUT} cols`
727[ -z "${cols}" -o 0 -ge "${cols}" ] && cols=132
728
729#####
730#
731# Basic initialization. The ``else'' clause is equivalent,
732# but covers cases where old Terminal Information Utilities are present.
733#####
734[ -n "${stty1}" ] && stty ${stty1} 0<&1
735
736#
737# "tput init" will return an "^M" in many cases to "stdout", i.e., printer!
738# This creates problems for some PS printers
739#
740if [ "${TERM}" = "PS" -o "${TERM}" = "PSR" ]
741then
742	:
743elif ${TPUT} init 2>/dev/null
744then
745	:
746else
747	pgm=`${TPUT} iprog`
748	if [ -x "${pgm}" ]
749	then
750		eval ${pgm}
751	fi
752
753	${TPUT} is1
754	${TPUT} is2
755
756	tabset=
757	if [ "8" != "`${TPUT} it`" ]
758	then
759		stty tab3 0<&1 1>/dev/null 2>&1
760
761	elif `${TPUT} ht >/dev/null`
762	then
763		tabset="/usr/lib/tabset/${TERM}"
764		if [ -r ${tabset} ]
765		then
766			cat -s ${tabset}
767		fi
768		stty tab3 0<&1 1>/dev/null 2>&1
769	fi
770
771	file=`${TPUT} if`
772	if [ "${tabset}" != "${file}" -a -r "${file}" ]
773	then
774		cat -s "${file}"
775	fi
776
777	${TPUT} is3
778	echo "${CR}\c"
779fi
780[ -n "${stty2}" ] && stty ${stty2} 0<&1
781
782#####
783#
784# Set the page size and print spacing, but not the character set.
785# We will be doing the character set later (after the header).
786#####
787internal_lpset "${cpi}" "${lpi}" "${width}" "${length}" ""
788
789#####
790#
791# The banner page (and cancellation page) will
792# use double width characters if they're available.
793#####
794WIDE_CS=`${TPUT} swidm 2>/dev/null` && NORM_CS=`${TPUT} rwidm 2>/dev/null`
795PAD="#####${NL}"
796
797#####
798#
799# Some printers need to have the banner page filtered.
800#####
801case "${TERM}" in
802
803PS | PSR )
804	banner_filter="/usr/lib/lp/postscript/postprint | /usr/lib/lp/postscript/postio"
805	LPTELL_OPTS="-l"
806	;;
807
808esac
809if [ -n "${banner_filter}" ]
810then
811	banner_filter="| ${banner_filter}"
812fi
813
814#####
815#
816# Now that the printer is ready for printing, we're able
817# to record on paper a cancellation.
818#####
819
820cancel_banner () {
821	echo "${PAD}${PAD}\c"
822	echo "#####${WIDE_CS} Job ${request_id}${NORM_CS}${NL}\c"
823	echo "#####${WIDE_CS} suspended or canceled${NORM_CS}${NL}\c"
824	echo "${PAD}${PAD}\c"
825}
826
827canceled () {
828	${TPUT} scs 0 2>/dev/null
829	echo "${CR}\c"
830	if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ]
831	then
832		WIDE_CS= NORM_CS=
833	fi
834	cancel_banner
835	if [ -n "${BFF}" ]
836	then
837		echo "${CR}${BFF}\c"
838	fi
839}
840
841trap 'eval canceled ${banner_filter}; exit_code=0 exit' 15
842
843
844###########
845##
846## Print the banner page
847###########
848
849#####
850#
851# You may want to change the following code to get a custom banner.
852#####
853
854regular_banner () {
855	echo "${CR}\c"
856	echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c"
857	echo "#####${WIDE_CS}       User: ${user_name}${NORM_CS}${NL}\c"
858	if [ -n "$ALIAS_USERNAME" ]
859	then
860		echo "${PAD}\c"
861		echo "#####${WIDE_CS}      Alias: ${ALIAS_USERNAME}${NORM_CS}${NL}\c"
862	fi
863	if [ -n "${title}" ]
864	then
865		echo "${PAD}\c"
866		echo "#####${WIDE_CS}      Title: ${title}${NORM_CS}${NL}\c"
867	fi
868	echo "${PAD}\c"
869	echo "#####${WIDE_CS}    Printed: `LANG=C date '+%a %H:%M %h %d, %Y'`${NORM_CS}${NL}\c"
870	echo "${PAD}\c"
871	echo "#####${WIDE_CS} Job number: ${request_id}${NORM_CS}${NL}\c"
872	echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c"
873	if [ -n "${BFF}" ]
874	then
875		echo "${CR}${BFF}\c"
876	fi
877}
878
879small_banner () {
880	echo "${CR}\c"
881	echo "${PAD}\c"
882	echo "#####  User: ${user_name}${NL}\c"
883	if [ -n "${title}" ]
884	then
885		echo "##### Title: ${title}${NL}\c"
886	fi
887	echo "#####  Date: `LANG=C date '+%a %H:%M %h %d, %Y'`${NL}\c"
888	echo "#####   Job: ${request_id}${NL}\c"
889	echo "${PAD}\c"
890	if [ -n "${BFF}" ]
891	then
892		echo "${CR}${BFF}\c"
893	fi
894}
895
896if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ]
897then
898	banner=small_banner
899else
900	banner=regular_banner
901fi
902
903## Skip this for PS/PSR in TSOL, since lp.tsol_separator handles the banners
904if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
905then
906	( eval "${banner} ${banner_filter}" 2>&1 1>&3 ) \
907		| ${LPTELL} ${LPTELL_OPTS} ${printer}
908fi
909
910###########
911##
912## Surround the job by PostScript code to produce banner
913## and trailerpages and page headers and footers.
914##
915###########
916
917BANNER_EXIT_CODE=${TMPPREFIX}.banner.exit_code
918echo 0 > ${BANNER_EXIT_CODE}
919TSOLSEPARATOR_LOG=${TMPPREFIX}.banner.errmsg
920
921tsol_bannerize () {
922	TSOLSEPARATOR_OPTS="-e ${TSOLSEPARATOR_LOG}"
923
924	if [ "yes" = "${nolabels}" ]
925	then
926		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -l"
927	fi
928
929	if [ "yes" = "${nobanner}" ]
930	then
931		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -t /dev/null -b /dev/null"
932	fi
933
934	if [ "${TERM}" = "PSR" ]
935	then
936		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -r"
937	fi
938
939	# Get rid of the #, TAB and NL characters in the title
940	tsol_title=`echo $title`
941	tsol_title=`echo $tsol_title | sed 's/#//g'`
942
943	LC_TIME=C ${LPTSOLSEPARATOR} ${TSOLSEPARATOR_OPTS} "${printer}" \
944	    "${request_id}" "${user_name}" "${tsol_title}" "${file}"
945	echo $? > ${BANNER_EXIT_CODE}
946	true
947}
948
949bannerize=tsol_bannerize
950
951if [ "yes" = "${nobanner}" -a  "yes" = "${nolabels}" ]
952then
953	bannerize=cat
954fi
955
956if [ "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
957then
958	bannerize=cat
959fi
960
961
962###########
963##
964## Initialize the physical printer (Part II)
965## Here we select the character set.
966## One could argue that this should be done before the banner is printed,
967## but we don't, to keep the banner page looking consistent for the
968## operator. You can move this code before the banner code if you
969## disagree. If you do, combine it with the other call to "internal_lpset"
970## to do everything in one shot.
971###########
972internal_lpset "" "" "" "" "${CHARSET}"
973
974###########
975##
976## Print some copies of the file(s)
977###########
978
979#####
980#
981# The protocol between the interface program and the Spooler
982# is fairly simple:
983#
984#	All standard error output is assumed to indicate a
985#	fault WITH THE REQUEST. The output is mailed to the
986#	user who submitted the print request and the print
987#	request is finished.
988#
989#	If the interface program sets a zero exit code,
990#	it is assumed that the file printed correctly.
991#	If the interface program sets a non-zero exit code
992#	less than 128, it is assumed that the file did not
993#	print correctly, and the user will be notified.
994#	In either case the print request is finished.
995#
996#	If the interface program sets an exit code greater
997#	than 128, it is assumed that the file did not print
998#	because of a printer fault. If an alert isn't already
999#	active (see below) one will be activated. (Exit code
1000#	128 should not be used at all. The shell, which executes
1001#	this program, turns SIGTERM, used to kill this program
1002#	for a cancellation or disabling, into exit 128. The
1003#	Spooler thus interpretes 128 as SIGTERM.)
1004#
1005#	A message sent to the standard input of the ${LPTELL}
1006#	program is assumed to describe a fault WITH THE PRINTER.
1007#	The output is used in an alert (if alerts are defined).
1008#	If the fault recovery is "wait" or "begin", the printer
1009#	is disabled (killing the interface program if need be),
1010#	and the print request is left on the queue.
1011#	If the fault recovery is "continue", the interface program
1012#	is allowed to wait for the printer fault to be cleared so
1013#	it can resume printing.
1014#
1015# This interface program relies on filters to detect printer faults.
1016# In absence of a filter provided by the customer, it uses a simple
1017# filter (${LPCAT}) to detect the class of faults that cause DCD
1018# (``carrier'') drop. The protocol between the interface program and
1019# the filter:
1020#
1021#	The filter should exit with zero if printing was
1022#	successful and non-zero if printing failed because
1023#	of a printer fault. This interface program turns a
1024#	non-zero exit of the filter into an "exit 129" from
1025#	itself, thus telling the Spooler that a printer fault
1026#	(still) exists.
1027#
1028#	The filter should report printer faults via a message
1029#	to its standard error. This interface program takes all
1030#	standard error output from the filter and feeds it as
1031#	standard input to the ${LPTELL} program.
1032#
1033#	The filter should wait for a printer fault to clear,
1034#	and should resume printing when the fault clears.
1035#	Preferably it should resume at the top of the page
1036#	that was being printed when the fault occurred.
1037#	If it waits and finishes printing, it should exit
1038#	with a 0 exit code. If it can't wait, it should exit
1039#	with a non-zero exit code.
1040#
1041#	The interface program expects that ANY message on the
1042#	standard error from the filter indicates a printer fault.
1043#	Therefore, a filter should not put user (input) error
1044#	messages on the standard error, but on the standard output
1045#	(where the user can read them when he or she examines
1046#	the print-out).
1047#
1048#####
1049
1050badfileyet=
1051i=1
1052while [ $i -le $copies ]
1053do
1054	for file in ${files}
1055	do
1056		if [ -r "${file}" ]
1057		then
1058			#####
1059			#
1060			# Here's where we set up the $LPTELL program to
1061			# capture fault messages, and...
1062			#
1063			# Here's where we print the file.
1064			#
1065			# We set up a pipeline to $LPTELL, but play a trick
1066			# to get the filter's standard ERROR piped instead of
1067			# its standard OUTPUT: Divert the standard error (#2) to
1068			# the standard output (#1) IN THE PIPELINE. The shell
1069			# will have changed #1 to be the pipe, not the
1070			# printer, so diverting #2 connects it to the pipe.
1071			# We then change the filter's #1 to a copy of the real
1072			# standard output (the printer port) made earlier,
1073			# so that is connected back to the printer again.
1074			#
1075			# We do all this inside a parenthesized expression
1076			# so that we can get the exit code; this is necessary
1077			# because the exit code of a pipeline is the exit
1078			# code of the right-most command, which isn't the
1079			# filter.
1080			#
1081			# These two tricks could be avoided by using a named
1082			# pipe to connect the standard error to $LPTELL. In
1083			# fact an early prototype of this script did just
1084			# that; however, the named pipe introduced a timing
1085			# problem. The processes that open a named pipe hang
1086			# until both ends of the pipe are opened. Cancelling
1087			# a request or disabling the printer often killed one
1088			# of the processes, causing the other process to hang
1089			# forever waiting for the other end of the pipe to
1090			# be opened.
1091			#####
1092			EXIT_CODE=${TMPPREFIX}e
1093			trap '' 1	# Let the filter handle a hangup
1094			trap '' 2 3	# and interrupts
1095			(
1096				#####
1097				# Put the 0<${file} before the "eval" to keep
1098				# clever users from giving a file name that
1099				# evaluates as something to execute.
1100				#####
1101				0<${file} $bannerize | eval ${FILTER} 2>&1 1>&3
1102				echo $? >${EXIT_CODE}
1103			) | ${LPTELL} ${LPTELL_OPTS} ${printer}
1104
1105			# if lp.tsol_separator had an error, send its logged
1106			# error message to LPTELL.
1107			banner_exit_code=`cat ${BANNER_EXIT_CODE}`
1108			if [ -n "${banner_exit_code}" -a \
1109				0 -ne "${banner_exit_code}" -a \
1110				 -n "${LPTELL}" -a \
1111				-r "${TSOLSEPARATOR_LOG}" ]
1112			then
1113				cat ${TSOLSEPARATOR_LOG} | ${LPTELL} ${printer}
1114				echo 77 > ${EXIT_CODE}
1115			fi
1116
1117			trap 'catch_hangup; exit_code=129 exit 129' 1
1118			trap 'catch_interrupt; exit_code=129 exit 129' 2 3
1119			exit_code=`cat ${EXIT_CODE}`
1120
1121			if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ]
1122			then
1123				trap '' 15  # Avoid dying from disable
1124				sleep 4	    # Give $LPTELL a chance to tell
1125				exit ${exit_code}
1126			fi
1127
1128			if [ -n "${FF}" -a "no" = "${nofilebreak}" ]
1129			then
1130				echo "${CR}${FF}\c"
1131			fi
1132
1133		else
1134
1135			#####
1136			#
1137			# Don't complain about not being able to read
1138			# a file on second and subsequent copies, unless
1139			# we've not complained yet. This removes repeated
1140			# messages about the same file yet reduces the
1141			# chance that the user can remove a file and not
1142			# know that we had trouble finding it.
1143			#####
1144			if [ "${i}" -le 1 -o -z "${badfileyet}" ]
1145			then
1146				errmsg WARNING ${E_IP_BADFILE} \
1147					"cannot read file \"${file}\"" \
1148					"see if the file still exists and is readable,
1149		or consult your system administrator;
1150		printing continues"
1151				badfileyet=yes
1152			fi
1153
1154		fi
1155
1156	done
1157	i=`expr $i + 1`
1158
1159done
1160
1161# Skip this for TSOL, since lp.tsol_separator handles the banners
1162#
1163# if [ "no" = "${nobanner}" -a "${TERM}" = "PSR" ]
1164# then
1165# 	( eval "${banner} ${banner_filter}" 2>&1 1>&3 ) \
1166# 		| ${LPTELL} ${LPTELL_OPTS} ${printer}
1167# fi
1168
1169if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ]
1170then
1171	exit ${exit_code}
1172fi
1173
1174#####
1175#
1176# Always ensure the complete job ends with a ``formfeed'', to
1177# let the next job start on a new page. (If someone wants to
1178# concatenate files, they can give them in one job.)
1179# So, if we haven't been putting out a ``formfeed'' between files,
1180# it means we haven't followed the last file with a formfeed,
1181# so we do it here.
1182#####
1183if [ -n "${FF}" -a "yes" = "${nofilebreak}" ]
1184then
1185	echo "${CR}${FF}\c"
1186fi
1187
1188${DRAIN}
1189
1190exit_code=0 exit 0
1191