#! /bin/sh
# -*- sh -*-

# $ApsCVS: src/apsfilter/bin/apsfilter,v 1.190.2.10 2001/04/01 11:55:05 andreas Exp $

##############################################################################
#
#   apsfilter 6.1.1
#   ---------------
#
#   Copyright by Andreas Klemm <andreas@apsfilter.org>
#   Copyright 1993 - 2001
#
#   You are permitted to modify and distribute apsfilter in the
#   terms of the GNU Public License (GPL) see excerpt below.
#
#   For Unix Systems With BSD Alike Print Mechanism (lpd, /etc/printcap)
#
#   Supported filetypes
#
#       ASCII, BMP, data (PCL, etc.), DVI, FBM, FIG, FITS, GIF, Group 3 fax,
#       HTML, IFF ILBM, JPEG, Kodak Photo CD, MGR, MIFF, PBM/PGM/PNM/PPM, PDF,
#       PNG, PostScript, RLE, SGI, Sketch, Sun raster, Targa, TIFF, troff,
#       WordPerfect graphics, XCF, X pixmap
#
#   Supported compression types
#
#       bzip2, gzip, compress, freeze, pack
#
##############################################################################

##############################################################################
#
#                       C O P Y R I G H T
#
##############################################################################
#
# You are permitted to use this script in the terms of the
#
#                   GNU GENERAL PUBLIC LICENSE
#                      Version 2, June 1991
#
#  Copyright (C) 1989, 1991 Free Software Foundation, Inc.
#                           675 Mass Ave, Cambridge, MA 02139, USA
#  Everyone is permitted to copy and distribute verbatim copies
#  of this license document, but changing it is not allowed.
##############################################################################

##############################################################################
# Start Up
##############################################################################

#-----------------------------------------------------------------------------
# [DEBUG] Uncomment this for command tracing.
#-----------------------------------------------------------------------------

#set -x

#-----------------------------------------------------------------------------
# Set a basic PATH; might have to be extended in $CONF_DIR/apsfilterrc
#-----------------------------------------------------------------------------

PATH="/usr/local/bin:/usr/X11R6/bin:/usr/bin:/bin"

#-----------------------------------------------------------------------------
# apsfilter version; don't change this
#-----------------------------------------------------------------------------

VERSION=6.1.1

#-----------------------------------------------------------------------------
# restrictive umask; needed because private tmp dir must be rwx--x--x
#-----------------------------------------------------------------------------

umask 077

#-----------------------------------------------------------------------------
# Ignore hang up and broken pipe signals.
#-----------------------------------------------------------------------------

trap '' 1 13

#-----------------------------------------------------------------------------
# Remove temporary files before terminating.
#-----------------------------------------------------------------------------

trap 'rm -rf "$APS_TMPDIR"; exit $return_code;' 0

# return code 9: JSIGNAL (for LPRng)
# these are SIGINT SIGTRAP SIGBUS SIGUSR1 SIGUSR2 SIGTERM
trap 'exit 9' 1 5 7 10 12 15

##############################################################################
# Error Reporting Functions
#
# These are guaranteed to work no matter what.
##############################################################################

#=============================================================================
# fatal RETURN_CODE SUMMARY [LINE...]
#
# This is the generic interface for reporting fatal errors.
#
# RETURN_CODE is an integer number that gives the spooler a hint what kind
# of error has occured (mostly for LPRng; BSD-lpr doesn't seem to care,
# as long as the error code is >0)
#	1 (JFAIL):	transient error condition; retry might succeed
#	2 (JABORT):	fatal error; retry probably won't succeed
#
# SUMMARY is a concise one-line message describing the error.  The rest of the
# arguments offer a verbose explanation, and list possible solutions to the
# problem.
#
# Someday soon it will be possible to disable the verbose parts, e.g. for
# experienced users, or large sites, to keep the logs short.
#=============================================================================

fatal()
{
    local line

    # lpd isn't running as root on some systems, so check for permissions.
    [ -w /dev/console ] && exec 2> /dev/console

    return_code=$1; shift

    for line; do echo "apsfilter: $line"; done >&2

    {
	echo "To: ${NOTIFY:-root}"
	echo "Subject: apsfilter: $1"
	echo
	echo "apsfilter fatal error: $1"
	echo
	shift
	for line; do echo "$line"; done
	echo
	echo
	echo "-- apsfilter, your lpd input filter"
    } | send_mail

    exit
}

#=============================================================================
# Error messages.
#
# All of them are in one place, here, to facilitate localization, and to make
# the code look nicer.
#=============================================================================

fatal_basedir()
{
    fatal 2 "can't find apsfilter basedir" \
	    "Please adjust $CONF_DIR/basedir or run SETUP."
}

fatal_config()
{
    fatal 2 "can't find configuration" \
	    "The configuration file $CONF_DIR/$QUEUE/apsfilterrc" \
	    "is missing. Please run SETUP."
}

fatal_file_type()
{
    fatal 2 "unsupported file type '${FILE_TYPE%%,*}'" \
	    "If you think you can help us to support files of this type," \
	    "please contact us on <apsfilter-hackers@apsfilter.org>."
}

fatal_filter()
{
    fatal 2 "missing $1; can't convert file type '${FILE_TYPE%%,*}'" \
	    "Please install $1 if you want to be able to print files of" \
	    "this type."
}

fatal_method()
{
    fatal 2 "invalid method '$METHOD'" \
	  "Please run SETUP."
}

fatal_spooldir()
{
    fatal 2 "can't determine the lpd spool directory" \
	  "Please fix your lpd or stop printing to remote printers" \
	  "directly."
}

fatal_tmpdir()
{
    fatal 1 "error creating directory for temporary files" \
	  "Clean up the '$TMPDIR' directory or change the value of" \
	  "TMPDIR."
}

fatal_unpacker()
{
    fatal 2 "missing $1; can't unpack file type '${FILE_TYPE%%,*}'" \
	  "Please install $1 if you want to be able to print files of" \
	  "this type."
}

##############################################################################
# Auxiliary Functions
#
# These are guaranteed not to overwrite your local variables.
##############################################################################

#=============================================================================
# send_mail
#
# Sends the standard input with "sendmail" to the implied user.
#=============================================================================

send_mail()
{
    local old_ifs path

    old_ifs="$IFS"
    IFS=:
    for path in /usr/sbin /usr/lib $PATH; do
	if [ -x "$path/sendmail" ]; then
	    "$path/sendmail" -oem -t
	    break
	fi
    done
    IFS="$old_ifs"
}

#=============================================================================
# find_filter FILTER
#
# Return false unless FILTER is found in the $PATH.
#=============================================================================

find_filter()
{
    # Unfortunately, FreeBSD and NetBSD ash doesn't dig `command -v'.
    type "$1" > /dev/null 2>&1
}

##############################################################################
# Functions For Printing
#
# These now all take their input from stdin (again); the previous approach
# (to provide commands as parameters that generate the proper input) would
# only save two (at most three) trivial cat commands, but it was very picky
# w.r.t quoting -- and also a pain to read and debug.
##############################################################################

#=============================================================================
# do_convert [FORMAT]
#
# Print all kinds of images.
#=============================================================================

do_convert()
{
    eval convert -rotate '"-90>"' -page "$PAPERSIZE" \
	${MONO:+-colorspace GRAY} ${1:+${1}:}- ps2:- | print_ps
}

#=============================================================================
# do_nconvert [OPTION...]
#
# Print all kinds of images.
#=============================================================================

do_nconvert()
{
    eval nconvert -quiet ${MONO:+-grey 256} ${COLOR:+-truecolors} -out pnm \
	"$@" -o /dev/stdout /dev/stdin | print_pnm
}

#=============================================================================
# print_ascii
#
# Print ASCII files.
#=============================================================================

print_ascii()
{
    local features jobname lang

    jobname="$JOB"
    if [ `echo "$jobname" | wc -c` -gt 40 ]; then
	# try shortening excessively long job name
	jobname=`basename $jobname`
    fi

    case "$ASCII_FILTER" in
	mpage)
	    find_filter mpage || fatal_filter mpage

	    if [ "$MPAGE_OPTS" ]; then
		eval mpage $MPAGE_OPTS | print_ps
	    else
		if [ ! "$MPAGE_PAPERSIZE" ]; then
		    case "$PAPERSIZE" in
			letter) MPAGE_PAPERSIZE=Letter ;;
			legal)  MPAGE_PAPERSIZE=Legal  ;;
			ledger) MPAGE_PAPERSIZE=Ledger ;;
			a3)     MPAGE_PAPERSIZE=A3     ;;
			a4)     MPAGE_PAPERSIZE=A4     ;;
			*)      MPAGE_PAPERSIZE=Letter ;;
		    esac
		fi

		case $ASCII_PPS in
		    2|4|8)	features="-$ASCII_PPS" ;;
		    *)		features="-1" ;;
		esac
		unset LANDSCAPE PS_NUP

		eval mpage -b $MPAGE_PAPERSIZE -da \
		    ${MPAGE_BASIC:--CISO-Latin.1 -f} ${ASCII_BORDER:+-B} \
		    ${ASCII_HEADER:+-H} $features ${ASCII_LANDSCAPE:+-l} \
		    | print_ps
	    fi
	    ;;
	enscript)
	    find_filter enscript || fatal_filter enscript

	    if [ "$ENSCRIPT_OPTS" ]; then
		eval enscript $ENSCRIPT_OPTS -q -p - | print_ps
	    else
		if [ ! "$ENSCRIPT_PAPERSIZE" ]; then
		    case "$PAPERSIZE" in
			letter) ENSCRIPT_PAPERSIZE=Letter ;;
			legal)  ENSCRIPT_PAPERSIZE=Legal  ;;
			a3)     ENSCRIPT_PAPERSIZE=A3     ;;
			a4)     ENSCRIPT_PAPERSIZE=A4     ;;
			a5)     ENSCRIPT_PAPERSIZE=A5     ;;
			*)      ENSCRIPT_PAPERSIZE=Letter ;;
		    esac
		fi
		
		case "$PRINTER" in
		    cdesk*|cdj*|desk*|djet*|hpdj|pcl3)
			case "$ENSCRIPT_PAPERSIZE" in
			    Letter|A4)
				ENSCRIPT_PAPERSIZE="$ENSCRIPT_PAPERSIZE"dj ;;
			esac
			;;
		esac

		case $ASCII_PPS in
		    1|2|4|8)	features="-U $ASCII_PPS" ;;
		    *)		features="" ;;
		esac
		unset LANDSCAPE PS_NUP

		# enscript is not very smart about file magic
		case "$FILE_TYPE" in
		    *awk*)		lang=awk ;;
		    *c\ *program*)	lang=c ;;
		    *c++*)		lang=cpp ;;
		    *python*)		lang=python ;;
		    *postscript*)	lang=postscript ;;
		    *diff*)		lang=diff ;;
		    *html*)		lang=html ;;
		    *mail*|*news*)	lang=mail ;;
		    *perl*)		lang=perl ;;
		    *emacs*lisp*)	lang=elisp ;;
		    *lisp*|*scheme*)	lang=scheme ;;
		    *shell*|*script*)	lang=sh ;;
		esac

		eval enscript -M $ENSCRIPT_PAPERSIZE ${ASCII_BORDER:+-j} \
		    ${ENSCRIPT_BASIC:--X 88591 --color${MONO:+=blackwhite}} \
		    ${ASCII_HEADER:+-b "'%D|\$n|Page \$% of \$='"} $features \
		    ${lang:+-E$lang} -t '"$jobname"' -q -p - | print_ps
	    fi
	    ;;
	recode)
	    find_filter recode || fatal_filter recode
	    eval recode -q ${RECODE_OPTS:-latin1..ibmpc} | print_raw
	    ;;
	*)
	    find_filter a2ps || fatal_filter a2ps

	    if [ "$A2PS_OPTS" ]; then
		eval a2ps $A2PS_OPTS -q -o - | print_ps
	    else
		if [ ! "$A2PS_PAPERSIZE" ]; then
		    case "$PAPERSIZE" in
			letter) A2PS_PAPERSIZE=Letter ;;
			legal)  A2PS_PAPERSIZE=Legal  ;;
			ledger) A2PS_PAPERSIZE=Ledger ;;
			a3)     A2PS_PAPERSIZE=A3     ;;
			a4)     A2PS_PAPERSIZE=A4     ;;
			*)      A2PS_PAPERSIZE=Letter ;;
		    esac
		fi

		case "$PRINTER" in
		    cdesk*|cdj*|desk*|djet*|hpdj|pcl3)
			case "$A2PS_PAPERSIZE" in
			    Letter|A4) A2PS_PAPERSIZE="$A2PS_PAPERSIZE"dj ;;
			esac
			;;
		esac

		case $ASCII_PPS in
		    2|4|8)	features="-$ASCII_PPS" ;;
		    *)		features="-1" ;;
		esac
		unset LANDSCAPE PS_NUP

		# a2ps is not very smart about file magic, either
		case "$FILE_TYPE" in
		    *awk*)		lang=awk ;;
		    *c\ *program*)	lang=c ;;
		    *c++*)		lang=cxx ;;
		    *roff*)		lang=roff ;;
		    *python*)		lang=python ;;
		    *postscript*)	lang=ps ;;
		    *html*)		lang=html ;;
		    *mail*|*news*)	lang=mail ;;
		    *perl*)		lang=perl ;;
		    *scheme*)		lang=scheme ;;
		    *emacs*lisp*)	lang=elisp ;;
		    *lisp*)		lang=clisp ;;
		    *c[-\ ]shell*)	lang=csh ;;
		    *shell*|*script*)	lang=sh ;;
		esac

		[ "$ASCII_BORDER" ] || features="$features --borders=no"
		[ "$ASCII_HEADER" ] || features="$features --no-header"

		eval a2ps -M $A2PS_PAPERSIZE --center-title='"$jobname"' \
		    -b'"Printed by $USER from $HOST"' \
		    ${A2PS_BASIC:--X iso1 -g --prologue=${COLOR:+color}${MONO:+gray}} \
		    $features ${lang:+-E$lang} ${ASCII_LANDSCAPE:+-r} -q \
		    -o - | print_ps
	    fi
	    ;;
    esac
}

#=============================================================================
# print_auto
#
# Print input after using file to determine how.
#=============================================================================

print_auto()
{
    case "$FILE_TYPE" in
	*dvi*)
	    print_dvi		;;
	*fbm*)
	    print_fbm		;;
	*fig*)
	    print_fig		;;
	*fits*)
	    print_fits		;;
	*gif*)
	    print_gif		;;
	*group*3*fax*|*raw*g3*data*)
	    print_g3		;;
	*html*)
	    print_html		;;
	*ilbm*)
	    print_ilbm		;;
	*jpeg*)
	    print_jpeg		;;
	*kodak*photo*cd*)
	    print_pcd		;;
	*mgr*)
	    print_mgr		;;
	*miff*)
	    print_miff		;;
	pc*bitmap*data*)
	    print_bmp		;;
	*pdf*)
	    print_pdf		;;
	*pgm*)
	    print_pgm		;;
	*png*)
	    print_png		;;
	*pnm*|*pbm*|*ppm*)
	    print_pnm		;;
	postscript*)
	    print_ps		;;
	rle*image*)
	    print_rle		;;
	*sgi*image*)
	    print_sgi		;;
	*sketch*)
	    print_sketch   	;;
	sun*raster*|*rasterfile*)
	    print_ras		;;
	*targa*)
	    print_tga		;;
	*tiff*)
	    print_tiff		;;
	*troff*)
	    print_troff		;;
	*wordperfect*graphic*)
	    print_wpg		;;
	*xcf*)
	    print_xcf		;;
	x*pixmap*)
	    print_xpm		;;
	*mail*|*news*|*ascii*|*text*|*english*|*script*)
	    print_ascii		;;
	*data*|*escape*|*pcl*|*pjl*|*printer*job*language*|*ms*windows*)
	    print_data		;;
	*)
	    fatal_file_type	;;
    esac
}

#=============================================================================
# print_bmp
#
# Print (Windows, OS/2) BMP images.
#=============================================================================

print_bmp()
{
    if find_filter convert; then
	# convert doesn't seem to like an explicit "bmp" here
	do_convert
    elif find_filter nconvert; then
	do_nconvert -in bmp
    else
	find_filter bmptoppm || fatal_filter bmptoppm
	bmptoppm | print_pnm
    fi
}

#=============================================================================
# print_data
#
# Print data.
#=============================================================================

print_data()
{
    local c

    print_copy()
    {
	if [ -f "$CONF_DIR/$QUEUE/smbclient.conf" ]; then
	    find_filter smbclient || fatal_filter smbclient
	    . "$CONF_DIR/$QUEUE/smbclient.conf"
	    [ "$SMB_PASSWD" ] && export PASSWD="$SMB_PASSWD"
	    eval smbclient "'//$SMB_SERVER/$SMB_PRINTER'" \
		${SMP_IP:+-I"$SMB_IP"} ${SMB_USER:+-U"$SMB_USER"} \
		${SMB_WORKGROUP:+-W"$SMB_WORKGROUP"} -b"${SMB_BUFFER:-1400}" \
		${SMB_FLAGS:--N} -c "'print -'"
	elif [ -f "$CONF_DIR/$QUEUE/lpr.conf" ]; then
	    . "$CONF_DIR/$QUEUE/lpr.conf"
	    lpr ${REMOTE_NAME:+-P"$REMOTE_NAME"}
	elif [ -f "$CONF_DIR/$QUEUE/pap.conf" ]; then
	    find_filter pap || fatal_filter pap
	    . "$CONF_DIR/$QUEUE/pap.conf"
	    pap -e -p "$PAP_NBPNAME" -s "$SPOOLDIR/pap.status"
	else
	    cat
	fi
    }

    if [ "$SAVE_DUPLEX_COPY" ]; then
	# print_ps_duplex() does the manual copying
	tee "$APS_TMPDIR/copy-$SAVE_DUPLEX_COPY" | print_copy
    else
	if [ "$COPIES" = 1 ]; then
	    print_copy
	else
	    tee "$APS_TMPDIR/copy" | print_copy
	    for c in $(seq 2 "$COPIES"); do
		print_copy < "$APS_TMPDIR/copy"
	    done
	    rm -f "$APS_TMPDIR/copy"
	fi
    fi
}

#=============================================================================
# print_dvi
#
# Print DVI files.
#=============================================================================

print_dvi()
{
    local resolution_x options

    export TEXINPUTS="$TEXINPUTS:$HOMEDIR:/tmp:/var/tmp"

    cat > "$APS_TMPDIR/dvi"
    if [ "$PRINT_DVI" ]; then
	eval $PRINT_DVI < "$APS_TMPDIR/dvi" | print_data
    else
	find_filter dvips || fatal_filter dvips

	[ "$HAVE_MAKETEXPK" ] || options="-M"

	resolution_x="${RESOLUTION%%[!0-9]*}"
	if [ ! "$resolution_x" ]; then
	    case "$PRINTER" in
		ljet4)
		    resolution_x=600 ;;
		epson|bj200|escp2|st800|bjc600|necp6|stcolor|*.upp)
		    resolution_x=360 ;;
		*)
		    resolution_x=300 ;;
	    esac
	fi

	[ "$resolution_x" -gt 400 ] && options="$options -Z"
	[ "$LANDSCAPE" ] && options="$options -t landscape"

	if [ ! "$DVIPS_PAPERSIZE" ]; then
	    case "$PAPERSIZE" in
		letter|legal|ledger) DVIPS_PAPERSIZE=$PAPERSIZE ;;
		a3)                  DVIPS_PAPERSIZE=A3         ;;
		a4)                  DVIPS_PAPERSIZE=A4         ;;
		*)                   DVIPS_PAPERSIZE=letter     ;;
	    esac
	fi

	eval dvips ${DVIPS_RES_DorP:--D} $resolution_x -R -f -q \
	    -t "$DVIPS_PAPERSIZE" $options < "$APS_TMPDIR/dvi" | print_ps
    fi
    rm -f "$APS_TMPDIR/dvi"
}

#=============================================================================
# print_fbm
#
# Print Fuzzy Bitmap images.
#=============================================================================

print_fbm()
{
    find_filter nconvert || fatal_filter nconvert
    do_nconvert -in fbm
}

#=============================================================================
# print_fig
#
# Print xfig files.
#=============================================================================

print_fig()
{
    find_filter fig2dev || fatal_filter fig2dev
    fig2dev -Lps -c -z "$PAPERSIZE" | print_ps
}

#=============================================================================
# print_fits
#
# Print FITS images.
#=============================================================================

print_fits()
{
    if find_filter convert; then
	do_convert fits
    elif find_filter fitstopnm; then
	fitstopnm | print_pnm
    else
	find_filter nconvert || fatal_filter nconvert
	cat > "$APS_TMPDIR/fits"
	do_nconvert -in fits < "$APS_TMPDIR/fits"
	rm -f "$APS_TMPDIR/fits"
    fi
}

#=============================================================================
# print_gif
#
# Print GIF images.
#=============================================================================

print_gif()
{
    if find_filter convert; then
	do_convert gif
    elif find_filter nconvert; then
	do_nconvert -in gif
    else
	find_filter giftopnm || fatal_filter giftopnm
	giftopnm | print_pnm
    fi
}

#=============================================================================
# print_g3
#
# Print Group 3 faxes.
#=============================================================================

print_g3()
{
    local options

    if find_filter convert; then
	do_convert fax
    elif find_filter nconvert; then
	do_nconvert -in fax
    else
	find_filter pbmtolps || fatal_filter pbmtolps
	case "$FILE_TYPE" in
	    *normal*resolution*)  options=-s ;;
	esac
	if find_filter g32pbm; then
	    g32pbm $options
	else
	    find_filter g3topbm || fatal_filter g3topbm
	    g3topbm $options
	fi | pbmtolps -dpi 204x196 | print_ps
    fi
}

#=============================================================================
# print_html
#
# Print HTML files.
#=============================================================================

print_html()
{
    find_filter html2ps || fatal_filter html2ps
    if ! find_filter gs; then
	echo >&2 "apsfilter warning: html2ps needs gs for DSC compliance"
    fi

     eval html2ps ${HTML2PS_OPTS:--e ISO-8859-1 -u -H} ${COLOR:+-U} \
	${MONO:+-g} ${LANDSCAPE:+-L} -D -S '"paper { type: $PAPERSIZE; }"' \
	| print_ps
}

#=============================================================================
# print_ilbm
#
# Print IFF ILBM images.
#=============================================================================

print_ilbm()
{
    if find_filter nconvert; then
	cat > "$APS_TMPDIR/ilbm"
	do_nconvert -in lbm < "$APS_TMPDIR/ilbm"
	rm -f "$APS_TMPDIR/ilbm"
    else
	find_filter ilbmtoppm || fatal_filter ilbmtoppm
	ilbmtoppm | print_pnm
    fi
}

#=============================================================================
# print_jpeg
#
# Print JPEG images.
#=============================================================================

print_jpeg()
{
    if find_filter convert; then
	do_convert jpeg
    elif find_filter nconvert; then
	do_nconvert -in jpeg
    elif find_filter djpeg; then
	if [ "$MONO" ]; then
	    djpeg -grayscale | print_pgm
	else
	    djpeg | print_pnm
	fi
    else
	find_filter jpegtopnm || fatal_filter jpegtopnm
	jpegtopnm | print_pnm
    fi
}

#=============================================================================
# print_mgr
#
# Print MGR images.
#=============================================================================

print_mgr()
{
    if find_filter nconvert; then
	do_nconvert -in mgr
    else
	find_filter mgrtopbm || fatal_filter mgrtopbm
	mgrtopbm | print_pnm
    fi
}

#=============================================================================
# print_miff
#
# Print MIFF images.
#=============================================================================

print_miff()
{
    if find_filter convert; then
	do_convert miff
    else
	find_filter nconvert || fatal_filter nconvert
	do_nconvert -in miff
    fi
}

#=============================================================================
# print_pcd
#
# Print Kodak Photo CD images.
#=============================================================================

print_pcd()
{
    if find_filter convert; then
	do_convert pcd
    elif find_filter nconvert; then
	cat > "$APS_TMPDIR/pcd"
	do_nconvert -in pcd < "$APS_TMPDIR/pcd"
	rm -f "$APS_TMPDIR/pcd"
    elif find_filter pcdtoppm; then
	cat > "$APS_TMPDIR/pcd"
	if [ "$MONO" ]; then
	    pcdtoppm --gray "$APS_TMPDIR/pcd" - | print_pgm
	else
	    pcdtoppm "$APS_TMPDIR/pcd" - | print_pnm
	fi
	rm -f "$APS_TMPDIR/pcd"
    else
	find_filter hpcdtoppm || fatal_filter hpcdtoppm
	cat > "$APS_TMPDIR/pcd"
	hpcdtoppm "$APS_TMPDIR/pcd" | print_pnm
	rm -f "$APS_TMPDIR/pcd"
    fi
}

#=============================================================================
# print_pdf
#
# Print PDF files.
#=============================================================================

print_pdf()
{
    ACROREAD_OPTS="-toPostScript -size ${PAPERSIZE} \
		${ACROREAD_OPTS:--level2 -fast}"

    cat > "$APS_TMPDIR/pdf"
    if find_filter acroread4; then
	eval acroread4 $ACROREAD_OPTS -pairs "$APS_TMPDIR/pdf" /dev/stdout
    elif find_filter acroread; then
	eval acroread $ACROREAD_OPTS -pairs "$APS_TMPDIR/pdf" /dev/stdout
    elif find_filter pdftops; then
	pdftops ${PDFTOPS_OPTS:--q} -paperw $WIDTH_POINTS \
	    -paperh $HEIGHT_POINTS "$APS_TMPDIR/pdf" -
    else
	find_filter pdf2ps || fatal_filter pdf2ps
	pdf2ps -sPAPERSIZE="$PAPERSIZE" "$APS_TMPDIR/pdf" -
    fi | print_ps
    rm -f "$APS_TMPDIR/pdf"
}

#=============================================================================
# print_pgm
#
# Print PGM (greyscale) images.
#=============================================================================

print_pgm()
{
    if find_filter convert; then
	do_convert pgm
    else
	find_filter pnmtops || fatal_filter pnmtops
	pnmtops -width $WIDTH_INCHES -height $HEIGHT_INCHES 2> /dev/null \
	    | print_ps
    fi
}

#=============================================================================
# print_png
#
# Print PNG images.
#=============================================================================

print_png()
{
    case "$FILE_TYPE" in
	*rgba*|*alpha*)
	    # Only pngtopnm can handle the alpha channel.
	    find_filter pngtopnm || fatal_filter pngtopnm
	    pngtopnm -mix -background "#fff" | print_pnm
	    ;;
	*)
	    if find_filter convert; then
		do_convert png
	    elif find_filter nconvert; then
		do_nconvert -in png
	    else
		find_filter pngtopnm || fatal_filter pngtopnm
		pngtopnm | print_pnm
	    fi
	    ;;
    esac
}

#=============================================================================
# print_pnm
#
# Print PNM/PBM/PPM images.
#=============================================================================

print_pnm()
{
    if find_filter convert; then
	do_convert pnm
    else
	if [ "$MONO" ]; then
	    find_filter ppmtopgm || fatal_filter ppmtopgm
	    ppmtopgm | print_pgm
	else
	    find_filter pnmtops || fatal_filter pnmtops
	    pnmtops -width $WIDTH_INCHES -height $HEIGHT_INCHES 2> /dev/null \
		| print_ps
	fi
    fi
}

#=============================================================================
# print_ps
#
# Print PostScript files.
#=============================================================================

print_ps()
{
    local input

    if [ "$DUPLEX" ]; then
	if [ "$HARDWARE_DUPLEX" ]; then
	    PS_DUPLEX=set
	else
	    unset DUPLEX PS_DUPLEX
	    print_ps_duplex
	    return
	fi
    fi

    case "$PRINTER" in
	PS_*dpi)
	    ps_postprocessing | print_data
	    return
	    ;;
    esac

    find_filter gs || fatal_filter gs

    : ${GS_FONTPATH:=/usr/X11R6/lib/X11/fonts/Type1:/var/X11R6/lib/X11/fonts/Type1}
    : ${GS_LIB:=$GS_FONTPATH}
    export GS_FONTPATH GS_LIB

    case "$GS_VERSION" in
	6.*) input=-_ ;;
	*)   input=-  ;;
    esac

    ps_postprocessing | case "$PRINTER" in
	*.upp)
	    eval gs -q -dNOPAUSE -dSAFER @"$PRINTER" -sPAPERSIZE="$PAPERSIZE" \
		-sOutputFile=- ${PS_INIT:+'"$PS_INIT"'} $input \
		${PS_EXIT:+'"$PS_EXIT"'} | print_data
	    ;;
	ppa_*)
	    find_filter pnm2ppa || fatal_filter pnm2ppa
	    [ -f "$CONF_DIR/$QUEUE/pnm2ppa.conf" ] && \
		PPA_OPTS="$PPA_OPTS -f '$CONF_DIR/$QUEUE/pnm2ppa.conf'"
	    eval gs -q -dNOPAUSE -dSAFER -sDEVICE=ppmraw -r600 \
		-sPAPERSIZE="$PAPERSIZE" -sOutputFile=- $input | \
		pnm2ppa -v ${PRINTER#ppa_} $PPA_OPTS ${MONO:+--bw} | print_data
	    ;;
	*)
	    eval gs -q -dNOPAUSE -dSAFER -sDEVICE="$PRINTER" \
		${GS_MODEL:+-sModel="$GS_MODEL"} \
		${RESOLUTION:+-r"$RESOLUTION"} -sPAPERSIZE="$PAPERSIZE" \
		-sOutputFile=- $GS_FEATURES ${PS_INIT:+'"$PS_INIT"'} $input \
		${PS_EXIT:+'"$PS_EXIT"'} | print_data
	    ;;
    esac
}

print_ps_duplex()
{
    local copies c

    # try to set up a special named pipe (FIFO) for communication; has to be
    # world writable
    mkfifo -m 622 "$APS_TMPDIR/duplex-key"

    if [ -p "$APS_TMPDIR/duplex-key" ]; then
	# dump the input into a temporary file while extracting the even pages
	ps_postprocessing | tee "$APS_TMPDIR/duplex.ps" | \
	{
	    # don't process PostScript with the pstools again
	    SKIP_POSTPROCESSING=set
	    [ "$COPIES" != 1 ] && SAVE_DUPLEX_COPY=even
	    if find_filter psselect; then
		psselect -q -e
	    else
		find_filter pstops || fatal_filter pstops
		pstops -q 2:1
	    fi | print_ps
	}
	# the previous setting was just valid inside the subshell
	SKIP_POSTPROCESSING=set

	duplex_notification

	# print the odd pages
	[ "$COPIES" != 1 ] && SAVE_DUPLEX_COPY=odd
	if find_filter psselect; then
	    psselect -q -o < "$APS_TMPDIR/duplex.ps"
	else
	    # pstops exists, otherwise we wouldn't be here (see above)
	    pstops -q 2:0 < "$APS_TMPDIR/duplex.ps"
	fi | print_ps

	rm -f "$APS_TMPDIR/duplex.ps"

	# turn on the copy machine!
	if [ "$COPIES" != 1 ]; then
	    copies="$COPIES"; unset SAVE_DUPLEX_COPY; COPIES=1
	    for c in $(seq 2 "$copies"); do
		print_data < "$APS_TMPDIR/copy-even"
		duplex_notification
		print_data < "$APS_TMPDIR/copy-odd"
	    done
	fi
    else
	# the FIFO couldn't be set up, so print the file simplex style
	echo >&2 "apsfilter warning: duplex fifo couldn't be created"
	print_ps
    fi

    rm -f "$APS_TMPDIR/duplex-key"
}

ps_postprocessing()
{
    if [ "$SKIP_POSTPROCESSING" ]; then
	cat
    else
	eval cat ${PS_UTILS:+| $PS_UTILS} ${PS_BOOK:+| psbook -q} \
	    ${PS_NUP:+| psnup -q -$PS_NUP} ${PS_DUPLEX:+| ps_set_tumble} \
	    ${PAPERTRAY:+| ps_select_tray}
    fi
}

ps_select_tray()
{
    awk "{ print } /^%%EndProlog/ { print \"\
%%BeginSetup\n\
  statusdict begin\n\
  $PAPERTRAY setpapertray\n\
  end\n\
%%EndSetup\" }"
}

ps_set_tumble()
{
    local tumble

    if [ "$BINDING" = short ]; then
	tumble=true
    else
	tumble=false
    fi

    # this code was taken from the psset command (a2ps 4.13b)
    awk "{ print } /^%%EndProlog/ { print \"\
countdictstack\n\
/psset_mark\n\
{\n\
%%BeginFeature: *Duplex true\n\
  (<<) cvx exec /Duplex (true) cvx exec (>>) cvx exec\n\
  systemdict /setpagedevice get exec\n\
%%EndFeature\n\
%%BeginFeature: *Tumble $tumble\n\
  (<<) cvx exec /Tumble ($tumble) cvx exec (>>) cvx exec\n\
  systemdict /setpagedevice get exec\n\
%%EndFeature\n\
} stopped\n\
{ /psset_mark eq { exit } if } loop\n\
countdictstack exch sub dup 0 gt\n\
{\n\
  { end } repeat\n\
}{\n\
  pop\n\
} ifelse\" }"
}

duplex_notification()
{
    local duplex_passwd key

    # send an instruction mail to the user saying to flip the sheets,
    # put them back in and echo a special key into the FIFO to resume
    # the operation
    duplex_passwd=`
	if [ -r /dev/urandom ]; then
	    hexdump -n 8 -e '"%x"' /dev/urandom
	elif [ -r /dev/random ]; then
	    hexdump -n 8 -e '"%x"' /dev/urandom
	elif [ "$RANDOM" ]; then
	    echo "$RANDOM$RANDOM"
	else
	    echo "$$"
	fi | tr -cd [:alnum:]
    `

    {
	cat <<EOF
To: $USER${HOST:+@$HOST}
Subject: duplex notification

You requested to print a file in duplex mode. The even pages have been
sent to the printer. Wait for all pages to be ejected, then flip the
sheets and put them back into the printer. Please insert them so that
the binding is on the $BINDING edge of the _virtual_ page.

To print the odd pages, enter the following command:

echo '$duplex_passwd' > '$APS_TMPDIR/duplex-key'

Note: This command must be entered on a single line. Use cut-and-paste
whenever possible -- switch off auto-wrapping of lines in your mail
reader if needed.


Do not reply to this message. It was generated by apsfilter. In case
of an error, please contact your system administator.
EOF
    } | send_mail

    # now wait for the secret key (busy read from a FIFO)
    while [ "$key" != "$duplex_passwd" ]; do
	read $read_r key < "$APS_TMPDIR/duplex-key"
    done
}

#=============================================================================
# print_ras
#
# Print Sun RasterFiles.
#=============================================================================

print_ras()
{
    if find_filter convert; then
	do_convert ras
    elif find_filter ras2ps; then
	ras2ps ${COLOR:+-C} | print_ps
    elif find_filter nconvert; then
	do_nconvert -in ras
    else
	find_filter rasttopnm || fatal_filter rasttopnm
	rasttopnm | print_pnm
    fi
}

#=============================================================================
# print_raw
#
# Print raw data.
#=============================================================================

print_raw()
{
    {
	[ "$RAW_PROLOGUE" ] && printf $RAW_PROLOGUE
	cat
	[ "$RAW_EPILOGUE" ] && printf $RAW_EPILOGUE
    } | print_data
}

#=============================================================================
# print_rle
#
# Print RLE images.
#=============================================================================

print_rle()
{
    if find_filter convert; then
	do_convert rle
    elif find_filter nconvert; then
	do_nconvert -in rle
    else
	find_filter rletopnm || fatal_filter rletopnm
	rletopnm | print_pnm
    fi
}

#=============================================================================
# print_sgi
#
# Print SGI images.
#=============================================================================

print_sgi()
{
    if find_filter convert; then
	# convert doesn't seem to like an explicit "sgi" here
	do_convert
    elif find_filter nconvert; then
	do_nconvert -in sgi
    else
	find_filter sgitopnm || fatal_filter sgitopnm
	sgitopnm | print_pnm
    fi
}

#=============================================================================
# print_sketch
#
# Print Sketch files.
#
# Note: If your document contains inline images (bitmap, EPS, etc.), it's
#       probably better to convert it to PostScript by hand first, otherwise
#       the relative paths will cause sk2ps to barf.
#=============================================================================

print_sketch()
{
    find_filter sk2ps || fatal_filter sk2ps
    sk2ps /dev/stdin | print_ps
}

#=============================================================================
# print_tga
#
# Print Targa images.
#=============================================================================

print_tga()
{
    if find_filter convert; then
	do_convert tga
    elif find_filter nconvert; then
	do_nconvert -in tga
    else
	find_filter tgatoppm || fatal_filter tgatoppm
	tgatoppm | print_pnm
    fi
}

#=============================================================================
# print_tiff
#
# Print TIFF images.
#=============================================================================

print_tiff()
{
    if find_filter tiff2ps; then
	cat > "$APS_TMPDIR/tiff"
	tiff2ps -a2 -w $WIDTH_INCHES -h $HEIGHT_INCHES "$APS_TMPDIR/tiff" | \
	    print_ps
	rm -f "$APS_TMPDIR/tiff"
    elif find_filter convert; then
	do_convert tiff
    elif find_filter nconvert; then
	cat > "$APS_TMPDIR/tiff"
	do_nconvert -in tiff < "$APS_TMPDIR/tiff"
	rm -f "$APS_TMPDIR/tiff"
    else
	find_filter tifftopnm || fatal_filter tifftopnm
	cat > "$APS_TMPDIR/tiff"
	tifftopnm "$APS_TMPDIR/tiff" | print_pnm
	rm -f "$APS_TMPDIR/tiff"
    fi
}

#=============================================================================
# print_troff
#
# Print troff files.
#=============================================================================

print_troff()
{
    find_filter grog || fatal_filter grog
    find_filter groff || fatal_filter groff
    eval `grog -S -Tps - < "$HEADER"` | print_ps
}

#=============================================================================
# print_wpg
#
# Print WordPerfect graphics.
#=============================================================================

print_wpg()
{
    if find_filter convert; then
	do_convert wpg
    else
	find_filter nconvert || fatal_filter nconvert
	do_nconvert -in wpg
    fi
}

#=============================================================================
# print_xcf
#
# Print GIMP XCF files.
#=============================================================================

print_xcf()
{
    find_filter nconvert || fatal_filter nconvert
    cat > "$APS_TMPDIR/xcf"
    do_nconvert -in xcf < "$APS_TMPDIR/xcf"
    rm -f "$APS_TMPDIR/xcf"
}

#=============================================================================
# print_xpm
#
# Print X pixmaps.
#=============================================================================

print_xpm()
{
    if find_filter convert; then
	do_convert xpm
    elif find_filter nconvert; then
	do_nconvert -in xpm
    else
	find_filter xpmtoppm || fatal_filter xpmtoppm
	xpmtoppm | print_pnm
    fi
}

#=============================================================================
# unpack HANDLER
#
# Unpack input and pass it on to HANDLER.
#=============================================================================

unpack()
{
    local HEADER FILE_TYPE unpacker

    DEPTH=$(($DEPTH + 1))
    HEADER="$APS_TMPDIR/header$DEPTH"
    dd bs=1k count=16 > "$HEADER" 2> /dev/null
    FILE_TYPE=`file "$HEADER"`
    FILE_TYPE=`echo ${FILE_TYPE#$HEADER: } | tr A-Z a-z`

    unpacker=
    case "$FILE_TYPE" in
	bzip*)
	    unpacker=bunzip2 ;;
	gzip*|packed*)
	    unpacker=gunzip  ;;
	compress*)
	    unpacker=zcat    ;;
	frozen*)
	    unpacker=fcat    ;;
    esac
    if [ "$unpacker" ]; then
	find_filter $unpacker || fatal_unpacker $unpacker
	cat "$HEADER" - | $unpacker | unpack "$@"
    else
	cat "$HEADER" - | eval "$@"
    fi

    rm -f "$HEADER"
}

##############################################################################
# Main Body
##############################################################################

#-----------------------------------------------------------------------------
# Process command line arguments.
#
# Recognized options:
#     -c
#     -h HOST
#     -H HOST (LPRng)
#     -n USER
#     -L USER (LPRng)
#     -C CLASS
#     -Z Z_OPTS (LPRng)
#     -f JOB (LPRng)
#
# Options and their arguments can also be concatenated (e.g. `-nroot').
#
# Unrecognized options are ignored.
#-----------------------------------------------------------------------------

while [ $# -gt 0 ]; do
    variable=
    case "$1" in
	-c)      METHOD=raw        ;;
	-h*|-H*) variable=HOST     ;;
	-n*|-L*) variable=USER     ;;
	-C*)     variable=CLASS    ;;
	-Z*)     variable=Z_OPTS   ;;
	-f*)     variable=JOB      ;;
	-*)      variable=variable ;;
	*)       ACCT_FILE="$1"    ;;
    esac
    if [ "$variable" ]; then
	value="${1#-?}"
	if [ ! "$value" ]; then
	    shift
	    value="$1"
	fi
	eval $variable='"$value"'
    fi
    shift
done
unset variable value

#-----------------------------------------------------------------------------
# Directories.
#
# CONF_DIR: The global apsfilter configuration directory.
#
# HOMEDIR: User's home directory (extracted from /etc/passwd).
#
# SPOOLDIR: The lpd spool directory.
#
# TMPDIR: Directory used for storing temporary files.
#
# APS_TMPDIR: Directory used for storing temporary files securely.
#-----------------------------------------------------------------------------

# check if we must use the "-r" flag for "read" operations
if echo dummy | read -r dummy 2>/dev/null; then
    read_r="-r"
else
    unset read_r
fi

CONF_DIR=$(dirname $(dirname $(dirname $0)))

[ -d "$CONF_DIR/basedir" ] || fatal_basedir

HOMEDIR="`eval echo ~$USER`"
case "$HOMEDIR" in
    [!/]*)
	# the shell doesn't know tilde expansion
	HOMEDIR=`
	    if find_filter ypcat; then
		# we're on a NIS (aka yp) client
		ypcat passwd | grep "^$USER:"
	    else
		grep "^$USER:" /etc/passwd
	    fi | \
	    if [ $? -eq 0 ]; then
		# grep found something
		IFS=: read $read_r user passwd uid gid gecos home shell
		echo "$home"
	    fi
	`
	;;
esac

SPOOLDIR="${SPOOL_DIR:-${ACCT_FILE%/*}}"
[ "$SPOOLDIR" ] || fatal_spooldir

# get the printer queue name from the spool dir name
QUEUE=`basename $SPOOLDIR`

#-----------------------------------------------------------------------------
# Process configuration files.
#-----------------------------------------------------------------------------

[ -f "$CONF_DIR/apsfilterrc" ] && . "$CONF_DIR/apsfilterrc"

[ -f "$CONF_DIR/$QUEUE/apsfilterrc" ] || fatal_config
# this file does the basic configuration for each printer (driver name,
# paper size, method, resolution), so it must be present
. "$CONF_DIR/$QUEUE/apsfilterrc"

[ -f "$CONF_DIR/$QUEUE/apsfilterrc.$USER" ] && \
    . "$CONF_DIR/$QUEUE/apsfilterrc.$USER"

[ -n "$USE_USER_CODE" -a -f "$HOMEDIR/.apsfilter/apsfilterrc.$QUEUE" ] && \
    . "$HOMEDIR/.apsfilter/apsfilterrc.$QUEUE"

export PATH

# our private temporary directory has mode 711 so that the user can see our
# fifo "$APS_TMPDIR/duplex-key" (in case it's needed)
: ${TMPDIR:=/tmp}
APS_TMPDIR="$TMPDIR/apsfilter$$"
rm -rf "$APS_TMPDIR"
mkdir -m 711 "$APS_TMPDIR"
if [ $? -ne 0 ]; then
    # maybe someone tried a denial-of-service attack
    find_filter mktemp || fatal_tmpdir
    APS_TMPDIR=`mktemp -q -d "$TMPDIR/apsfilter.XXXXXX"` || fatal_tmpdir
    chmod 711 "$APS_TMPDIR"
fi

# gs doesn't always remove its temp files; let it use our private
# temp directory, so that everything will be removed later on
export TEMP="$APS_TMPDIR" TMPDIR="$APS_TMPDIR"

#-----------------------------------------------------------------------------
# Adjust the printer type and resolution.
#-----------------------------------------------------------------------------

case "$PRINTER" in
    hpdj_*|pcl3_*)
	GS_MODEL=${PRINTER#*_}
	PRINTER=${PRINTER%%_*}
	;;
    hpdj|pcl3)
	GS_MODEL=unspec
	;;
    PS*_*dpi)
	dpi="${PRINTER#PS*_}"
	dpi="${dpi%dpi}"
	RESOLUTION="$dpi"x"$dpi"
	unset dpi
	;;
esac
[ "$RESOLUTION" = default ] && RESOLUTION="$GS_RESOL"

#-----------------------------------------------------------------------------
# Process the control file.
#
# We don't do this when LPRng is used since it gives us all the information we
# need on the command line.
#-----------------------------------------------------------------------------

# LPRng sets $CONTROL, and BSD lpr doesn't.
if [ ! "$CONTROL" ]; then
    {
	read pid
	read $read_r control_file
    } < "$SPOOLDIR/lock"
    while read $read_r line; do
	value="${line#?}"
	case "$line" in
	    C*) CLASS="$value" ;;
	    H*) HOST="$value"  ;;
	    J*) JOB="$value"   ;;
	    N*) FNAME="$value" ;;
	    P*) USER="$value"  ;;
	esac
    done < "$SPOOLDIR/$control_file"
    if [ "x$JOB" = "x" ]; then
	JOB="$FNAME"
    fi

    unset pid control_file line value
fi

#-----------------------------------------------------------------------------
# Don't show a job title if it's coming from standard input.
#
# BSD lpr uses `stdin' for this case, and LPRng uses `(stdin)' or `(STDIN)'.
#-----------------------------------------------------------------------------

[ "$JOB" = stdin -o "$JOB" = '(stdin)' -o "$JOB" = '(STDIN)' ] && unset JOB

#-----------------------------------------------------------------------------
# Ghostscript.
#
# GS_VERSION: Version of Ghostscript.
#-----------------------------------------------------------------------------

find_filter gs && GS_VERSION="`gs --version`"

#-----------------------------------------------------------------------------
# Handle class options and (LPRng specific) Z options.
#-----------------------------------------------------------------------------

if [ "$CLASS$Z_OPTS" ]; then
  old_ifs="$IFS"
  IFS=:,
  set -- `echo "$CLASS,$Z_OPTS" | tr A-Z a-z`
  IFS="$old_ifs"
  unset old_ifs

  for option
  do
    # gs printer driver specific options
    case "$PRINTER" in
      stcolor)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="180x180" ;;
	  med*)     RESOLUTION="360x360" ;;
	  high)     RESOLUTION="720x720" ;;
	  uni*)     GS_FEATURES="$GS_FEATURES -dUnidirectional" ;;
	  micro*)   GS_FEATURES="$GS_FEATURES -dMicroweave" ;;
	  nowea*)   GS_FEATURES="$GS_FEATURES -dnoWeave" ;;
	  soft*)    GS_FEATURES="$GS_FEATURES -dSoftweave" ;;
	  gscmyk|gsmono|gsrgb|fsmono|fsrgb|fsx4|fscmyk|hscmyk|fs2)
		    GS_FEATURES="$GS_FEATURES -sDithering=$option" ;;
	  1bpp|3bpp|8bpp|16bpp|24bpp|30bpp|32bpp)
		    GS_FEATURES="$GS_FEATURES -dBitsPerPixel=${option%bpp}" ;;
	  flag0)    GS_FEATURES="$GS_FEATURES -dFlag0" ;;
	  plain)    GS_FEATURES="$GS_FEATURES -sOutputCode=plain" ;;
	  runl*)    GS_FEATURES="$GS_FEATURES -sOutputCode=runlength" ;;
	  delt*)    GS_FEATURES="$GS_FEATURES -sOutputCode=deltarow" ;;
	esac
	;;
      cdj670|cdj850|cdj890|cdj1600|cdj970)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="150x150" ;;
	  med*)     RESOLUTION="300x300" ;;
	  high)     RESOLUTION="600x600" ;;
	  plain)    GS_FEATURES="$GS_FEATURES -dPapertype=0" ;;
	  bond)     GS_FEATURES="$GS_FEATURES -dPapertype=1" ;;
	  spec*)    GS_FEATURES="$GS_FEATURES -dPapertype=2" ;;
	  glos*)    GS_FEATURES="$GS_FEATURES -dPapertype=3" ;;
	  trans*)   GS_FEATURES="$GS_FEATURES -dPapertype=4" ;;
	  draft)    GS_FEATURES="$GS_FEATURES -dQuality=-1" ;;
	  norm*)    GS_FEATURES="$GS_FEATURES -dQuality=0" ;;
	  pres*)    GS_FEATURES="$GS_FEATURES -dQuality=1" ;;
	  retoff)   GS_FEATURES="$GS_FEATURES -dRetStatus=0" ;;
	  reton)    GS_FEATURES="$GS_FEATURES -dRetStatus=1" ;;
	  1bpp|3bpp|8bpp|16bpp|24bpp|30bpp|32bpp)
		    GS_FEATURES="$GS_FEATURES -dBitsPerPixel=${option%bpp}" ;;
	esac
	;;
      cdj880)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="150x150" ;;
	  med*)     RESOLUTION="300x300" ;;
	  high)     RESOLUTION="600x600" ;;
	  plain)    GS_FEATURES="$GS_FEATURES -dPapertype=0" ;;
	  bond)     GS_FEATURES="$GS_FEATURES -dPapertype=1" ;;
	  spec*)    GS_FEATURES="$GS_FEATURES -dPapertype=2" ;;
	  glos*)    GS_FEATURES="$GS_FEATURES -dPapertype=3" ;;
	  transp*)  GS_FEATURES="$GS_FEATURES -dPapertype=4" ;;
	  draft)    GS_FEATURES="$GS_FEATURES -dQuality=-1" ;;
	  norm*)    GS_FEATURES="$GS_FEATURES -dQuality=0" ;;
	  pres*)    GS_FEATURES="$GS_FEATURES -dQuality=1" ;;
	  1bpp|3bpp|8bpp|16bpp|24bpp|30bpp|32bpp)
		    GS_FEATURES="$GS_FEATURES -dBitsPerPixel=${option%bpp}" ;;
	esac
	;;
      cdj500|pjtest|declj250)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="150x150" ;;
	  med*)     RESOLUTION="300x300" ;;
	  high)     RESOLUTION="600x600" ;;
	  plain)    GS_FEATURES="$GS_FEATURES -dPapertype=0" ;;
	  bond)     GS_FEATURES="$GS_FEATURES -dPapertype=1" ;;
	  spec*)    GS_FEATURES="$GS_FEATURES -dPapertype=2" ;;
	  glos*)    GS_FEATURES="$GS_FEATURES -dPapertype=3" ;;
	  transp*)  GS_FEATURES="$GS_FEATURES -dPapertype=4" ;;
	  draft)    GS_FEATURES="$GS_FEATURES -dQuality=-1" ;;
	  norm*)    GS_FEATURES="$GS_FEATURES -dQuality=0" ;;
	  pres*)    GS_FEATURES="$GS_FEATURES -dQuality=1" ;;
	  1bpp|3bpp|8bpp|16bpp|24bpp)
		    GS_FEATURES="$GS_FEATURES -dBitsPerPixel=${option%bpp}" ;;
	esac
	;;
      cdj550)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="150x150" ;;
	  med*)     RESOLUTION="300x300" ;;
	  high)     RESOLUTION="600x600" ;;
	  plain)    GS_FEATURES="$GS_FEATURES -dPapertype=0" ;;
	  bond)     GS_FEATURES="$GS_FEATURES -dPapertype=1" ;;
	  spec*)    GS_FEATURES="$GS_FEATURES -dPapertype=2" ;;
	  glos*)    GS_FEATURES="$GS_FEATURES -dPapertype=3" ;;
	  transp*)  GS_FEATURES="$GS_FEATURES -dPapertype=4" ;;
	  draft)    GS_FEATURES="$GS_FEATURES -dQuality=-1" ;;
	  norm*)    GS_FEATURES="$GS_FEATURES -dQuality=0" ;;
	  pres*)    GS_FEATURES="$GS_FEATURES -dQuality=1" ;;
	  1bpp|3bpp|8bpp|16bpp|24bpp|32bpp)
		    GS_FEATURES="$GS_FEATURES -dBitsPerPixel=${option%bpp}" ;;
	esac
	;;
      pjxl300|pjxltest)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="150x150" ;;
	  med*)     RESOLUTION="300x300" ;;
	  high)     RESOLUTION="600x600" ;;
	  draft)    GS_FEATURES="$GS_FEATURES -dPrintQuality=-1" ;;
	  norm*)    GS_FEATURES="$GS_FEATURES -dPrintQuality=0" ;;
	  pres*)    GS_FEATURES="$GS_FEATURES -dPrintQuality=1" ;;
	  render[0-9]|render10)
		    GS_FEATURES="$GS_FEATURES -dRenderType=${option#render}" ;;
	  1bpp|3bpp|8bpp|16bpp|24bpp|32bpp)
		    GS_FEATURES="$GS_FEATURES -dBitsPerPixel=${option%bpp}" ;;
	esac
	;;
      bjc600|bjc800)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="150x150" ;;
	  med*)     RESOLUTION="300x300" ;;
	  high)     RESOLUTION="600x600" ;;
	  low)      GS_FEATURES="$GS_FEATURES -dPrintQuality=Low";;
	  draft)    GS_FEATURES="$GS_FEATURES -dPrintQuality=Draft";;
	  norm*)    GS_FEATURES="$GS_FEATURES -dPrintQuality=Normal";;
	  pres*)    GS_FEATURES="$GS_FEATURES -dPrintQuality=High" ;;
	  plain)    GS_FEATURES="$GS_FEATURES -dMediaType=PlainPaper";;
	  coated)   GS_FEATURES="$GS_FEATURES -dMediaType=CoatedPaper" ;;
	  trans*)   GS_FEATURES="$GS_FEATURES -dMediaType=TransparencyFilm" ;;
	  envel*)   GS_FEATURES="$GS_FEATURES -dMediaType=Envelope" ;;
	  card)     GS_FEATURES="$GS_FEATURES -dMediaType=Card" ;;
	  other)    GS_FEATURES="$GS_FEATURES -dMediaType=Other" ;;
	  1bpp|4bpp|8bpp|16bpp|24bpp|32bpp)
		    GS_FEATURES="$GS_FEATURES -dBitsPerPixel=${option%bpp}" ;;
	esac
	;;
      hpdj|pcl3)
	# gs driver specific options
	case "$option" in
	  low)      RESOLUTION="150x150" ;;
	  med*)     RESOLUTION="300x300" ;;
	  high)     RESOLUTION="600x600" ;;
	  500|510|520|540|unspec)
		    GS_MODEL="$option" ;;
	  500c|550c|560c|850c|855c)
		    GS_MODEL="${option%c}C" ;;
	  mono)     GS_FEATURES="$GS_FEATURES -sColorMode=mono" ;;
	  cmy)      COLOR=color
		    GS_FEATURES="$GS_FEATURES -sColorMode=CMY" ;;
	  cmy+k)    COLOR=color
		    GS_FEATURES="$GS_FEATURES -sColorMode=CMY+K" ;;
	  cmyk)     COLOR=color
		    GS_FEATURES="$GS_FEATURES -sColorMode=CMYK" ;;
	  draft)    GS_FEATURES="$GS_FEATURES -sPrintQuality=draft" ;;
	  norm*)    GS_FEATURES="$GS_FEATURES -sPrintQuality=normal" ;;
	  pres*)    GS_FEATURES="$GS_FEATURES -sPrintQuality=presentation";;
	  plain)    GS_FEATURES="$GS_FEATURES -sMediaType=plain" ;;
	  bond)     GS_FEATURES="$GS_FEATURES -sMediaType=bond" ;;
	  prem*)    GS_FEATURES="$GS_FEATURES -sMediaType=Premium" ;;
	  glos*)    GS_FEATURES="$GS_FEATURES -sMediaType=glossy" ;;
	  trans*)   GS_FEATURES="$GS_FEATURES -sMediaType=transparency" ;;
	  photo)    GS_FEATURES="$GS_FEATURES -sMediaType=photo" ;;
	esac
	;;
      ppa_*)
	# PPA-printers for use with pnm2ppa
	case "$option" in
	  uni|bi|eco)	PPA_OPTS="$PPA_OPTS --$option" ;;
	  noblack)	PPA_OPTS="$PPA_OPTS -p" ;;
	  fast)		PPA_OPTS="$PPA_OPTS --fd" ;;
	esac
	;;
      stp_*)
	# gimp-print driver
	# (to be done)
	;;
      omni_*)
	# IBM's omni driver
	# (to be done)
	;;
      PS_*dpi|PSgs_*dpi)
	# PostScript printer options
	;;
    esac

    # printer driver independent options
    case "$option" in
      a3|a4|legal|letter|ledger)	# set paper size
	PAPERSIZE=$option ;;
      ascii|auto|raw)		# force printing method
	METHOD=$option ;;
      a2ps|mpage|enscript|recode)	# choose filter for ASCII files
	ASCII_FILTER=$option ;;
      1pps|2pps|4pps|8pps)	# pages per sheet
	PS_NUP=${option%pps}; ASCII_PPS=$PS_NUP ;;
      header|noheader)		# use ASCII headers?
	ASCII_HEADER=${option#noheader} ;;
      border|noborder)		# use ASCII borders?
	ASCII_BORDER=${option#noborder} ;;
      duplex|simplex)		# use duplex printing?
        DUPLEX=${option#simplex} ;;
      shortbind|longbind)	# binding edge for hardware duplex
	BINDING=${option%bind} ;;
      tray[0-9])		# paper tray selection
	PAPERTRAY=${option#tray} ;;
      book)			# "book" printing mode
	PS_BOOK=set PS_NUP=2 ASCII_PPS=2 DUPLEX=set BINDING=short ;;
      land*|port*)		# force paper orientation
	LANDSCAPE=${option##port*}; ASCII_LANDSCAPE=$LANDSCAPE ;;
      color|colour|mono)	# use color or grayscale
	COLOR=$option ;;
      copies=*)			# no of copies
        COPIES=${option#copies=} ;;
    esac
  done
  unset option
fi

#-----------------------------------------------------------------------------
# Fix the options.
#-----------------------------------------------------------------------------

: ${MAXCOPIES:=10}
[ "${COPIES##*[^0-9]*}" ] || COPIES=1  # now $COPIES is an integer number
[ "$COPIES" -gt "$MAXCOPIES" ] && COPIES="$MAXCOPIES"
[ "$COPIES" = 0 ] && exit  # printing 0 copies is _really_ fast :)

[ "$COLOR" = mono ] && COLOR= MONO=set || COLOR=set MONO=
[ "$LANDSCAPE" ] && PORTRAIT= || LANDSCAPE= PORTRAIT=set
[ "$BINDING" = short ] || BINDING=long
[ "$DISABLE_DUPLEX" ] && unset DUPLEX
: ${ASCII_LANDSCAPE=$LANDSCAPE} ${ASCII_PPS=$PS_NUP}

case "$PRINTER" in
    hpdj|pcl3)
	if [ "$GS_MODEL" = u970 ]; then
	    GS_MODEL=unspec
	    if [ "$RESOLUTION" = 600x600 ]; then
		GS_FEATURES="$GS_FEATURES -sPrintQuality=presentation"
	    fi
	fi
	set -- $GS_FEATURES
	GS_FEATURES=
	for feature; do
	    case "$feature" in
		-dBitsPerPixel=*)                                     ;;
		*)                GS_FEATURES="$GS_FEATURES $feature" ;;
	    esac
	done
	unset feature
	;;
    ljet4l)
	# This printer needs a fixed resolution.
	PRINTER=ljet4
	RESOLUTION=300x300
	;;
    stp_*)
	GS_MODEL=${PRINTER#stp_}
	PRINTER=stp
	GS_FEATURES="$GS_FEATURES -dColor=${COLOR:+1}${MONO:+0}"
	;;
    omni_*)
	GS_FEATURES="$GS_FEATURES -sDeviceName=${PRINTER#omni_} \
	    -sproperties='${MONO:+printmode=PRINT_MODE_1_ANY} \
	    form=FORM_$(echo $PAPERSIZE | tr a-z A-Z)'"
	PRINTER=omni
	;;
    ppa_*)
	case "$PAPERSIZE" in
	    a3)		PAPERSIZE=a4 ;;
	    a4|legal)	;;
	    *)		PAPERSIZE=letter ;;
	esac
	;;
    PSgs_*dpi)
	RESOLUTION="${PRINTER#PSgs_}"; RESOLUTION="${RESOLUTION%dpi}"
	[ "$COLOR" ] && PRINTER=pswrite || PRINTER=psgray
	;;
    cdj970)
	# this inkjet driver can handle duplex printing
	if [ -n "$DUPLEX" -a -n "$HARDWARE_DUPLEX" ]; then
	    if [ "$BINDING" = short ]; then
		GS_FEATURES="$GS_FEATURES -dDuplex=1"
	    else
		GS_FEATURES="$GS_FEATURES -dDuplex=2"
	    fi
	    unset DUPLEX
	else
	    GS_FEATURES="$GS_FEATURES -dDuplex=0"
	fi
	;;
esac

case "$PS_NUP" in
    2|4|8)	;;
    *)		unset PS_NUP ;;
esac

case "$PAPERSIZE" in
    a4)		WIDTH_POINTS=595;   HEIGHT_POINTS=842
		WIDTH_INCHES=8.26;  HEIGHT_INCHES=11.69 ;;
    a3)		WIDTH_POINTS=842;   HEIGHT_POINTS=1190
		WIDTH_INCHES=11.69; HEIGHT_INCHES=16.53 ;;
    letter)	WIDTH_POINTS=612;   HEIGHT_POINTS=792
		WIDTH_INCHES=8.5;   HEIGHT_INCHES=11    ;;
    legal)	WIDTH_POINTS=612;   HEIGHT_POINTS=1008
		WIDTH_INCHES=8.5;   HEIGHT_INCHES=14    ;;
    ledger)	WIDTH_POINTS=1224;  HEIGHT_POINTS=792
		WIDTH_INCHES=17;    HEIGHT_INCHES=11    ;;
    *)		# use safe default values (small enough)
		WIDTH_POINTS=595;   HEIGHT_POINTS=792
		WIDTH_INCHES=8.26;  HEIGHT_INCHES=11    ;;
esac

#-----------------------------------------------------------------------------
# [DEBUG] If you uncomment this, temporary files will not be removed upon
#         termination.
#-----------------------------------------------------------------------------

#trap : 0

#-----------------------------------------------------------------------------
# [DEBUG] If you uncomment this, all output will be saved to a temporary file,
#         and nothing will be sent to the printer.
#-----------------------------------------------------------------------------

#print_data() { cat > "$APS_TMPDIR/data"; }

#-----------------------------------------------------------------------------
# [DEBUG] If you uncomment this, PostScript input will not be processed by gs.
#         (Useful with the line above.)
#-----------------------------------------------------------------------------

#print_ps() { ps_postprocessing | print_data; }

#-----------------------------------------------------------------------------
# Call the appropriate printing function.
#-----------------------------------------------------------------------------

case "$METHOD" in
    auto)  unpack print_auto	;;
    ascii) unpack print_ascii	;;
    raw)   print_raw		;;
    *)     fatal_method		;;
esac
