#!/bin/bash
#
# /usr/lib/lpdfilter/bin/if
#
# main script which should be called with the same
# name as the lpd queue.
#
# Copyright 2000 SuSE GmbH, Nuernberg Germany
#
# Author: Werner Fink <feedback@suse.de>
#

#
# For debugging
# activate the apropriate debug level
DEBUG="off"
# DEBUG="low"
# DEBUG="medium"
# DEBUG="high"
declare -xr DEBUG

#
# Activate debugging if requested.
#
if test -n "$DEBUG" -a "$DEBUG" != "off" ; then
    # bash should be verbose
    set -x
    # Redirect stderr to a file
    exec 2> $(mktemp /tmp/lpdfilter.if-$$.XXXXXX) || exit 2

    # Redirect output to a file
    if test "$DEBUG" = "high" ; then
	exec 1> $(mktemp /tmp/lpdfilter.out-$$.XXXXXX) || exit 2
    fi
fi

#
# Global variabels and settings used by us
#
declare -r  OIFS="$IFS"
declare -i  n=0
declare -r  key="$$\$$RANDOM"
export pid=$$
umask 077

#
# Remove buildin printf and use the real one.
#
enable -n printf &> /dev/null

#
# Get our environment
#
. /usr/lib/lpdfilter/global/signals
. /usr/lib/lpdfilter/global/functions
type -p readpc   &> /dev/null || export PATH=/usr/lib/lpdfilter/bin:${PATH}
type -p xpmtoppm &> /dev/null || export PATH=${PATH}:/usr/X11R6/bin

#
# These signals are send by our sub processes if something fails.
#
trap "echo $0 Internal error 1>&2; exit 1" SIGUSR1
trap "echo $0 Internal error 1>&2; exit 2" SIGUSR2

#
# If we're reading from a pipe (FIFO) we exit here because
# we can not do this.
#
test -f /dev/fd/0 || fatal_error

#
# Read options give by the lpd (BSD, plp, LPRng)
# First strip spaces between options and their argument
# then we scan most of the options.
#
opt=;arg=
while test "${1::1}" = "-" ; do
    if test ${#1} -gt 2 -o "${2::1}" = "-" ; then
	opt="$1"  ; shift
    else
	opt="$1$2"; shift 2
    fi
    arg="${opt:2}"
    opt="${opt:1:1}"
    arg="${arg%% }"
    set -a
    case "$opt" in
	P)  :    ${queue="$arg"} ;; # Used if set
	w)  :    ${width="$arg"} ;; # Ignored
	l)  :   ${length="$arg"} ;; # Ignored
	i)  :   ${indent="$arg"} ;; # Ignored
	x)  :    ${xdots="$arg"} ;; # Ignored
	y)  :    ${ydots="$arg"} ;; # Ignored
	c)  :  ${literal="true"} ;; # Ignored
	N)  :     ${file="$arg"} ;; # Used if set
	S)  :  ${comment="$arg"} ;; # our options in printcap
	Y)  :    ${calls="$arg"} ;; # Ignored
	J)  :      ${job="$arg"} ;; # Used if set
	Z)  : ${zoptions="$arg"} ;; # Used if set
	C)  : ${priority="$arg"} ;; # Ignored
	R)  : ${raccount="$arg"} ;; # Ignored
	L)  :     ${name="$arg"} ;; # Used if set
	n)  :     ${user="$arg"} ;; # Used if set
	H|h):     ${host="$arg"} ;; # Used if set
	F)  :   ${filter="$arg"} ;; # Used if set
	k)  :  ${control="$arg"} ;; # Used if set
	a)  :       ${af="$arg"} ;; # Used if set
	e)  :     ${data="$arg"} ;; # Data at fd0
	?)			 ;; # Ignored
    esac
    set +a
done
unset opt arg

set -a
# We got the accounting file at the end of all options
test $# -eq 1 -a -e "$1" && \
: ${af=$1}
: ${sd=${af%/*}}
: ${queue=${sd##*/}}
# We need the the `cf' (control file)
: ${control="$(tail -1 ${sd}/lock)"}
# We need the file name of this run
: ${data=$(readlink /dev/fd/0)}
set +a

#
# Are we running under LPRng?
# PLP is here a BSDlpr.
#
case "$control" in
    *control.*) daemon="LPRng"  ;;
    *)		daemon="BSDlpr" ;;
esac

#
# Wait on our control/hold file (LPRng).
#
hold="${control%${host}}"
hold="h${hold#c}"
n=0
while test ! -s $control ; do
    sleep 1
    test -s $hold && break
    test $((n=n+1)) -gt 10 && break
done
if test ! -s $control ; then
    if test -s $hold ; then
	control="$hold"
	daemon="LPRng"
    else
	control=
    fi
fi

#
# Use the specific printcap (Currently no NIS is supported)
#
printcap="/etc/printcap"
case "$daemon" in
    LPRng)
	printcap=$(grep -a -E '^printcap_path=' /etc/lpd.conf 2> /dev/null)
	printcap=${printcap#*=}
	test -s "$printcap" || printcap="/etc/printcap"
esac

#
# Now scan the cf/hf, check for the file name of the current run.
#
opt=;arg=
while read line ; do
    case "$control" in
	hf*)
	    case "$line" in
		*=*)
		    arg=${line%%=*}
		    opt=${line#*=}
		    ;;
		*)
		    arg="${line:1}"
		    opt="${line::1}"
		    ;;
	    esac
	    ;;
	*)
	    arg="${line:1}"
	    opt="${line::1}"
	    ;;
    esac
    arg="${arg%% }"
    set -a
    case "$opt" in
	J) :      ${job="$arg"} ;; # Used if set
	C) :    ${class="$arg"} ;; # Ignored
	D) :   ${unkown="$arg"} ;; # Ignored
	L) :     ${name="$arg"} ;; # Used if set
	T) :    ${title="$arg"} ;; # Used if set
	H) :     ${host="$arg"} ;; # Used if set
	P) :     ${user="$arg"} ;; # Used if set
	M) :   ${mailto="$arg"} ;; # Used if set
	Z) : ${zoptions="$arg"} ;; # Used if set
	f|l|p|t|d|n|g|c|v|r|1|2|3|4) ;; # Various file formats (ignored)
	W) :    ${width="$arg"} ;; # Ignored
	I) :   ${indent="$arg"} ;; # Ignored
	U) :  ${spooled="$arg"} ;; # Ignored
	N)  test "$arg" -ef $data && \
		   file="$arg"  ;; # Overwrite (plp)
	S) :   ${inodes="$arg"} ;; # Ignored
	P) :    ${queue="$arg"} ;; # Used if set
	?)			;; # Ignored
    esac
    set +a
done < ${control:-/dev/null}
unset line opt arg

#
# Read printcap entry and get all options
#
entry="$(readpc "$queue" "$printcap" 2> /dev/null)"
test $? -eq 0 || fatal_error "Error: can not print"
n=0
IFS=":"
for e in $entry ; do
    if test $((n=n+1)) -eq 1 ; then
	continue
    fi
    # Expand the printcap values of the printcap entry
    case "$e" in
	# Expand our option entry which is the comment
	# field within the first line of the printcap entry
	cm=lpdfilter*)	eval export "${e#cm=lpdfilter}"	;;
	lp=*%*)		e=${e#lp=}
			eval export port=${e#*\%}
			eval export rm=${e%\%*}		;;
	lp=*@*)		e=${e#lp=}
			eval export rm=${e#*@}
			eval export rp=${e%@*}		;;
	*=*)		eval export ${e}		;;
	*\#*)		eval export ${e%\#*}=${e#*\#}	;;
	*@)		eval export ${e%@}=no		;;
	*)		eval export ${e}=yes		;;
    esac
done
unset e entry
IFS="$OIFS"

#
# Suppress form feed between multiple jobs?
#
case "$daemon" in
    LPRng)	test -z "$sf" && sf=yes ;;
    BSDlpr|PLP) test -z "$sf" && sf=no  ;;
esac

#
# Set by hand if not found
#
set -a
: ${host="$(hostname -f)"}
: ${date="$(date +'%Y-%m-%d %T')"}
: ${title="$file"}
: ${home=$(getpwhome $user)}
: ${name=$(getpwgecos $user)}
: ${mailto="$user"}
set +a

#
# All options could be overwritten by using a
# seperate configuration file
#
set -a
test -r /etc/lpdfilter/${queue}/conf && . /etc/lpdfilter/${queue}/conf
set +a

#
# If sh is set (suppress header) then lpd does not use
# headers, nevertheless lpdfilter may use a banner page.
# Therefore we overwrite `sh' if `banner' is set.
#
: ${banner=no}
if test "${banner+set}" = "set" -a "$banner" != "no" ; then
    sh=no
fi

#
# Handle Z option of LPRng and PLP
#
shopt -s extglob
number='+([0-9])?(.+([0-9]))'
gvscan="${number}?(,${number}?(,${number}?(,${number})))"
if test -n "$zoptions" ; then
    IFS=":;"
    for z in $zoptions ; do
	set -a
	case "$z" in
	    duplex=off)	    duplex=off		;;
	    duplex=on)      duplex=on		;;
	    duplex=tumble)  duplex=tumble	;;
	    tray=[0-9])     tray=${z#tray=}	;;
	    gamma=$gvscan)  gamma=${z#gamma=}	;;
	    feature=1)      feature=1		;;
	    feature=2)      feature=2		;;
	    feature=1n)     feature=1n		;;
	    feature=2n)     feature=2n		;;
	    feature=1l)     feature=1l		;;
	    feature=1ln)    feature=1ln		;;
	    *)					;; # Ignore
	esac
	set +a
    done
    unset z
    IFS="$OIFS"
fi
unset number gvscan
shopt -u extglob

#
# Which language is preferred for printing?
#
if test -z "$encoding" ; then
    # For `get_encoding' see /usr/lib/lpdfilter/global/functions
    export encoding=$(get_encoding)
fi

#
# Systemwide source path expansion for TeX/LaTeX
#
if test -n "$texinputs" -a -n "${texinputs#*:}" ; then
    export TEXINPUTS=${texinputs}:
fi

#
# Get the requested method
#
case "${method}" in
    raw)   eval "$(guess)"    ;;
    auto)  eval "$(guess)"    ;;
    *)     eval "$(guess -r)"
	   type=${method}     ;;
esac
export unpack type creator options cat

#
# Test for a bypass to an other queue if output is /dev/null
#
if test /dev/fd/1 -ef /dev/null ; then
    if   test -n "$redirect" ; then
	if test -x "$redirect" ; then
	    exec 1> >(exec -a "${redirect##*/}" $redirect)
	elif readpc "$redirect" "$printcap" &> /dev/null ; then
	    redirectopts="-l -h -P$redirect ${job:+"-J $job"} ${title:+"-T $title"}"
	    runlpr=lpr
	    if test "$(id -un)" = "root" -o "$(id -un)" = "lp" ; then
		# Only root or lp can run `runlpr'
		runlpr="runlpr"
		redirectopts="-U$user $redirectopts"
	    fi
	    exec 1> >(exec -a lpr $runlpr $redirectopts)
	fi
    elif test -x /etc/lpdfilter/${queue}/redirect ; then
	if link=$(readlink /etc/lpdfilter/${queue}/redirect) ; then
	    exec 1> >(exec -a "${link##*/}" $link)
	    unset link
	else
	    exec 1> >(exec -a "redirect" /etc/lpdfilter/${queue}/redirect)
	fi
    fi
fi

#
# Handling of file descriptors
#  0 is input
#  1 is for output
#  2 is for error messages
#  3 is for accounting or this bash script
# We remap the following file descriptors
#  4 is for output
#  5 may later set for gs
#  1 is for error messages too
#
exec 4>&1 1>&2

#
# Standard input filter on fd 0 for uncompressing if any.
#
if test -n "$unpack" ; then
    exec 0< <(exec -a "${unpack%% *}" $unpack)
fi

#
# This is the raw channel
#
if test "$type" = "raw" -o "$method" = "raw" ; then
    (
	test -n "$raw_setup"	&& printf "$raw_setup"
	#
	# Use recode here?
	#
	if test -n "$outcode" -a "$type" = "ascii" ; then
	    # Real ascii on the raw channel
	    recode -q ${encoding:-latin1}..${outcode:-ibmpc}
	else
	    cat -
	fi
	test -n "$raw_reset"	&& printf "$raw_reset"
	test    "$sf" = yes	|| printf "\014"

    ) | cat 1>&4
    test $? -eq 0 || fatal_error "Error: can not print in raw mode."
    test "$cl" = "yes" && exec 4>&-
    exit 0
fi

#
# If driver is not PS we use ghostscript
#
if test "$drv" != "PS" ; then
    if test "$drv" = "upp" ; then
	test -s /etc/lpdfilter/${queue}/upp || fault_job
	upp=""
	while read line ; do
	    case "$line" in
		-sDEVICE=PS)	drv=PS		  ;; # Overwrite driver!
		-r*)		dpi="${line#-r}"  ;;
		-dDEVICEXRESOLUTION=*)
			       xres="${line#*=}"  ;;
		-dDEVICEYRESOLUTION=*)
			       yres="${line#*=}"  ;;
		-sPAPERSIZE=*) size="${line#*=}"  ;;
		-sCOLOR=*)    color="${line#*=}"  ;;
		-sPOSTFILTER=\"*\")
			  eval post="${line#*=}"  ;;
		@*)		upp="${line#@}"   ;;
		*)
	    esac
	done < /etc/lpdfilter/${queue}/upp
	if test -n "$upp" ; then
	    upp=$(echo "($upp) findlibfile { pop print } { pop } ifelse" | \
		  gs -sDEVICE=nullpage -q -dNOPAUSE - -c quit)
	fi
	if test -n "$upp" ; then
	    while read line ; do
		case "$line" in
		    -r*)	dpi="${line#-r}"  ;;
		    *)
		esac
	    done < $upp
	fi
	if test -z "$dpi" ; then
	    test -n "$xres" && dpi=$xres
	    test -n "$yres" && dpi=${dpi+"${dpi}x"}$yres
	fi
    fi
    if test -n "$af" -a "$la" != "yes" -a -w "$af" ; then
	exec 1> >(accsearch)
    fi
    # Do not add `-dSAFER' here but before reading
    # the users PostScript. Note GS_AFTER is used to overwrite
    # the <size>dj in upp case.
    GS_OPTIONS="-q -dQUIET -dNOPAUSE -dBATCH -dSHORTERRORS"
    case "$size" in
	tabloid|11x17)	GS_OPTIONS="$GS_OPTIONS -sPAPERSIZE=11x17" ;  GS_AFTER="-sPAPERSIZE=11x17" ;;
	a4dj)		GS_OPTIONS="$GS_OPTIONS -sPAPERSIZE=a4"    ;  GS_AFTER="-sPAPERSIZE=a4"    ;;
	letterdj)	GS_OPTIONS="$GS_OPTIONS -sPAPERSIZE=letter";  GS_AFTER="-sPAPERSIZE=letter";;
	*)		GS_OPTIONS="$GS_OPTIONS -sPAPERSIZE=$size" ;  GS_AFTER="-sPAPERSIZE=$size" ;;
    esac
    test -n "$dpi" && GS_OPTIONS="$GS_OPTIONS -r$dpi"
    #
    # Ghostscript OUT to printer IN filter on fd 5 if any.
    #
    OutPipe="-sOutputFile=/dev/fd/5"
    if   test -x /etc/lpdfilter/${queue}/post ; then
	if link=$(readlink /etc/lpdfilter/${queue}/post) ; then
	    exec 5> >(exec -a "${link##*/}" $link 1>&4)
	    unset link
	else
	    exec 5> >(exec -a "post" /etc/lpdfilter/${queue}/post 1>&4)
	fi
    elif test -n "$post" -a -x "${post%% *}" ; then
	arg0="${post%% *}"
	exec 5> >(exec -a "${arg0##*/}" $post 1>&4)
	unset arg0
    else
	exec 5> >(exec -a "buffer" cat 1>&4)
    fi
    GS_OPTIONS="$GS_OPTIONS $OutPipe"
    export GS_OPTIONS
    export GS_LIB=/etc/lpdfilter/${queue}
    if test -r /etc/profile.d/lilypond.sh ; then
	. /etc/profile.d/lilypond.sh
    elif type -p kpsewhich &> /dev/null ; then
	GS_FONTPATH=$(kpsewhich -expand-path='$T1FONTS')${GS_FONTPATH:+":$GS_FONTPATH"}
    fi
    for f in	/usr/X11R6/lib/Acrobat5/Resource/Font \
		/usr/X11R6/lib/Acrobat4/Fonts	\
		/usr/X11R6/lib/X11/fonts/Type1
    do
	GS_FONTPATH=${GS_FONTPATH:+"${GS_FONTPATH}:"}$f
    done
    test -n "${GS_FONTPATH}" && export GS_FONTPATH
else
    #
    # PostScript OUT to printer IN filter on fd 5 if any.
    #
    OutPipe="/dev/fd/5"
    if   test -x /etc/lpdfilter/${queue}/post ; then
	if link=$(readlink /etc/lpdfilter/${queue}/post) ; then
	    exec 5> >(exec -a "${link##*/}" $link 1>&4)
	    unset link
	else
	    exec 5> >(exec -a "post" /etc/lpdfilter/${queue}/post 1>&4)
	fi
    elif test -n "$post" -a -x "${post%% *}" ; then
	arg0="${post%% *}"
	exec 5> >(exec -a "${arg0##*/}" $post 1>&4)
	unset arg0
    else
	exec 5> >(exec -a "buffer" cat 1>&4)
    fi
fi
#
# Export size and dpi
#
export size dpi

#
# Standard input filter on fd 0 for stripping specials if any.
# Note, that the FIFO's fd 0 is the old one.
#
if test -n "$cat"  -a "$cat" != "cat" ; then
    exec 0< <(exec -a "${cat%% *}" $cat)
fi

#
# If driver is not a real PostScript[tm] printer and input is
# PostScript[tm] we do not need run ps2ps.
## (run ps2ps even if we use ghhostscript for e.g. Euro to
##  be sure that we are able to print several encodings and
##  PostScript creators ike Netscape)
##test "$drv" != "PS" -a "$type" = "ps" && type=""

#
# Standard input filter on fd 0 for formating input if any.
# Note, that the FIFO's fd 0 is the old one.
# Could be overwritten by using a filter or a link to an other
# filter /etc/lpdfilter/${queue}/${type}2ps
#
if test -n "$type" ; then
    if test -x /etc/lpdfilter/${queue}/${type}2ps ; then
	if link=$(readlink /etc/lpdfilter/${queue}/${type}2ps) ; then
	    exec 0< <(exec -a "${link##*/}" $link)
	    unset link
	else
	    exec 0< <(exec -a "${type}2ps" /etc/lpdfilter/${queue}/${type}2ps)
	fi
    else
	exec 0< <(exec -a "${type}2ps" /usr/lib/lpdfilter/filter/${type}2ps)
    fi
fi

#
# PostScript to PostScript filter on fd 0 if any.
# Note, that the FIFO's fd 0 is the old one.
#
if test -x /etc/lpdfilter/${queue}/pre ; then
    if link=$(readlink /etc/lpdfilter/${queue}/pre) ; then
	exec 0< <(exec -a "${link##*/}" $link)
	unset link
    else
	exec 0< <(exec -a "pre" /etc/lpdfilter/${queue}/pre)
    fi
fi

#
# Initialize printer if necessary.
#
test -n "$reset_before" && printf "$reset_before" 1>&4
sleep 1

#
# Main pipe for PostScript
#
(
    #
    # Read the first comments of the PostScript code
    # from stdin
    #
    while read line ; do
	case "${line}" in
	%*) echo -E "${line}" ;;
	*)  break ;;
	esac
    done

    #
    # Just in case a driver needs some PostScript stuff
    # like the stcolor does.
    #
    if test -n "${drvpreload}" ; then
	echo "%%BeginDriverPreload ${drv}"
	echo "(${drvpreload}) findlibfile { pop run } if"
	echo "%%EndDriverPreload ${drv}"
    fi

    # Doing some fiddlings like banner page, duplex on/off,
    # and tray select.
    echo "%%BeginInFilterSection $(hostname -s) $(date)"
	test -n "$tray"     && tray   $tray
	test -n "$gamma"    && gamma  $gamma
	if test "$sh" != "yes" ; then
	    banner <<-EOF
		(User:  ${user-unknown}@${host})
		(Name:  ${name-unknown})
		(Job:   ${job-unknown})
		(Title: ${title})
		(Data:  ${file})
		(Host:  ${host})
		(Date:  ${date})
		EOF
	    test "$duplex" = "on" && echo showpage
	fi
	test -n "$duplex"   && duplex $duplex
    echo "%%EndInFilterSection"

    # First no comment line back to stdin.
    echo -E "${line}"

    # Cat out the rest of stdin.
    cat -
    pipe_status

    # Doing accounting informations with gs
    if test "$drv" != "PS" ; then
	if test -n "$af" -a "$la" != "yes" -a -w "$af" ; then
	    echo "($key:) print currentdevice /PageCount gsgetdeviceprop == flush" 
	fi
    fi
) | case "$drv" in
    PS)     cat 1> ${OutPipe#*=} ;;
    upp)    gs @/etc/lpdfilter/${queue}/upp	$GS_AFTER	-dSAFER - -c quit ;;
    *.upp)  gs @$drv					-dSAFER - -c quit ;;
    hpdj_*) gs -sDEVICE=${drv%_*} -sModel=${drv#*_}	-dSAFER - -c quit ;;
    stp_*)  mod=${drv#*_}
	    gs -sDEVICE=stp       -sModel=${mod//_/-}	-dSAFER - -c quit ;;
    omni_*) 
	    mod=${drv#*_}
	    case "$size" in
		tabloid|11x17)	OMNI="-sproperties=form=FORM_TABLOID"	  ;;
		a4dj)		OMNI="-sproperties=form=FORM_A4"	  ;;
		a[0-5])		OMNI="-sproperties=form=FORM_A${size#a}"  ;;
		b[0-5])		OMNI="-sproperties=form=FORM_A${size#b}"  ;;
		letterdj)	OMNI="-sproperties=form=FORM_LETTER"	  ;;
		letter)		OMNI="-sproperties=form=FORM_LETTER"	  ;;
		legal)		OMNI="-sproperties=form=FORM_LEGAL"	  ;;
		ledger)		OMNI="-sproperties=form=FORM_LEDGER"	  ;;
		*)		OMNI="-sproperties=form=FORM_${size}"	  ;;
	    esac
	    OMNI="$OMNI -sDeviceName=${mod}"
	    gs -sDEVICE=omni $OMNI			-dSAFER - -c quit ;;
    *-*)    gs -sDEVICE=${drv%_*} -sModel=${drv#*_}	-dSAFER - -c quit ;;
    *)      gs -sDEVICE=${drv}				-dSAFER - -c quit ;;
    esac
    pipe_status

#
# Close fd 5 if open
#
case "${OutPipe#*=}" in /dev/fd/5) exec 5>&- ;; esac

#
# Reset printer if necessary.
#
sleep 2
test -n "$reset_after"  && printf "$reset_after"  1>&4

#
# Close the device if given within printcap
#
test "$cl" = "yes" && exec 4>&-

# Exit status:
#  0 is success
#  1 is failed and retry
#  2 is failed and not retry
exit 0

# The End
