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