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