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