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