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