#! /bin/bash
#    $Id: common-functions.sh.in,v 1.35 2008/09/11 04:04:17 faxguy Exp $

#
# This holds various functions that are common to the
# various bin scripts.
#

#
# Produce mailable encoding for binary files.
#
encode()
{
    if [ ! -f "$1" ]; then
	return	# encode what?
    fi
    if [ -x "$MIMENCODE" ]; then
	$MIMENCODE < $1 2>$ERRORSTO
    elif [ -x "$UUENCODE" ]; then
	if [ "$ENCODING" = "base64" ]; then
	    $UUENCODE -m $1 ==== | $GREP -v "====$" 2>$ERRORSTO
	else
	    $UUENCODE $1 $1 2>$ERRORSTO
	fi
    else
	# Do not use "-x" for backward compatibility; even if it fails
	# this is last chance to encode data, so there's nothing to lose.
	$MIMENCODE < $1 2>$ERRORSTO
    fi
}

#
# For getting all of the "faxinfo" items to line up.  As the CallID tags
# can be customized we must take unmodified faxinfo output into account.
#
setInfoSize()
{
    INFOSIZE=`$INFO -n $1 | $SED 's/:.*//g' | $SED q | wc -L`
    for ITEM in DICTSENDER DICTPAGES DICTQUALITY DICTSIZE DICTRECEIVED \
		DICTTIMETORECV DICTSIGNALRATE DICTDATAFORMAT DICTERRCORRECT \
		DICTCALLID1 DICTCALLID2 DICTCALLID3 DICTCALLID4 DICTCALLID \
		DICTCALLID6 DICTCALLID7 DICTRECEIVEDON DICTCOMMID; do
	THISLEN="`eval echo \\\""$"$ITEM\\\" | wc -L | $SED 's/ //g'`"
	if [ $THISLEN -gt $INFOSIZE ]; then INFOSIZE=$THISLEN; fi
    done
}

#
# For getting all of the notify job items to line up.
#
setItemSize()
{
    ITEMSIZE=0
    for ITEM in DICTDESTINATION DICTJOBID DICTGROUPID DICTSENDER DICTMAILADDR \
		DICTCOMMID DICTMODEM DICTSUBMITTEDFROM DICTPAGEWIDTH \
		DICTPAGELENGTH DICTRES DICTSTATUS DICTDIALOGS DICTDIALS \
		DICTCALLS DICTPAGES DICTATTEMPTS DICTDIRNUM DICTRECEIVER DICTQUALITY \
		DICTPAGEWIDTH DICTPAGELENGTH DICTDATAFORMAT DICTREMOTEEQUIPMENT \
		DICTREMOTESTATION DICTSIGNALRATE; do
	THISLEN="`eval echo \\\""$"$ITEM\\\" | wc -L | $SED 's/ //g'`"
	if [ $THISLEN -gt $ITEMSIZE ]; then ITEMSIZE=$THISLEN; fi
    done
}

faxInfo()
{
    $INFO -n $1 | $SED -e 's/^ *//g' \
		-e "s/^ *Sender:/$DICTSENDER:/" \
		-e "s/^Pages:/$DICTPAGES:/" \
		-e "s/^Quality:/$DICTQUALITY:/" \
		-e "s/^Page:/$DICTSIZE:/" \
		-e "s/^Received:/$DICTRECEIVED:/" \
		-e "s/^TimeToRecv:/$DICTTIMETORECV:/" \
		-e "s/^SignalRate:/$DICTSIGNALRATE:/" \
		-e "s/^DataFormat:/$DICTDATAFORMAT:/" \
		-e "s/^ErrCorrect:/$DICTERRCORRECT:/" \
		-e "s/^CallID1:/$DICTCALLID1:/" \
		-e "s/^CallID2:/$DICTCALLID2:/" \
		-e "s/^CallID3:/$DICTCALLID3:/" \
		-e "s/^CallID4:/$DICTCALLID4:/" \
		-e "s/^CallID5:/$DICTCALLID5:/" \
		-e "s/^CallID6:/$DICTCALLID6:/" \
		-e "s/^CallID7:/$DICTCALLID7:/" \
		-e "s/ Yes$/ $DICTYES/" \
		-e "s/ No$/ $DICTNO/" \
		-e "s/ Normal$/ $DICTNORMAL/" \
		-e "s/ Fine$/ $DICTFINE/" \
		-e 's/:/|/' | \
		printFormatted $INFOSIZE
}

printFormatted()
{
    $AWK -F\| -v s=$1 'BEGIN { size = s; } { ("echo \""$1"\" | wc -L") | getline ilen; close("echo \""$1"\" | wc -L"); printf "%"size-ilen"s%s:%s\n", "",$1,$2 }'
}

#
# Export qfile content to environment
# parseQfile(prefix, filename)
# Both parameters can be omitted. Defaults to no prefix and $QFILE.
#
parseQfile()
{
    # In shell scripts, there are no special characters in hard-quoted
    # strings (quoted with (')). Single-quotes can't even be escaped
    # inside such strings and must be put outside of them. We thus replace
    # (') with ('\'') which terminates the current string, adds a single
    # quote and starts a new string.
    #
    # We no longer escape newlines, because we don't eval
    #
    # print out variable name and value so we can eval it in the shell
    #
    VAR_PREFIX="$1"
    if [ -n "$2" ] ; then
        FILENAME=$2;
    else
        FILENAME=$QFILE;
    fi
    if [ ! -f "$FILENAME" ] ; then
        return # cannot do much more without a file
    fi
    $AWK -F: '
    function p(varname,val)
    {
        gsub(/\047/, "\047\\\047\047", val);
        # mawk sees 047 as decimal 47 rather than octal, so we use the decimal
        # value of the quote character: 39.
        printf "%s%s=%c%s%c\n",var_prefix,varname,39,val,39
        printf "export %s%s\n",var_prefix,varname
    }
    BEGIN {
        var_prefix="'"$VAR_PREFIX"'";
        nfiles = 0;
        npins = 0;
    }
    /^nsf/	{ p("equipment", $3); p("station", $5); next; }
    /^external/    { p("number", $2); next; }      # override unprocessed number
    /^regarding/    { regarding = $0; sub("regarding:", "", regarding); p("regarding", regarding); next; }
    /^jobtag/    { jobtag = $0; sub("jobtag:", "", jobtag); p("jobtag", jobtag); next; }
    # status needs to be used in the shell as faxstatus since status is reserved word
    /^status:/    { status = $0; sub("status:", "", status);
              while ($0 ~ /\\\\$/ && getline > 0) {
                  sub(/\\\\$/, "\\n", status);
                  status = status $0;
              } p("faxstatus", status);
              next;
            }
    /^[!]*post/    { p("files_"++nfiles, $4); p("filetype_"nfiles, "PostScript"); next; }
    /^[!]*tiff/    { p("files_"++nfiles, $4); p("filetype_"nfiles, "TIFF"); next; }
    /^[!]*pdf/    { p("files_"++nfiles, $4); p("filetype_"nfiles, "PDF"); next; }
    /^[!]*pcl/    { p("files_"++nfiles, $4); p("filetype_"nfiles, "PCL"); next; }
    /^page:/    { p("pins_"++npins, $4); next; }
    /^data:/    { p("files_"++nfiles, $4); next; }
    /^poll/        { p("poll", " -p"); next; }
    # Only parse remaining valid lines and allows for colons to appear in the value part
    /^[a-z]+:/     { str = $0; sub($1":", "", str); p($1, str); next; }
    {printf "# Invalid line> %s\n", $0;}
    END { p("nfiles", nfiles); p("npins", npins) } ' $FILENAME > $TMPDIR/qfile-awk.sh
    . $TMPDIR/qfile-awk.sh
}

#
# Produce faxable TIFF (MH, MR, MMR) from a PDF or Postscript file.
#
gs2fax()
{
    test -z "$files" && files="-"	# read from stdin
    case "${pagewidth}x${pagelength}" in
	1728x280|1728x279|2592x280|2592x279|3456x280|3456x279)	# 279.4mm is actually correct...
	    paper=letter;;
	1728x364|2592x364|3456x364) 
	    paper=legal;;
	*x296|*x297)			# more roundoff problems...
	    paper=a4;;
	*x364)
	    paper=b4;;
	2432x*|3648x*|4864x*)
	    paper=a3;;
	*)
	    echo "$0: Unsupported page size: $pagewidth x $pagelength";
	    exit 254;;			# causes document to be rejected
    esac
    #
    # The image must end up with a pixel width according to T.32 Table 21.
    # Ghostscript contains code to fixate a4 and letter to 1728 pixels
    # when using 196-204 dpi and tiffg3/4, it supposedly does the same for
    # B4 but not for A3, thus the floats are needed (for A3's benefit).
    #
    # See ghostscript/doc/Devices.htm under -dAdjustWidth=1 (default).
    # Use -dAdjustWidth=0 to disable.  With the right patch,
    # http://bugs.ghostscript.com/show_bug.cgi?id=688064
    # AdjustWidth can be made to specify the pagewidth directly and
    # replace -dFIXEDMEDIA to permit TIFFs to be produced with
    # varied lengths.
    #
    # Another alternative to -dFIXEDMEDIA is -dPDFFitPage which has the 
    # difference of "resizing" images to fit the page rather than cutting
    # them off.
    #
    case "$paper" in
	a4)
	    case "$pagewidth" in
		2592) hres=313.65;;		# VR_300X300
		3456) hres=418.20;;		# VR_R16
		*) hres=209.10;;		# everything else, 1728 pixels
	    esac;;
	b4)
	    case "$pagewidth" in
		3072) hres=311.97;;		# VR_300X300
		4096) hres=415.95;;		# VR_R16
		*) hres=207.98;;		# everything else, 2048 pixels
	    esac;;
	a3)
	    case "$pagewidth" in
		3648) hres=311.94;;		# VR_300X300
		4864) hres=415.93;;		# VR_R16
		*) hres=207.96;;		# everything else, 2432 pixels
	    esac;;
	*)					# letter, legal
	    case "$pagewidth" in
		2592) hres=304.94;;		# VR_300X300
		3456) hres=406.59;;		# VR_R16
		*) hres=203.29;;		# everything else, 1728 pixels
	    esac;;
    esac

    #
    # The sed work fixes bug in Windows-generated
    # PostScript that causes certain international
    # character marks to be placed incorrectly.
    #
    #    | $SED -e 's/yAscent Ascent def/yAscent 0 def/g' \
    #
    # NB: unfortunately it appears to break valid PostScript;
    #     so it's been disabled.

    #
    # Suggestion from "Alan Sparks" <asparks@nss.harris.com>,
    # Add the -DFIXEDMEDIA argument to the last command in ps2fax.
    # This prevents page sizing within the documents from altering
    # the command-line page size specification.  This prevents
    # TIFFs to be made with pages of varied lengths, however.
    # See the comments on AdjustWidth above.
    #
    FIXEDWIDTH="-dFIXEDMEDIA"

    #
    # Ghostscript's default dithering with the fax drivers is 
    # usually unsatisfactory.  So we have an option to process
    # the image through libtiff's Floyd-Steinberg dithering.
    # This will be a bit more costly on CPU and image preparation
    # time... enable it cautiously.
    #
    # An alternative to libtiff Floyd-Steinberg dithering would
    # be to use a threshold array based stochastic mask within
    # Ghostscript.  However, this alternative may perform less 
    # than ideally on unmodified Ghostscript versions prior to 8.62.
    # See: http://bugs.ghostscript.com/show_bug.cgi?id=689633
    #
    DITHERING=default

    #
    # Apply customizations such as watermarking.
    #
    if [ -f etc/FaxModify ]; then
	. etc/FaxModify
    fi

    if [ "$DITHERING" = "gs-stocht" ]; then
	$CAT $files | $PS -q \
	    -sDEVICE=$device \
	    -dNOPAUSE \
	    -dSAFER=true \
	    -sPAPERSIZE=$paper \
	    $FIXEDWIDTH \
	    -dBATCH \
	    -r$hres\x$vres \
	    "-sOutputFile=$out" \
	    stocht.ps -c "<< /HalftoneMode 1 >> setuserparams" -
	return
    fi
    if [ "$DITHERING" = "libtiff-fs" ] && ($PS -h | $GREP tiff24nc >/dev/null 2>&1) && \
       [ -x $TIFFBIN/tiff2bw ] && [ -x $TIFFBIN/tiffdither ] && [ -x $TIFFBIN/tiff2ps ] && \
       [ -x $TIFFBIN/tiffsplit ] && [ -x $TIFFBIN/tiffcp ]; then
	$PS -q -sDEVICE=tiff24nc -dNOPAUSE -dSAFER=true -sPAPERSIZE=$paper \
	    $FIXEDWIDTH -dBATCH -r$hres\x$vres "-sOutputFile=$out.1" $files
	# Both tiff2bw and tiffdither only operate on single pages, so...
	mkdir tmpdir.$$
	cd tmpdir.$$
	$TIFFBIN/tiffsplit ../$out.1
	for i in *; do
	    $TIFFBIN/tiff2bw $i $i.2
	    $TIFFBIN/tiffdither $i.2 $i.3
	    $RM -f $i $i.2
	done
	$TIFFBIN/tiffcp * ../$out.2
	$RM -f *
	cd ..
	rmdir tmpdir.$$
	#
	# Unfortunately, this process leaves the image with Photometric of min-is-black, which
	# is opposite from what we need for faxing.  So we have to run it again through gs.
	#
	$TIFFBIN/tiff2ps -a $out.2 > $out.3
	files=$out.3
    else
	DITHERING=default
    fi

    $PS -q \
	-sDEVICE=$device \
	-dNOPAUSE \
	-dSAFER=true \
	-sPAPERSIZE=$paper \
	$FIXEDWIDTH \
	-dBATCH \
	-r$hres\x$vres \
	"-sOutputFile=$out" \
	$files

    if [ "$DITHERING" = "libtiff-fs" ]; then
	$RM -f $out.1 $out.2 $out.3
    fi
}

SetupPrivateTmp()
{
    if [ -d "$HYLAFAX_TMPDIR" ]; then
        # Private temp area already created.
        return
    fi

    # Would have liked to use -t, but older mktemp don't support it.
    if [ -z "$TMPDIR" ] || [ ! -w "$TMPDIR" ]; then
        TMPDIR="/tmp"
    fi
    HYLAFAX_TMPDIR=`mktemp -d $TMPDIR/hylafaxtmp-XXXXXXXX 2>/dev/null` || {
        HYLAFAX_TMPDIR="$TMPDIR/hylafaxtmp-$RANDOM-$RANDOM-$RANDOM-$$"
        mkdir -m 0700 "$HYLAFAX_TMPDIR"
    }
    if [ $? != 0 ]
    then
	echo "Couldn't setup private temp area - exiting!" 1>&2
	exit 1
    fi
    # We want any called programs to use our tmp dir.
    TMPDIR=$HYLAFAX_TMPDIR
    export TMPDIR

    trap cleanupExit 0
    trap "hfExit 1" 1 2 15
}

CleanupPrivateTmp ()
{
    if [ -d "$HYLAFAX_TMPDIR" ]
    then
	rm -Rf "$HYLAFAX_TMPDIR"
    fi
}

cleanupExit ()
{
    trap - 0 1 2 15
    CleanupPrivateTmp
}

hfExit ()
{
    cleanupExit
    exit $1
}
