home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/sh
- #ident "@(#)PS 1.3"
- #ident "$Header: $"
-
- ###########
- ##
- ## PostScript interface program.
- ##
- ###########
- umask 0
-
-
- #####
- ##
- ## Some logs.
- ##
- #####
- LOGFILE="/var/tmp/$$.log"
- ERRFILE="/var/tmp/$$.err"
-
- /sbin/rm -f $LOGFILE $ERRFILE
-
- #####
- #
- # Special for debugging.
- #
- #####
- DEBUGFILE="/var/tmp/PS.debug"
- #echo "$LOGFILE" >$DEBUGFILE
- #echo "$ERRFILE" >>$DEBUGFILE
- #echo "$*" >>$DEBUGFILE
- #/usr/bin/env >>$DEBUGFILE
-
- #####
- ##
- ## Some tools. Options will be added later.
- ##
- #####
- LPBIN="/usr/lib/lp/bin"
- PSBIN="/usr/lib/lp/postscript"
- PATH="/bin:/usr/bin:${LPBIN}"
-
- LPCAT="$LPBIN/lp.cat"
- LPTELL="$LPBIN/lp.tell"
-
- #####
- # ${DRAIN} is the name of a program that will wait
- # long enough for data sent to the printer to print.
- #####
- if [ -x "${LPBIN}/drain.output" ]
- then
- DRAIN="${LPBIN}/drain.output 5" # wait only five seconds
- else
- DRAIN=
- fi
-
- POSTPRINT="$PSBIN/postprint"
- DOWNLOAD="$PSBIN/download"
- POSTREVERSE="$PSBIN/postreverse"
- POSTIO="$PSBIN/postio"
- POSTIO_B="$LPCAT"
-
- CAT=/usr/bin/cat
- MAILX=/usr/bin/mailx
-
- #####
- #
- # Until we get to the point below where the printer port
- # and physical printer are initialized, we can't do much
- # except exit if the Spooler/Scheduler cancels us.
- #
- #####
- ExitCode=0
-
- Exit ()
- {
- if [ "$debug" = "yes" -a -n "$user_name" ]
- then
- if [ -s $LOGFILE ]
- then
- $MAILX -s "$request_id: LOG FILE" \
- $user_name <$LOGFILE
- fi
- if [ -s $ERRFILE ]
- then
- $MAILX -s "$request_id: ERROR FILE" \
- $user_name <$ERRFILE
- fi
- if [ -s $DEBUGFILE ]
- then
- $MAILX -s "$request_id: DEBUG FILE" \
- $user_name <$DEBUGFILE
- fi
- fi
- rm -f $ERRFILE $LOGFILE $DEBUGFILE
- if [ -n "$1" ]
- then
- exit $1
- else
- exit $ExitCode
- fi
- }
-
- trap 'Exit 0' 15
-
- #####
- #
- # We can be clever about getting a hangup or interrupt, though, at least
- # until the filter runs. Do this early, even though $LPTELL
- # isn't defined, so that we're covered.
- #
- #####
- catch_hangup ()
- {
- echo \
- "The connection to the printer dropped; perhaps the printer went off-line?" \
- >$ERRFILE
- 0<$ERRFILE ${LPTELL} ${printer}
- $CAT $ERRFILE >>$LOGFILE
- >$ERRFILE
- return 0
- }
-
- catch_interrupt ()
- {
- echo \
- "Received an interrupt from the printer. The reason is unknown,
- although a common cause is that the baud rate is too high." \
- >$ERRFILE
- 0<$ERRFILE ${LPTELL} ${printer}
- $CAT $ERRFILE >>$LOGFILE
- >$ERRFILE
- return 0
- }
- trap 'catch_hangup; Exit 129' 1
- trap 'catch_interrupt; Exit 129' 2 3
-
- #####
- #
- # Most of the time we don't want the standard error to be captured
- # by the Spooler, mainly to avoid "Terminated" messages that the
- # shell puts out when we get a SIGTERM. We'll save the standard
- # error channel under another number, so we can use it when it
- # should be captured.
- #
- # Open another channel to the printer port, for use when the
- # regular standard output won't be directed there, such as in
- # command substitution (`cmd`).
- #####
- exec 5>&2 2>>$ERRFILE 3>&1
-
- #####
- # Error message formatter:
- #
- # Invoke as
- #
- # errmsg severity message-number problem help
- #
- # where severity is "ERROR" or "WARNING", message-number is
- # a unique identifier, problem is a short description of the
- # problem, and help is a short suggestion for fixing the problem.
- #####
-
- LP_ERR_LABEL="UX:lp"
-
- E_IP_ARGS=1
- E_IP_OPTS=2
- #E_IP_FILTER=3
- E_IP_STTY=4
- E_IP_UNKNOWN=5
- E_IP_BADFILE=6
- E_IP_BADCHARSET=7
- E_IP_BADCPI=8
- E_IP_BADLPI=9
- E_IP_BADWIDTH=10
- E_IP_BADLENGTH=11
- E_IP_ERRORS=12 # (in slow.filter)
-
- errmsg ()
- {
- case $1 in
- ERROR )
- sev=" ERROR";
- ;;
- WARNING )
- sev="WARNING";
- ;;
- esac
- echo "${LP_ERR_LABEL}: ${sev}: $3
- TO FIX: $4" >&5
- }
-
- #####
- #
- # This program is invoked as
- #
- # ${SPOOLDIR}/.../printer request-id user title copies options files...
- #
- # The first three arguments are simply reprinted on the banner page,
- # the fourth (copies) is used to control the number of copies to print,
- # the fifth (options) is a blank separated list (in a single argument)
- # of user or Spooler supplied options (without the -o prefix),
- # and the last arguments are the files to print.
- #####
-
- if [ $# -lt 5 ]
- then
- errmsg ERROR ${E_IP_ARGS} \
- "wrong number of arguments to interface program" \
- "consult your system administrator"
- Exit 1
- fi
-
- printer=`basename $0`
- request_id=$1
- user_name=$2
- title=$3
- copies=$4
- option_list=$5
-
- shift 5
- files="$*"
-
- nobanner="no"
- nofilebreak="no"
- debug="no"
- stty=
-
- inlist=
- for i in ${option_list}
- do
- case "${inlist}${i}" in
-
-
- nobanner )
- nobanner="yes"
- ;;
-
- nofilebreak )
- nofilebreak="yes"
- ;;
-
- debug )
- debug="yes"
- ;;
- copies=* )
- ;;
- length=* )
- ;;
- pages=* )
- ;;
-
- #####
- #
- # If you want to add options that, like "stty",
- # take a list (e.g. -o lopt='a b c'), identify
- # them here and below (look for LOPT).
- #####
- stty=* | flist=* | lpd=* )
- #LOPT stty=* | flist=* | lpd=* | lopt=* )
-
- inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"`
- case "${i}" in
- ${inlist}\'*\' )
- item=`expr "${i}" : "^[^=]*='*\(.*\)'\$"`
- ;;
- ${inlist}\' )
- continue
- ;;
- ${inlist}\'* )
- item=`expr "${i}" : "^[^=]*=''*\(.*\)\$"`
- ;;
- ${inlist}* )
- item=`expr "${i}" : "^[^=]*=\(.*\)\$"`
- ;;
- *\' )
- item=`expr "${i}" : "^\(.*\)'\$"`
- ;;
- * )
- item="${i}"
- ;;
- esac
-
- #####
- #
- # We don't dare use "eval" because a clever user could
- # put something in an option value that we'd end up
- # exec'ing.
- #####
- case "${inlist}" in
- stty= )
- stty="${stty} ${item}"
- ;;
- flist= )
- flist="${flist} ${item}"
- ;;
- lpd= )
- lpd="${lpd} ${item}"
- ;;
- #LOPT lopt= )
- #LOPT lopt="${lopt} ${item}"
- #LOPT ;;
- esac
-
- case "${i}" in
- ${inlist}\'*\' )
- inlist=
- ;;
- ${inlist}\'* )
- ;;
- *\' | ${inlist}* )
- inlist=
- ;;
- esac
- ;;
-
- * )
- errmsg WARNING ${E_IP_OPTS} \
- "unrecognized \"-o ${i}\" option" \
- "check the option, resubmit if necessary
- printing continues"
- ;;
- esac
- done
-
- #####
- #
- # Additional ``parameters'' are passed via Shell environment
- # variables:
- #
- # TERM The printer type (used for Terminfo access)
- # FILTER The filter to run
- #####
-
- if [ -z "$TERM" ]
- then
- errmsg ERROR ${E_IP_ARGS} \
- "Missing mandatory environment variable." \
- "Consult your system administrator"
- Exit 1
- fi
- if [ -z "$FILTER" ]
- then
- FILTER="$LPCAT 0"
- fi
-
- #####
- ##
- ## Banner filter
- ##
- #####
- #####
- ##
- ## Where to find fonts (if appropriate).
- ##
- #####
- DOWNLOAD="$DOWNLOAD -p$printer"
-
- POSTIO="$POSTIO 2>>$ERRFILE"
-
- #####
- ## Allow infinite delays on write.
- #####
- POSTIO_B="$POSTIO_B 0 2>>$ERRFILE"
-
- case "${TERM}" in
- "PS" | "PS-r" | "PSR")
- BFILTER="$DOWNLOAD | $POSTIO"
- ;;
-
- "PS-b" | "PS-br" )
- BFILTER="$DOWNLOAD | $POSTIO_B"
- ;;
-
- *)
- errmsg ERROR ${E_IP_ARGS} \
- "Bad mandatory environment variable." \
- "Consult your system administrator."
- Exit 1
- ;;
- esac
-
- EndJob ()
- {
- if [ "$TERM" = "PS-b" -o "$TERM" = "PS-br" ]
- then
- echo "\004\c"
- fi
- }
-
- ###########
- ##
- ## Initialize the printer port
- ##
- ###########
-
- #####
- #
- # SERIAL PORTS:
- # Initialize everything.
- #
- # PARALLEL PORTS:
- # Don't initialize baud rate.
- #
- # It's not obvious how to tell if a port is parallel or serial.
- # However, by splitting the initialization into two steps and letting
- # the serial-only part fail nicely, it'll work.
- #
- # Another point: The output must be a ``tty'' device. If not, don't
- # bother with any of this.
- #####
- stty1= stty2=
- tty 0<&1 1>/dev/null 2>&1 && {
-
- #####
- #
- # First set the default parameters,
- # then the requested parameters.
- #####
-
- stty 9600 0<&1 2>/dev/null 1>&2
-
- stty cs8 -cstopb -parenb -parodd \
- ixon -ixany \
- opost -olcuc onlcr -ocrnl -onocr -onlret -ofill \
- nl0 cr0 tab0 bs0 vt0 ff0 \
- 0<&1 2>/dev/null 1>&2
-
- if [ -n "${stty}" ]
- then
- if stty ${stty} 0<&1 1>/dev/null 2>&5
- then
- :
- else
- errmsg ERROR ${E_IP_STTY} \
- "stty option list failed" \
- "check the \"-o stty\" option you used,
- or consult your system administrator"
- Exit 1
- fi
- fi
-
-
- ##########
- #
- # Find out if we have to turn off opost before initializing the
- # printer and on after. Likewise, check clocal.
- #
- # Turning OFF opost (output postprocessing) keeps the UNIX system
- # from changing what we try to send to the printer. Turning ON
- # clocal keeps the UNIX system from dropping what we are trying to
- # send if the printer drops DTR. An example of the former is the
- # AT&T 479, which wants to send a linefeed (ASCII 10) when a page
- # width of 10 is set; with opost on, this COULD BE turned into a
- # carriage-return/linefeed pair. An example of the latter is the
- # AT&T 455, which momentarily drops DTR when it gets the
- # initialization string, is2; with clocal off, the UNIX system
- # stops sending the rest of the initialization sequence at that
- # point.
- #
- # THIS CODE MUST FOLLOW THE REST OF THE PORT INITIALIZATION CODE.
- ##########
- cur_stty=`stty -a 0<&3`
- expr "${cur_stty}" : '.*-opost' 1>/dev/null 2>&1 \
- || stty1="${stty1} -opost" stty2="${stty2} opost"
- expr "${cur_stty}" : '.*-clocal' 1>/dev/null 2>&1 \
- && stty1="${stty1} clocal" stty2="${stty2} -clocal"
- expr "${cur_stty}" : '.* opost.*' 1>/dev/null 2>&1 \
- || banner_filter=${FIX386BD}
-
- }
-
-
-
- #####
- #
- # Basic initialization. The ``else'' clause is equivalent,
- # but covers cases where old Terminal Information Utilities are present.
- #####
- [ -n "${stty1}" ] && stty ${stty1} 0<&1
- [ -n "${stty2}" ] && stty ${stty2} 0<&1
-
-
- #####
- #
- # Now that the printer is ready for printing, we're able
- # to record on paper a cancellation.
- #
- #####
-
- cancel_page ()
- {
- originator="$user_name"
- if [ -n "$ALIAS_USERNAME" ]
- then
- user="$ALIAS_USERNAME"
- fi
- title="*** Cancelled ***"
- $CAT $PSBIN/banner.ps
- echo "(`LC_ALL=C LC_CTYPE=C LC_TIME=C date '+%a %H:%M %h %d, %Y'`)"
- echo "($request_id)"
- echo "($title)"
- echo "($originator)"
- echo "($user_name)"
- echo "banner"
- }
-
- cancel_job ()
- {
- EndJob # if needed.
- echo "##" >>$LOGFILE
- echo "## Printing cancel page." >>$LOGFILE
- echo "## Banner filter: '$BFILTER'" >>$LOGFILE
- echo "##" >>$LOGFILE
- eval "cancel_page | $BFILTER"
- ExitCode=$?
- EndJob
- if [ $ExitCode -ne 0 ]
- then
- $CAT $ERRFILE >>$LOGFILE
- echo "##" >>$LOGFILE
- echo "## Cancel page failed." >>$LOGFILE
- echo "## ExitCode: $ExitCode" >>$LOGFILE
- echo "##" >>$LOGFILE
- 0<$ERRFILE $LPTELL $printer
- >$ERRFILE
- Exit
- else
- $CAT $ERRFILE >>$LOGFILE
- >$ERRFILE
- fi
- }
- trap 'cancel_job; Exit 0' 15
-
- ###########
- #
- # Print the banner page
- #
- ###########
-
- #####
- #
- # You may want to change the following code to get a custom banner.
- #
- #####
-
- banner_page ()
- {
- originator="$user_name"
- if [ -n "$ALIAS_USERNAME" ]
- then
- user="$ALIAS_USERNAME"
- fi
- if [ -z "${title}" ]
- then
- title="<untitled>"
- fi
- $CAT $PSBIN/banner.ps
- echo "(`LC_ALL=C LC_CTYPE=C LC_TIME=C date '+%a %H:%M %h %d, %Y'`)"
- echo "($request_id)"
- echo "($title)"
- echo "($originator)"
- echo "($user_name)"
- echo "banner"
- }
-
-
- #####
- #
- # Generate banner page for non-reversed job.
- #
- #####
- if [ "no" = "${nobanner}" -a \( "${TERM}" = "PS" -o "$TERM" = "PS-b" \) ]
- then
- echo "##" >>$LOGFILE
- echo "## Printing banner page." >>$LOGFILE
- echo "## Banner filter: '$BFILTER'" >>$LOGFILE
- echo "##" >>$LOGFILE
- eval "banner_page | $BFILTER"
- ExitCode=$?
- EndJob
- if [ $ExitCode -ne 0 ]
- then
- $CAT $ERRFILE >>$LOGFILE
- echo "##" >>$LOGFILE
- echo "## Banner page failed." >>$LOGFILE
- echo "## ExitCode: $ExitCode" >>$LOGFILE
- echo "##" >>$LOGFILE
- 0<$ERRFILE $LPTELL $printer
- >$ERRFILE
- Exit
- else
- $CAT $ERRFILE >>$LOGFILE
- >$ERRFILE
- fi
- fi
-
- ###########
- ##
- ## Print some copies of the file(s)
- ##
- ###########
-
- #####
- #
- # The protocol between the interface program and the Spooler
- # is fairly simple:
- #
- # All standard error output is assumed to indicate a
- # fault WITH THE REQUEST. The output is mailed to the
- # user who submitted the print request and the print
- # request is finished.
- #
- # If the interface program sets a zero exit code,
- # it is assumed that the file printed correctly.
- # If the interface program sets a non-zero exit code
- # less than 128, it is assumed that the file did not
- # print correctly, and the user will be notified.
- # In either case the print request is finished.
- #
- # If the interface program sets an exit code greater
- # than 128, it is assumed that the file did not print
- # because of a printer fault. If an alert isn't already
- # active (see below) one will be activated. (Exit code
- # 128 should not be used at all. The shell, which executes
- # this program, turns SIGTERM, used to kill this program
- # for a cancellation or disabling, into exit 128. The
- # Spooler thus interpretes 128 as SIGTERM.)
- #
- # A message sent to the standard input of the ${LPTELL}
- # program is assumed to describe a fault WITH THE PRINTER.
- # The output is used in an alert (if alerts are defined).
- # If the fault recovery is "wait" or "begin", the printer
- # is disabled (killing the interface program if need be),
- # and the print request is left on the queue.
- # If the fault recovery is "continue", the interface program
- # is allowed to wait for the printer fault to be cleared so
- # it can resume printing.
- #
- # This interface program relies on filters to detect printer faults.
- # In absence of a filter provided by the customer, it uses a simple
- # filter (${LPCAT}) to detect the class of faults that cause DCD
- # (``carrier'') drop. The protocol between the interface program and
- # the filter:
- #
- # The filter should exit with zero if printing was
- # successful and non-zero if printing failed because
- # of a printer fault. This interface program turns a
- # non-zero exit of the filter into an "exit 129" from
- # itself, thus telling the Spooler that a printer fault
- # (still) exists.
- #
- # The filter should report printer faults via a message
- # to its standard error. This interface program takes all
- # standard error output from the filter and feeds it as
- # standard input to the ${LPTELL} program.
- #
- # The filter should wait for a printer fault to clear,
- # and should resume printing when the fault clears.
- # Preferably it should resume at the top of the page
- # that was being printed when the fault occurred.
- # If it waits and finishes printing, it should exit
- # with a 0 exit code. If it can't wait, it should exit
- # with a non-zero exit code.
- #
- # The interface program expects that ANY message on the
- # standard error from the filter indicates a printer fault.
- # Therefore, a filter should not put user (input) error
- # messages on the standard error, but on the standard output
- # (where the user can read them when he or she examines
- # the print-out).
- #
- #####
-
- badfileyet=
- i=1
- while [ $i -le $copies ]
- do
- for file in ${files}
- do
-
- if [ -r "${file}" ]
- then
-
- #####
- #
- # Here's where we set up the $LPTELL program to
- # capture fault messages, and...
- #
- # Here's where we print the file.
- #
- # We set up a pipeline to $LPTELL, but play a trick
- # to get the filter's standard ERROR piped instead of
- # its standard OUTPUT: Divert the standard error (#2) to
- # the standard output (#1) IN THE PIPELINE. The shell
- # will have changed #1 to be the pipe, not the
- # printer, so diverting #2 connects it to the pipe.
- # We then change the filter's #1 to a copy of the real
- # standard output (the printer port) made earlier,
- # so that is connected back to the printer again.
- #
- # We do all this inside a parenthesized expression
- # so that we can get the exit code; this is necessary
- # because the exit code of a pipeline is the exit
- # code of the right-most command, which isn't the
- # filter.
- #
- # These two tricks could be avoided by using a named
- # pipe to connect the standard error to $LPTELL. In
- # fact an early prototype of this script did just
- # that; however, the named pipe introduced a timing
- # problem. The processes that open a named pipe hang
- # until both ends of the pipe are opened. Cancelling
- # a request or disabling the printer often killed one
- # of the processes, causing the other process to hang
- # forever waiting for the other end of the pipe to
- # be opened.
- #####
-
- trap '' 1 # Let the filter handle a hangup
- trap '' 2 3 # and interrupts
-
- #####
- # Put the 0<${file} before the "eval" to keep
- # clever users from giving a file name that
- # evaluates as something to execute.
- #####
- echo "##" >>$LOGFILE
- echo "## Printing file: '$file'" >>$LOGFILE
- echo "##" >>$LOGFILE
- 0<${file} eval ${FILTER} 2>&1 1>&3
- ExitCode=$?
- EndJob
-
- trap 'catch_hangup; exit 129' 1
- trap 'catch_interrupt; exit 129' 2 3
-
- if [ "${ExitCode}" -ne 0 ]
- then
- $CAT $ERRFILE >>$LOGFILE
- echo "##" >>$LOGFILE
- echo "## Job failed." >>$LOGFILE
- echo "## ExitCode: $ExitCode" >>$LOGFILE
- echo "##" >>$LOGFILE
- trap '' 15 # Avoid dying from disable
- 0<$ERRFILE $LPTELL $printer
- >$ERRFILE
- sleep 4 # Give $LPTELL a chance to tell
- Exit 129
- else
- $CAT $ERRFILE >>$LOGFILE
- >$ERRFILE
- fi
- else
- #####
- #
- # Don't complain about not being able to read
- # a file on second and subsequent copies, unless
- # we've not complained yet. This removes repeated
- # messages about the same file yet reduces the
- # chance that the user can remove a file and not
- # know that we had trouble finding it.
- #
- #####
- if [ "${i}" -le 1 -o -z "${badfileyet}" ]
- then
- errmsg WARNING ${E_IP_BADFILE} \
- "cannot read file \"${file}\"" \
- "see if the file still exists and is readable,
- or consult your system administrator;
- printing continues"
- badfileyet=yes
- fi
-
- fi
-
- done
- i=`expr $i + 1`
-
- done
-
- #####
- #
- # Banner page for reversed job.
- #
- #####
- if [ "no" = "${nobanner}" -a \
- \( "${TERM}" = "PS-r" -o "$TERM" = "PS-br" -o "$TERM" = "PSR" \) ]
- then
- echo "##" >>$LOGFILE
- echo "## Printing banner page." >>$LOGFILE
- echo "## Banner filter: '$BFILTER'" >>$LOGFILE
- echo "##" >>$LOGFILE
- eval "banner_page | $BFILTER"
- ExitCode=$?
- EndJob
- if [ $ExitCode -ne 0 ]
- then
- $CAT $ERRFILE >>$LOGFILE
- echo "##" >>$LOGFILE
- echo "## Banner page failed." >>$LOGFILE
- echo "## ExitCode: $ExitCode" >>$LOGFILE
- echo "##" >>$LOGFILE
- 0<$ERRFILE $LPTELL $printer
- >$ERRFILE
- Exit
- else
- $CAT $ERRFILE >>$LOGFILE
- >$ERRFILE
- fi
- fi
-
-
- ${DRAIN}
-
- Exit ${ExitCode}
-