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

# $ApsCVS: src/apsfilter/bin/apsfilter.in,v 1.212.2.17 2006/07/09 15:54:24 andreas Exp $

##############################################################################
#
#   apsfilter 7.2.7
#   ---------
#
#   Copyright by Andreas Klemm <andreas@apsfilter.org>
#   Copyright 1993 - 2002
#
#   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
#
#	archives (print summary of various types), ASCII, BMP, data (PCL
#	etc.), DVI, FBM, FIG, FITS, GIF, Group 3 fax, HTML, IFF ILBM, JPEG,
#	Kodak Photo CD, MGR, MIFF, PAM/PBM/PGM/PNM/PPM, PDF, PNG, PostScript,
#	RLE, SGI, Sketch, Sun raster, Targa, TGIF, TIFF, troff, WMF,
#	WordPerfect graphics, XCF (gimp), X pixmap, X window dump
#
#   Supported compression types
#
#	bzip2, gzip, compress, freeze, pack, lzop
#
##############################################################################

##############################################################################
#
#                       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
##############################################################################

#
# For easy debugging of apsfilter, please use the "-D" flag to "aps2file"
#

#-----------------------------------------------------------------------------
# Set a basic PATH; might have to be extended in
# /etc/apsfilter/apsfilterrc
#-----------------------------------------------------------------------------

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

#-----------------------------------------------------------------------------
# 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 'rc=`cat "$APS_TMPDIR/rc"`; rm -rf "$APS_TMPDIR"; exit $rc' 0

# return code 9: JSIGNAL (for LPRng)
# these are SIGINT SIGTRAP SIGBUS SIGUSR1 SIGUSR2 SIGTERM
trap 'echo 9 >"$APS_TMPDIR/rc"; exit' 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

    echo $1 >"$APS_TMPDIR/rc"; shift

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

    if [ -z "$APS2FILE_CONTEXT" ]; then
	{
	    echo "To: ${NOTIFY:-root}"
	    echo "Cc: $USER${HOST:+@$HOST}"
	    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"
	} | "/usr/sbin/sendmail" -oem -t
    fi

    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_driver_script()
{
    fatal 2 "driver script '$DRIVER_SCRIPT' for '$PRINTER' not found" \
	    "The driver script '$DRIVER_SCRIPT' which sets" \
	    "the options for the printer '$PRINTER'" \
	    "was not found. The file driver/MAPPING is corrupt."
}

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 adjust $CONF_DIR/$QUEUE/apsfilterrc." \
	  "Method must be 'auto', 'ascii' or 'raw'."
}

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.
##############################################################################

#=============================================================================
# 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()
{
    local opts

    case "$COLOR" in
    	gray)	opts="-colorspace GRAY" ;;
	mono)	opts="-monochrome" ;;
    esac
    eval convert -rotate '"-90>"' -page '"${WIDTH_POINTS}x${HEIGHT_POINTS}>"' \
	$opts ${1:+${1}:}- ps:- | print_ps
}

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

do_nconvert()
{
    local opts

    [ "$COLOR" = full ] && opts="-truecolors" || opts="-grey 256"
    eval nconvert -quiet $opts -out pnm "$@" -o /dev/stdout /dev/stdin \
	| print_pnm
}

#=============================================================================
# play_mp3
#
# play mp3 audio files
#=============================================================================

play_mp3()
{
    if [ "$INTERFACE" = "network" ]; then
	cat -
    else
	cat > "$APS_TMPDIR/mp3"
	if find_filter mpg123; then
	    mpg123 -q "$APS_TMPDIR/mp3"
	else
	    find_filter mpg321 || fatal_filter mpg321
	    mpg321 -q "$APS_TMPDIR/mp3"
	fi
	rm -f "$APS_TMPDIR/mp3"
    fi
}

#=============================================================================
# play_ogg
#
# play Ogg Vorbis audio files
#=============================================================================

play_ogg()
{
    if [ "$INTERFACE" = "network" ]; then
	cat -
    else
	find_filter ogg123 || fatal_filter ogg123
	cat > "$APS_TMPDIR/ogg"
	ogg123 --quiet "$APS_TMPDIR/ogg"
	rm -f "$APS_TMPDIR/ogg"
    fi
}

#=============================================================================
# play_riff
#
# play RIFF data, WAVE audio, Microsoft PCM
#=============================================================================

play_riff()
{
    if [ "$INTERFACE" = "network" ]; then
	cat -
    else
	if find_filter wavplay; then
	    cat > "$APS_TMPDIR/riff"
	    wavplay -q "$APS_TMPDIR/riff"
	    rm -f "$APS_TMPDIR/riff"
	else
	    find_filter play || fatal_filter play
	    play --type=wav --silent -
	fi
    fi
}

#=============================================================================
# play_sound
#
# play audio input using file command to determine how.
# This routine is used when using a dedicated print queue for playing
# sound on the local machine
#=============================================================================

play_sound ()
{
    case "$FILE_TYPE" in
	mp3*)
	    play_mp3		;;
	ogg*)
	    play_ogg		;;
	riff*)
	    play_riff		;;
	*)
	    fatal_file_type	;;
    esac
}

#=============================================================================
# print_ar
#
# Print a summary of an AR archive.
#=============================================================================

print_ar()
{
    find_filter ar || fatal_filter ar
    cat > "$APS_TMPDIR/ar"
    ar -tv "$APS_TMPDIR/ar" | print_ascii
    rm -f "$APS_TMPDIR/ar"
}

#=============================================================================
# print_arc
#
# Print a summary of an ARC archive.
#=============================================================================

print_arc()
{
    if find_filter unarc; then
	unarc v /dev/stdin | print_ascii
    else
	find_filter arc || fatal_filter arc
	arc v /dev/stdin | print_ascii
    fi
}

#=============================================================================
# print_arj
#
# Print a summary of an ARJ archive.
#=============================================================================

print_arj()
{
    find_filter unarj || fatal_filter unarj
    cat > "$APS_TMPDIR/archive.arj"
    unarj l "$APS_TMPDIR/archive.arj" | print_ascii
    rm -f "$APS_TMPDIR/archive.arj"
}

#=============================================================================
# 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
		: ${MPAGE_PAPERSIZE:=$PAPERSIZE}

		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

		if [ "$PRETTY_PRINTING" != 0 ]; then
		    # 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
		fi

		: ${ENSCRIPT_BASIC:=-X 88591}
		if [ "$COLOR" = full ]; then
		    features="$features --color"
		else
		    features="$features --color=blackwhite"
		fi
		eval enscript -M $ENSCRIPT_PAPERSIZE ${ASCII_BORDER:+-j} \
		    $ENSCRIPT_BASIC ${lang:+-E$lang} -t '"$jobname"' \
		    ${ASCII_HEADER:+-b "'%D|\$n|Page \$% of \$='"} $features \
		    ${ASCII_LANDSCAPE:+-r} -q -p - | print_ps
	    fi
	    ;;
	recode)
	    find_filter recode || fatal_filter recode
	    {
		[ "$RECODE_PROLOGUE" ] && printf $RECODE_PROLOGUE
		eval recode -q ${RECODE_OPTS:-latin1..ibmpc}
		[ "$RECODE_EPILOGUE" ] && printf $RECODE_EPILOGUE
	    } | print_raw
	    ;;
	*)
	    find_filter a2ps || fatal_filter a2ps

	    if [ "$A2PS_OPTS" ]; then
		eval a2ps $A2PS_OPTS -q -o - | print_ps
	    else
		: ${A2PS_PAPERSIZE:=$PAPERSIZE}
		case "$PRINTER" in
		    cdesk*|cdj*|desk*|djet*|hpdj|pcl3)
			case "$A2PS_PAPERSIZE" in
			    Letter|letter|A4|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

		: ${A2PS_BASIC:=--delegate=no -X iso1}
		[ "$ASCII_BORDER" ] || features="$features --borders=no"
		[ "$ASCII_HEADER" ] || features="$features --no-header"
		case "$COLOR" in
		    full)  features="$features --prologue=color" ;;
		    gray)  features="$features --prologue=gray" ;;
		    mono)  features="$features --prologue=bw" ;;
		esac
		case "$PRETTY_PRINTING" in
		    0)	features="$features --highlight-level=none" ;;
		    1)	features="$features --highlight-level=normal" ;;
		    2)	features="$features --highlight-level=heavy" ;;
		esac

		eval a2ps -M $A2PS_PAPERSIZE --center-title='"$jobname"' \
		    -b'"Printed by $USER from $HOST"' $A2PS_BASIC \
		    $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()
{
    # these patterns are based on the magic database of file-3.37,
    # with some additions

    case "$FILE_TYPE" in
	arc\ archive*)
	    print_arc		;;
	arj\ archive*)
	    print_arj		;;
	fbm\ image*)
	    print_fbm		;;
	fig\ image*)
	    print_fig		;;
	fits\ image*)
	    print_fits		;;
	gif\ image*)
	    print_gif		;;
	gimp\ xcf\ image*)
	    print_xcf		;;
	group\ 3\ fax*|raw\ g3\ data*)
	    print_g3		;;
	html\ document*|exported\ sgml*|\
	iso-8859\ html\ document*|iso-8859\ exported\ sgml*)
	    print_html		;;
	iff*ilbm*)
	    print_ilbm		;;
	jpeg\ image*)
	    print_jpeg		;;
	kodak\ photo\ cd*)
	    print_pcd		;;
	lha*archive*)
	    print_lha		;;
	mgr\ bitmap*)
	    print_mgr		;;
	microsoft\ cabinet*)
	    print_cab		;;
	miff\ image*)
	    print_miff		;;
	mp3*)
	    play_mp3		;;
	ms-windows*wmf*)
	    print_wmf		;;
	ogg*)
	    play_ogg		;;
	pam*image*|netpbm\ pam*image*)
	    print_pam		;;
	pbm*image*|netpbm\ pbm*image*|\
	pnm*image*|netpbm\ pnm*image*|\
	ppm*image*|netpbm\ ppm*image*)
	    print_pnm		;;
	pc\ bitmap*)
	    print_bmp		;;
	pdf\ document*)
	    print_pdf		;;
	pgm*image*|netpbm\ pgm*image*)
	    print_pgm		;;
	png\ image*)
	    print_png		;;
	posix\ tar\ archive*|gnu\ tar\ archive*)
	    print_tar		;;
	postscript\ document*)
	    print_ps		;;
	rar\ archive*)
	    print_rar		;;
	riff*)
	    play_riff		;;
	rle\ image*)
	    print_rle		;;
	rpm*)
	    print_rpm		;;
	sgi\ image*)
	    print_sgi		;;
	sketch\ document*)
	    print_sketch   	;;
	sun\ raster\ image*|rasterfile*)
	    print_ras		;;
	targa\ image*)
	    print_tga		;;
	tex\ dvi*)
	    print_dvi		;;
	tiff\ image*)
	    print_tiff		;;
	tgif\ file*)
	    print_tgif		;;
	troff*)
	    print_troff		;;
	wordperfect\ graphic*)
	    print_wpg		;;
	x\ pixmap\ image*)
	    print_xpm		;;
	xwd*image*)
	    print_xwd		;;
	zip\ archive*)
	    print_zip		;;
	zoo\ archive*)
	    print_zoo		;;

	# patterns which might cause a mismatch (mostly via strings in
	# file(1)'s output that appear in free form)
	*ar\ archive*)
	    print_ar		;;
	*cpio\ archive*)
	    print_cpio		;;

	# generic patterns
	*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
	do_convert bmp
    elif find_filter nconvert; then
	do_nconvert -in bmp
    else
	find_filter bmptoppm || fatal_filter bmptoppm
	bmptoppm | print_pnm
    fi
}

#=============================================================================
# print_cab
#
# Print a summary of a CAB archive.
#=============================================================================

print_cab()
{
    find_filter cabextract || fatal_filter cabextract
    cat > "$APS_TMPDIR/cab"
    cabextract -l "$APS_TMPDIR/cab" | print_ascii
    rm -f "$APS_TMPDIR/cab"
}

#=============================================================================
# print_cpio
#
# Print a summary of a CPIO archive.
#=============================================================================

print_cpio()
{
    find_filter cpio || fatal_filter cpio
    cpio -tv --quiet | print_ascii
}

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

print_data()
{
    local c

    print_copy()
    {
	local rc=0 ncp_print

	if [ "$APS2FILE_CONTEXT" ]; then
	    # we were called by aps2file -> output to stdout
	    cat
	elif [ -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'" \
		${SMB_IP:+-I"$SMB_IP"} ${SMB_USER:+-U"$SMB_USER"} \
		${SMB_WORKGROUP:+-W"$SMB_WORKGROUP"} -b"${SMB_BUFFER:-1400}" \
		${SMB_FLAGS:--N} -c "'print -'"
	    rc=$?
	elif [ -f "$CONF_DIR/$QUEUE/netware.conf" ]; then
	    . "$CONF_DIR/$QUEUE/netware.conf"
	    if find_filter ncprint; then
		# BSD systems have "ncprint"
		ncp_print=ncprint
		# create temporary .nwfsrc file (for security)
		{
		    echo "[${NCP_SERVER}:${NCP_USER}]"
		    echo "password=$NCP_PASSWD"
		} > "$APS_TMPDIR/.nwfsrc"
		chmod 600 "$APS_TMPDIR/.nwfsrc"
	    else
		find_filter nprint || fatal_filter ncprint/nprint
		# Linux systems have "nprint"
		ncp_print=nprint
		# create temporary .nwclient file (for security)
		echo "$NCP_SERVER/$NCP_USER ${NCP_PASSWD:--}" \
		    > "$APS_TMPDIR/.nwclient"
		chmod 600 "$APS_TMPDIR/.nwclient"
	    fi
	    # fake $HOME to make .nwfsrc and .nwclient visible
	    HOME="$APS_TMPDIR" $ncp_print -S "$NCP_SERVER" -U "$NCP_USER" \
		-q "$NCP_PRINTER" -c $COPIES
	    rc=$?
	elif [ -f "$CONF_DIR/$QUEUE/lpr.conf" ]; then
	    . "$CONF_DIR/$QUEUE/lpr.conf"
	    lpr -P"$REMOTE_NAME" -\#"$COPIES"
	    rc=$?
	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"
	    rc=$?
	else
	    cat
	fi
	if [ $rc -ne 0 ]; then
	    echo 2 >"$APS_TMPDIR/rc"; exit
	fi
    }

    if [ "$SAVE_DUPLEX_COPY" ]; then
	# print_ps_duplex() does the manual copying
	# we append here to include the (optional) blank page
	tee -a "$APS_TMPDIR/copy-$SAVE_DUPLEX_COPY" | print_copy
    else
	if [ "$COPIES" = 1 -o -n "$REMOTE_COPIES" ]; 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]*}"
	[ "$resolution_x" -gt 400 ] && options="$options -Z"
	[ "$LANDSCAPE" ] && options="$options -t landscape"
	PSNUP_ROTATE="-r"
	: ${DVIPS_PAPERSIZE:=$PAPERSIZE}

	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
    cat > "$APS_TMPDIR/fbm"
    do_nconvert -in fbm < "$APS_TMPDIR/fbm"
    rm -f "$APS_TMPDIR/fbm"
}

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

print_fig()
{
    if find_filter fig2dev; then
	fig2dev -Lps -c -z "$PAPERSIZE" | print_ps
    else
	find_filter sk2ps || fatal_filter sk2ps
	sk2ps /dev/stdin | print_ps
    fi
}

#=============================================================================
# print_fits
#
# Print FITS images.
#
# Note: Should 3-axes-files be handled as three grayscale images (convert) or
#	as RGB input, resulting in one color image (nconvert, fitstopnm)?
#=============================================================================

print_fits()
{
    if find_filter nconvert; then
	cat > "$APS_TMPDIR/fits"
	do_nconvert -in fits < "$APS_TMPDIR/fits"
	rm -f "$APS_TMPDIR/fits"
    elif find_filter fitstopnm; then
	# fitstopnm output "may" need to be flipped top for bottom, according
	# to the man page -- you gotta be kidding...
	fitstopnm | print_pnm
    else
	find_filter convert || fatal_filter convert
	do_convert fits
    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_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_html
#
# Print HTML files.
#=============================================================================

print_html()
{
    local opts paper

    if find_filter htmldoc; then
	[ "$COLOR" = full ] && opts="--color" || opts="--gray"
	eval htmldoc ${HTMLDOC_OPTS:---webpage} $opts --format ps \
	    ${LANDSCAPE:+--landscape} ${PORTRAIT:+--portrait} \
	    --size ${WIDTH_POINTS}x${HEIGHT_POINTS}pt - | print_ps
    else
	find_filter html2ps || fatal_filter html2ps
	if ! find_filter gs; then
	    echo >&2 "apsfilter warning: html2ps needs gs for DSC compliance"
	fi

	[ "$COLOR" = full ] && opts="-U" || opts="-g"
	[ "$PAPERSIZE" = tabloid ] && paper=11x17 || paper="$PAPERSIZE"
	eval html2ps ${HTML2PS_OPTS:--e ISO-8859-1 -u -H} $opts \
	    ${LANDSCAPE:+-L} -D -S '"paper { type: $paper; }"' | print_ps
    fi
}

#=============================================================================
# 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 [ "$COLOR" = full ]; then
	    djpeg | print_pnm
	else
	    djpeg -grayscale | print_pgm
	fi
    else
	find_filter jpegtopnm || fatal_filter jpegtopnm
	jpegtopnm | print_pnm
    fi
}

#=============================================================================
# print_lha
#
# Print a summary of a LHa(rc) archive.
#=============================================================================

print_lha()
{
    find_filter lha || fatal_filter lha
    lha v - | print_ascii
}

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

print_mgr()
{
    if find_filter mgrtopbm; then
	mgrtopbm | print_pnm
    else
	find_filter nconvert || fatal_filter nconvert
	cat > "$APS_TMPDIR/mgr"
	do_nconvert -in mgr < "$APS_TMPDIR/mgr"
	rm -f "$APS_TMPDIR/mgr"
    fi
}

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

print_miff()
{
    if find_filter convert; then
	# convert doesn't seem to like an explicit "miff" here, at least for
	# images with an alpha (matte) channel -- weird
	do_convert
    else
	find_filter nconvert || fatal_filter nconvert
	do_nconvert -in miff
    fi
}

#=============================================================================
# print_pam
#
# Print PAM (xv thumbnail) images.
#=============================================================================

print_pam()
{
    if find_filter convert; then
	do_convert p7
    elif find_filter xvminitoppm; then
	xvminitoppm | print_pnm
    elif find_filter xvpictoppm; then
	xvpictoppm | print_pnm
    else
	find_filter nconvert || fatal_filter nconvert
	cat > "$APS_TMPDIR/pam"
	do_nconvert -in p7 < "$APS_TMPDIR/pam"
	rm -f "$APS_TMPDIR/pam"
    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 [ "$COLOR" = full ]; then
	    pcdtoppm "$APS_TMPDIR/pcd" - | print_pnm
	else
	    pcdtoppm --gray "$APS_TMPDIR/pcd" - | print_pgm
	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()
{
    local paper

    ACROREAD_OPTS="-toPostScript -size ${WIDTH_POINTS}x${HEIGHT_POINTS} \
		${ACROREAD_OPTS:--level2 -fast}"

    cat > "$APS_TMPDIR/pdf"
    if find_filter acroread5; then
	eval acroread5 $ACROREAD_OPTS -pairs "$APS_TMPDIR/pdf" /dev/stdout
    elif 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
	[ "$PAPERSIZE" = tabloid ] && paper=11x17 || paper="$PAPERSIZE"
	pdf2ps -sPAPERSIZE="$paper" "$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 pnmdepth || fatal_filter pnmdepth
	find_filter pnmtops || fatal_filter pnmtops
	pnmdepth 255 | 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 [ "$COLOR" = full ]; then
	    find_filter pnmdepth || fatal_filter pnmdepth
	    find_filter pnmtops || fatal_filter pnmtops
	    pnmdepth 255 | pnmtops -width $WIDTH_INCHES \
		-height $HEIGHT_INCHES 2> /dev/null | print_ps
	else
	    find_filter ppmtopgm || fatal_filter ppmtopgm
	    ppmtopgm | print_pgm
	fi
    fi
}

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

print_ps()
{
    local paper gs_cmd

    if [ "$DUPLEX" ] && [ "$PRINTER" != PS -o -z "$HARDWARE_DUPLEX" ]; then
	unset DUPLEX
	print_ps_duplex
	return
    elif [ "$PRINTER" = PS ]; then
	ps_postprocessing | print_data
	return
    fi

    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

    [ "$PAPERSIZE" = tabloid ] && paper=11x17 || paper="$PAPERSIZE"
    case "$PRINTER" in
	*.upp)
	    gs_cmd="gs -q -dNOPAUSE -dSAFER -dPARANOIDSAFER -sstdout=%stderr \
		@'$PRINTER' -sPAPERSIZE='$paper' -sOutputFile=- \
		${PS_INIT:+'$PS_INIT'} - ${PS_EXIT:+'$PS_EXIT'}" ;;
	*)
	    gs_cmd="gs -q -dNOPAUSE -dSAFER -dPARANOIDSAFER -sstdout=%stderr \
		-sDEVICE='$PRINTER' -sPAPERSIZE='$paper' \
		${RESOLUTION:+-r'$RESOLUTION'} -sOutputFile=- $GS_FEATURES \
		${PS_INIT:+'$PS_INIT'} - ${PS_EXIT:+'$PS_EXIT'}" ;;
    esac
    if [ "$POST_FILTER" ]; then
	find_filter "$POST_FILTER" || fatal_filter "$POST_FILTER"
	gs_cmd="$gs_cmd | '$POST_FILTER' $POST_FILTER_OPTS"
    fi
    ps_postprocessing | eval $gs_cmd | print_data
}

print_ps_duplex()
{
    local copies c is_odd

    print_odd_pages()
    {
	[ "$copies" != 1 ] && SAVE_DUPLEX_COPY=odd
	psselect -q -o ${DUPLEX_REVERSE_ODD:+-r} < "$APS_TMPDIR/duplex.ps" | \
	    print_ps
    }
    print_even_pages()
    {
	[ "$copies" != 1 ] && SAVE_DUPLEX_COPY=even
	[ -n "$is_odd" -a -n "$DUPLEX_REVERSE_EVEN" ] && \
	    printf "$BLANK_PAGE" | print_data
	psselect -q -e ${DUPLEX_REVERSE_EVEN:+-r} < "$APS_TMPDIR/duplex.ps" | \
	    print_ps
	[ -n "$is_odd" -a -z "$DUPLEX_REVERSE_EVEN" ] && \
	    printf "$BLANK_PAGE" | print_data
    }

    # 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
	find_filter psselect || fatal_filter psselect

	# make page count even by adding a blank page if needed
	case $(ps_postprocessing | tee "$APS_TMPDIR/duplex.ps" | \
		psselect -p_1 2>&1 >/dev/null | tr "[]" "0\n" | head -1) in
	    01)		# don't bother to print 1 page in duplex mode
			print_ps < "$APS_TMPDIR/duplex.ps"
			rm -f "$APS_TMPDIR/duplex.ps" "$APS_TMPDIR/duplex-key"
			return ;;
	    *[13579])	is_odd=set ;;
	    *)		unset is_odd ;;
	esac

	# don't process PostScript with the pstools again
	SKIP_POSTPROCESSING=set

	copies="$COPIES"; COPIES=1
	if [ "$DUPLEX_ODD_FIRST" ]; then
	    print_odd_pages;  duplex_notification; print_even_pages
	else
	    print_even_pages; duplex_notification; print_odd_pages
	fi
	rm -f "$APS_TMPDIR/duplex.ps"

	# turn on the copy machine!
	if [ "$copies" != 1 ]; then
	    unset SAVE_DUPLEX_COPY
	    if [ "$DUPLEX_ODD_FIRST" ]; then
		for c in $(seq 2 "$copies"); do
		    print_data < "$APS_TMPDIR/copy-odd"
		    duplex_notification
		    print_data < "$APS_TMPDIR/copy-even"
		done
	    else
		for c in $(seq 2 "$copies"); do
		    print_data < "$APS_TMPDIR/copy-even"
		    duplex_notification
		    print_data < "$APS_TMPDIR/copy-odd"
		done
	    fi
	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 ${PS_BOOK:+psbook -q |} \
	    ${PS_NUP:+psnup -q -p$PAPERSIZE -$PS_NUP ${LANDSCAPE:+$PSNUP_ROTATE} |} \
	    ps_set_duplex ${PS_UTILS:+| $PS_UTILS}
    fi
}

ps_set_duplex()
{
    local tray

    find_filter psset || fatal_filter psset

    [ "$PAPERTRAY" ] && tray="-Spapertray:$PAPERTRAY"

    if [ "$DUPLEX" ]; then
	if [ "$BINDING" = short ]; then
	    psset -n -q -t $tray
	else
	    psset -n -q -d $tray
	fi
    else
	psset -n -q -s $tray
    fi
}

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=`"$AWK" 'BEGIN { srand(); for (i=0; i<8; i++) \
	printf "%02x", int(256*rand()) }'`
    {
	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 "$QUEUE".
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
    } | "/usr/sbin/sendmail" -oem -t

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

#=============================================================================
# print_rar
#
# Print a summary of a RAR archive.
#=============================================================================

print_rar()
{
    cat > "$APS_TMPDIR/rar"
    if find_filter unrar; then
	unrar v "$APS_TMPDIR/rar" | print_ascii
    else
	find_filter rar || fatal_filter rar
	rar v "$APS_TMPDIR/rar" | print_ascii
    fi
    rm -f "$APS_TMPDIR/rar"
}

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

print_ras()
{
    local opts

    if find_filter convert; then
	do_convert ras
    elif find_filter ras2ps; then
	[ "$COLOR" = full ] && opts="-C"
	ras2ps $opts | 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_rpm
#
# Print a summary of an RPM package.
#=============================================================================

print_rpm()
{
    local queryformat

    find_filter rpm || fatal_filter rpm

    queryformat="Name        : %-27{NAME} Distribution: %{DISTRIBUTION}
Version     : %-27{VERSION}       Vendor: %{VENDOR}
Release     : %-27{RELEASE}   Build Date: %{BUILDTIME:date}
Install date: %-27{INSTALLTIME:date}   Build Host: %{BUILDHOST}
Group       : %-27{GROUP}   Source RPM: %{SOURCERPM}
Size        : %{SIZE}
Summary     : %{SUMMARY}
Description :\n%{DESCRIPTION}

Files:
[        %{FILENAMES}\n]\

Requires:
[        %{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]\

Provides:
[        %{PROVIDES}\n]"

    # this silly contruction avoids a "broken pipe" error
    { rpm --qf "$queryformat" -qp -- -; cat > /dev/null; } | print_ascii
}

#=============================================================================
# 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_tar
#
# Print a summary of a TAR archive.
#=============================================================================

print_tar()
{
    find_filter tar || fatal_filter tar
    tar tvf - | print_ascii
}

#=============================================================================
# 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_tgif
#
# Print TGIF drawings.
#=============================================================================

print_tgif()
{
    local opts

    find_filter tgif || fatal_filter tgif
    cat > "$APS_TMPDIR/tgif.obj"
    [ "$COLOR" = full ] && opts="-color" || opts="-gray"
    tgif -print -adobe -ps -stdout $opts "$APS_TMPDIR/tgif.obj" 2> /dev/null \
	| print_ps
    rm -f "$APS_TMPDIR/tgif.obj"
}

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

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

#=============================================================================
# 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_wmf
#
# Print Windows Metafiles.
#=============================================================================

print_wmf()
{
    cat > "$APS_TMPDIR/wmf"
    if find_filter wmf2eps; then
	wmf2eps --ps ${LANDSCAPE:+--landscape} ${PORTRAIT:+--portrait} \
	    --centre "$APS_TMPDIR/wmf" | print_ps
    else
	find_filter sk2ps || fatal_filter sk2ps
	sk2ps "$APS_TMPDIR/wmf" | print_ps
    fi
    rm -f "$APS_TMPDIR/wmf"
}

#=============================================================================
# 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
}

#=============================================================================
# print_xwd
#
# Print X window dump files.
#=============================================================================

print_xwd()
{
    if find_filter convert; then
	do_convert xwd
    elif find_filter nconvert; then
	do_nconvert -in xwd
    else
	find_filter xwdtopnm || fatal_filter xwdtopnm
	xwdtopnm | print_pnm
    fi
}

#=============================================================================
# print_zip
#
# Print a summary of a ZIP archive.
#=============================================================================

print_zip()
{
    find_filter unzip || fatal_filter unzip
    cat > "$APS_TMPDIR/zip"
    unzip -v "$APS_TMPDIR/zip" | print_ascii
    rm -f "$APS_TMPDIR/zip"
}

#=============================================================================
# print_zoo
#
# Print a summary of a Zoo archive.
#=============================================================================

print_zoo()
{
    find_filter zoo || fatal_filter zoo
    cat > "$APS_TMPDIR/zoo"
    zoo l "$APS_TMPDIR/zoo" | print_ascii
    rm -f "$APS_TMPDIR/zoo"
}

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

unpack()
{
    local HEADER FILE_TYPE unpacker unpacker_opts

    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=bzip2 ; unpacker_opts=-d	;;
	gzip*|packed*|old\ packed*)
	    unpacker=gzip  ; unpacker_opts=-d	;;
	compress*)
	    unpacker=zcat  ; unpacker_opts=	;;
	frozen*)
	    unpacker=fcat  ; unpacker_opts=	;;
	lzop*)
	    unpacker=lzop  ; unpacker_opts=-d	;;
	empty)
	    rm -f "$HEADER"; return ;;
    esac
    if [ "$unpacker" ]; then
	find_filter $unpacker || fatal_unpacker $unpacker
	cat "$HEADER" - | $unpacker $unpacker_opts | 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)      LPD_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"

# aps2file runs in userland, so we can safely use user-specific configuration
[ "$APS2FILE_CONTEXT" ] && USE_USER_CODE=set

[ -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}
find_filter mktemp || fatal_tmpdir
APS_TMPDIR=`mktemp -q -d "$TMPDIR/apsfilter.XXXXXX"` || fatal_tmpdir
chmod 711 "$APS_TMPDIR"
echo 0 >"$APS_TMPDIR/rc"

# 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"

# default value for AWK (you can change it in apsfilterrc)
: ${AWK:=/usr/bin/awk}

# various quality setting defaults
: ${QUALITY:=medium} ${SWEEP:=bi} ${MEDIA:=plain} ${COLOR:=full}

# if raw printing was requested on the lpr command line ("-l" or "-b" switch),
# it has higher priority than the value in apsfilterrc, but can be set to be
# ignored anyway (to please buggy remote spoolers)
[ -n "$LPD_METHOD" -a -z "$IGNORE_LPD_RAW" ] && METHOD=raw

#-----------------------------------------------------------------------------
# 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"
    : ${JOB:=$FNAME}

    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

#-----------------------------------------------------------------------------
# Parse the command line options (via -C, -Z and -o).
#-----------------------------------------------------------------------------

if [ "$CLASS$Z_OPTS" ]; then
  old_ifs="$IFS"
  IFS=:,
  set -- $CLASS $Z_OPTS
  IFS="$old_ifs"
  unset old_ifs

  for option; do
    case "$option" in
      lo|low)				# printing quality
        QUALITY=low ;;
      hi|high)
	QUALITY=high ;;
      med|medium)
	QUALITY=medium ;;
      draft|photo)
        QUALITY=$option ;;

      uni|bi)				# uni/bi-directional printing
	SWEEP=$option ;;

      plain|coated|glossy|premium|trans)
	MEDIA=$option ;;		# paper type

      color|colour)			# color printing
	COLOR=full ;;
      gray|grey)
	COLOR=gray ;;
      mono)
	COLOR=mono ;;

      a3|a4|legal|letter|ledger|tabloid)
	PAPERSIZE=$option ;;		# paper size
      ascii|auto|raw)			# force printing method
	METHOD=$option ;;
      sound)
	METHOD=$option ;;		# play sound
      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} ;;
      pretty=[012])			# pretty-printing mode
	PRETTY_PRINTING=${option#pretty=} ;;
      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 ;;
      copies=*)				# no of copies
        COPIES=${option#copies=} ;;

      [A-Z])				# class specification
	;;
      *)				# unknown option
	if [ "$option" != "`uname -n`" ]; then
	    echo >&2 "apsfilter warning: unknown option '$option'"
	fi
	;;
    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 :)

[ "$LANDSCAPE" ] && PORTRAIT= || LANDSCAPE= PORTRAIT=set
[ "$BINDING" = short ] || BINDING=long
[ "$DISABLE_DUPLEX" ] && unset DUPLEX
: ${ASCII_LANDSCAPE=$LANDSCAPE} ${ASCII_PPS=$PS_NUP} ${BLANK_PAGE:='\012\014'}
: ${PRETTY_PRINTING:=1}

case "$PS_NUP" in
    2|4|8)	;;
    *)		unset PS_NUP ;;
esac
PSNUP_ROTATE="-l"

# if the admin decides to restrict certain options for the printer(s),
# it will be done in the following scripts
[ -e "$CONF_DIR/restrictions" ] && . "$CONF_DIR/restrictions"
[ -e "$CONF_DIR/$QUEUE/restrictions" ] && . "$CONF_DIR/$QUEUE/restrictions"


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    ;;
    tabloid)	WIDTH_POINTS=792;   HEIGHT_POINTS=1224
		WIDTH_INCHES=11;    HEIGHT_INCHES=17    ;;
    *)		# use safe default values (small enough)
		WIDTH_POINTS=595;   HEIGHT_POINTS=792
		WIDTH_INCHES=8.26;  HEIGHT_INCHES=11    ;;
esac


# if we print to a NetWare or remote lpd printer, rather let them do copies
# (for lower network impact); otherwise we will do it locally
if [ "${REMOTE_COPIES=set}" ]; then
    if [ -f "$CONF_DIR/$QUEUE/netware.conf" -o \
	 -f "$CONF_DIR/$QUEUE/lpr.conf" ]; then
	unset HARDWARE_COPIES
    else
	unset REMOTE_COPIES
    fi
fi

# now we call the driver script which assigns proper values to RESOLUTION
# and GS_FEATURES, hopefully :)

case "$PRINTER" in
    *.upp)
	# uniprint profile -- everything is hard-coded
	;;

    *)
	DRIVER_SCRIPT="$PRINTER"
	if [ ! -e "$CONF_DIR/basedir/driver/$DRIVER_SCRIPT" ]; then
	    # get the script name from the mapping file
	    DRIVER_SCRIPT=$(grep ":$PRINTER:" \
		"$CONF_DIR/basedir/driver/MAPPING" | sed "s/:.*//")
	fi
	if [ -z "$DRIVER_SCRIPT" ]; then
	    echo >&2 "apsfilter: driver script '$PRINTER' not yet available"
	elif [ -e "$CONF_DIR/basedir/driver/$DRIVER_SCRIPT" ]; then
	    . "$CONF_DIR/basedir/driver/$DRIVER_SCRIPT"
	else
	    fatal_driver_script
	fi

	# correct some printer/model settings
	case "$PRINTER" in
	    omni/*|hpijs/*)
		GS_FEATURES="$GS_FEATURES -sDeviceName=${PRINTER#*/}"
		PRINTER=${PRINTER%%/*} ;;
	    stp/*|hpdj/*)
		GS_FEATURES="$GS_FEATURES -sModel=${PRINTER#*/}"
		PRINTER=${PRINTER%%/*} ;;
	    pcl3/*)
		GS_FEATURES="$GS_FEATURES -sSubdevice=${PRINTER#pcl3/}"
		PRINTER=pcl3 ;;
	    gimp/*)
		GS_FEATURES="$GS_FEATURES -sDeviceModel='${PRINTER#gimp/}' \
		    -sIjsServer=ijsgimpprint -dIjsUseOutputFD"
		PRINTER=ijs ;;
	    ijs/*)
		GS_FEATURES="$GS_FEATURES -sDeviceModel='${PRINTER#ijs/}' \
		    -sIjsServer=hpijs -dIjsUseOutputFD"
		PRINTER=ijs ;;
	esac
	;;
esac

# if the driver does copying/duplex by itself, we won't do it anymore
# (hopefully the correct options have been added in the printer script)
if [ "$PRINTER" != PS ]; then
    [ "$HARDWARE_COPIES" ] && COPIES=1
    [ "$HARDWARE_DUPLEX" ] && unset DUPLEX
fi

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

case "$METHOD" in
    auto)  unpack print_auto	;; 
    ascii) unpack print_ascii	;;
    raw)   print_raw		;;
    sound) unpack play_sound	;; # when using dedicated print queue for sound
    *)     fatal_method		;;
esac
