#!/bin/sh 
# 
# fax - script to make, send, receive or print a fax
# Copyright Ed Casas 1993, 1994 
# 
# --- Start of user configuration section --- 
# 
# Note: do not put spaces before or after the equal (=) signs.  
#

# The name of the efax executable, including a full path if necessary.

EFAX=efax

# The device to which your fax modem is connected (e.g. ttya for
# /dev/ttya).  Use a dial-out (cua) device if sharing device with
# dial-out programs.

DEV=cua1

# Comment out one of the following lines depending on your modem.
# For Class 1 modems use "-o1 -i+FCLASS=1". For Class 2 modems
# use "-o2 -i+FCLASS=2 -i+FCR=1".  Most Class 2 modems emulate
# the Rockwell chipset and also need the -or option to reverse
# the bit order on receive.

# CLASSINIT="-o1 -i+FCLASS=1"			# Class 1
CLASSINIT="-o2 -i+FCLASS=2 -i+FCR=1 -or"	# Class 2

# Your fax number in international format (20 characters maximum).

ID="+1 800 555 2368"

# Dial string prefix and suffix such as '9' to get an external line, commas
# for delays, W to wait for dial tone, P for pulse dialing.

DIALPREFIX="T"
DIALSUFFIX=""

# The command to run Ghostscript, including a full path if necessary.	

GS=gs

# The command to convert from Group 3 (T.4) format to portable
# bit map (pbm) format.  For an introduction to PBM format see
# the man page for pbm(5).

G3TOPBM=g3topbm

# The command to convert from portable bit map (pbm) to your printer's
# graphics format.  Use pnmtops(1) if you have a Postscript printer.

PRTCVT="pbmtolj -resolution 300"

# The command to print graphics (the fax page image) from the standard
# input.  Typically this is "lpr" or "lp".

PRTPRT="lpr"

# The name(s) of the lock file(s) according to your system's
# conventions. Add a leading '#' to the file name for HDB (ASCII)
# format.

LOCK="-x /usr/spool/uucp/LCK..$DEV"		# "normal" systems
# LOCK=""					# no lock file
# LOCK="-x /usr/spool/locks/LK.047.040.011"	# SysV style names
# LOCK="-x #/usr/spool/uucp/LCK..$DEV"		# HDB format (ASCII pid)

# ****************************************************************
# The remaining options probably won't need to be changed.  Read
# the efax(1) man page for details.
# ****************************************************************

# The fax script file name. "$0" should almost always work.

FAX=$0

# Your hardware/software capabilities.  "1,3,0,0,0,0,0,0" and
# "0,3,0,0,0,0,0,0" should almost always work. The second digit
# can be 5 if you have a 14,400 bps fax modem.  See the efax(1)
# man page for a description of the fields.

HIINIT="1,3,0,0,0,0,0,0"
LOINIT="0,3,0,0,0,0,0,0"

# Commands to set up modem.  "-i&FE0Q0V1X4&D2S7=120" sets up
# almost all modems.  See the efax(1) man page for details.

INIT="-i&FE0Q0V1X4&C0&D2S7=120"

# Speaker mode and loudness. "-iM3L0" sets monitor speaker mode &
# loudness. Use M2 for speaker always on, M0 for always off.

SPKR="-iM3L0"

# Commands to reset modem when efax finishes. "-zH0&F" works in
# most cases.

RESET="-zH0&F"

# Message verbosity

VERB="-v ewin"

# Additional options required only for transmit or only for
# receive.  None normally required.

RXINIT=""
TXINIT=""

# Command to print a unique name for log files and received fax files.
# "date +%m%d%H%M%S" should work on most systems.

TSTAMP="date +%m%d%H%M%S"

# BUSYRETRIES is the maximum number of attempts to redial a busy number and
# BUSYDELAY is the number of seconds between attempts.  Busy numbers will
# not be retried if BUSYRETRIES is zero.  Values of 6 and 300 will retry
# every 5 minutes for 1/2 hour.

BUSYRETRIES=6
BUSYDELAY=300

# A list of signals to be ignored by the script so efax can terminate
# gracefully.  HUP (1), INT (2), QUIT (3), and TERM (15) are probably
# enough.

SIGNALS="1 2 3 15"

# Command to run another program (efax) at a higher-than-normal
# scheduling priority.  This command isn't used if it fails
# (e.g. because the current user isn't privileged).  Comment this
# out if it causes problems.  

NICE="nice -n -10"

# ****************************************************************
# The remaining configuration options apply only to the `fax
# answer' command.  You can ignore these if you will only be
# running efax manually.  See "USING INIT TO RUN EFAX" in the
# efax man page for more information.
# ****************************************************************

# device or file where fatal error messages should be written

CONSOLE=/dev/console

# The directory to store incoming faxes and log files.  This directory
# should already exist and be writable by the user(s) of this script.

FAXDIR=/usr/spool/fax

# The strftime(3) pattern that generates the file name for received files.
# At 10:45:36 on February 25, for example, "%m%d%H%M%S" would produce
# 0225104536, "%j-%H%M" would produce 056-1045, and %d%b%H%M 25Feb1045.

ANSFNAME="%d%b%H%M%S"

# umask for received files. Use 022 to allow anyone to retrieve faxes.

UMASK=022

# The command to execute (using eval) when a fax is received.  Normally
# this sends someone e-mail or prints out the faxes. The variable FILES
# will contain the names of the received files.  Comment this out to do
# nothing.

NOTIFY='mail -s "received fax files $FILES on `date`" $FAXMGR <$logfile'
#NOTIFY='lpr $logfile ; fax print $FILES'

# The user to be sent mail when a fax is received.

FAXMGR=`whoami`

# The number of rings to wait before answering.

ANSRINGS=1

# Modem commands and options to enable adaptive fax/data answer.  You must
# also define GETTY below to enable adaptive answer.  NOTES: (1) Modem
# adaptive answer does not work properly on my Class 2 Sierra-chipset 24/96
# modem unless the modem is set up at 2400 bps (not possible with this
# version of efax). (2) I have been unable to make adaptive answer work
# with my Zoom modem in Class 1.

# DATAINIT="-i+FAE=0"			# Class 1 no adaptive answer
# DATAINIT="-i+FAE=1"			# Class 1 modem adaptive answer
# DATAINIT="-i+FAE=0 -i+FCLASS=0 -oa"	# Class 1 software adaptive answer
# DATAINIT="-i+FAA=0"			# Class 2 no adaptive answer
# DATAINIT="-i+FAA=1"			# Class 2 modem adaptive answer
# DATAINIT="-i+FAA=0 -i+FCLASS=0 -oa"	# Class 2 software adaptive answer

# Argument passed to exec(2) of "/bin/sh -c" for incoming data calls.  This
# command will usually exec getty(8).  Up to 6 %d arguments are replaced by
# the baud rate following the CONNECT response from the modem (or 300 if
# none).  Use 'nice' if required to reduce any special priority set by
# NICE. Comment out if you don't want to handle data calls.

# for getty_ps (Linux)

# GETTY="exec /sbin/getty -h $DEV %d"

#
# --- End of user configuration section ---
#

# -------- help -------- 

if [ x$1 = "x-?"  -o  x$1 = x? ]
then
cat <<EOF

 Usage: 

   fax [ r[eceive] [-l] [filename-prefix] ]
   fax m[ake] [-l] { postscript-file-name | text-file-name }
   fax s[end] [-l] { -m | telephone-number } filename...
   fax p[rint] [-l] filename...
   fax test

   fax q[ueue]		

   fax stop
   fax start
   fax answer [-l]
   fax wait

 use -l for low (96 lpi) resolution
 use -m if the number has been dialed manually 	

 with no command, fax receives a fax.

 session logs are written to a file with the date/time as the
 file name and extension of .log (except for automatic reception).

 fax device is /dev/$DEV
 fax directory is $FAXDIR

EOF
exit 1
fi

# -------- get command and optional resolution flag

if [ $# -lt 1 ] ; then
	cmd=receive
else	
	cmd=$1 
	shift
fi

if [ x$1 = x-l ] ; then
	res=low
	CAP=$LOINIT
	shift
else
	res=high
	CAP=$HIINIT
fi

# -------- check if we can nice(1)

if $NICE true 2>/dev/null 
then
	true
else
	NICE=""
fi

# -------- set default log file name and received file name prefix

tstamp=`$TSTAMP`
logfile=${tstamp}.log

# -------- do the various commands

ERR=0

while true  # so we can use `break' to get to the end of the script
do

case $cmd in 

# fax answer : clean up logs and exec efax. normally run by init(8).

	answer)

	if cd $FAXDIR ; then
		true
	else
		echo ERROR: $FAX cannot cd to "$FAXDIR" >>CONSOLE
		sleep 30
		break 
	fi

	while [ -f $DEV.stop ] ; do sleep 15 ; done

	umask $UMASK

	for f in $DEV.[0-9]*   	# clean up old log files
	do
	  if [ ! -r $f ] ; then continue ; fi
	  if grep "done, returning [145]" $f >/dev/null ; then 
		true  # ignore `signal,' `locked,' and `no modem' exits
	  else
		FILES=`sed -n -e "/received/s/^[^>]*>\ *\([^\ ]*\).*/\1/p" $f`
		FILES=`echo $FILES`
		logfile=$f 
		eval $NOTIFY
		cat $f >>$DEV.log
	  fi
	  /bin/rm -r $f
	done	

	exec $NICE $EFAX -d/dev/$DEV $VERB $LOCK $INIT $SPKR \
	$CLASSINIT $RXINIT \
	-c "$CAP" -l "$ID" $RESET \
	$DATAINIT -g "$GETTY" \
	-iS0=$ANSRINGS -w -s -r "$ANSFNAME" \
	> $DEV.$$ 2>&1

	echo ERROR: $FAX answer exec failed >>CONSOLE
	sleep 30

	break
	;;


# fax wait : runs fax answer (emulates init(8) but PPID != 0)

	wait)

	while true
	do
		echo running $FAX answer
		$FAX answer
	done

	break
	;;


# fax queue : list received fax files

	q*)

	if cd $FAXDIR ; then
		true
	else
		echo $FAX cannot cd to directory "$FAXDIR"
		break 
	fi

	if /bin/ls [0-9]*.[0-9][0-9][0-9] >/dev/null 2>/dev/null
	then
		echo 
		echo Fax files in `pwd` :
		echo
		/bin/ls -l [0-9]*.[0-9][0-9][0-9]
		echo
	fi

	break 
	;;	

# fax start/stop/status : manage fax receive daemon


	start | stop | st* )	

	if cd $FAXDIR ; then
		true
	else
		echo $FAX cannot cd to directory "$FAXDIR"
		break 
	fi

	if /bin/ls ${DEV}.[0-9]* >/dev/null 2>/dev/null ; then
		logfile=`ls $DEV.[0-9]*|tail -1`
	else
		echo no fax answer process for device $DEV
		break 
	fi

	efaxpid=`echo $logfile | sed -e "s/${DEV}\.//g"`

	case $cmd in		# common section

# fax start - remove stop file so fax answer will continue

	start)

	if [ ! -w . ] ; then echo "can't write  `pwd`" ; break ; fi

	/bin/rm -f $DEV.stop

	break 
	;;

# fax stop - make a stop file and kill current fax answer daemon

	stop)

	if [ ! -w . ] ; then echo "can't write `pwd`" ; break ; fi

	if [ -f $DEV.stop ] ; then
		echo $DEV daemon already set to stop
	else
		touch $DEV.stop
	fi
	echo stopping fax daemon for $DEV, pid=$efaxpid
	kill -TERM $efaxpid

	break 
	;;


# fax status - display pid and last 3 lines of log file for current daemon

	st*)

	if [ -f $DEV.stop ] ; then
        	stat="(set to stop)"
      	fi

      	echo
      	echo  "latest fax answer process (${efaxpid}) ${stat}: "
      	echo

      	if ps -u $efaxpid 2>/dev/null ; then
		true
      	else
		echo " pid $efaxpid not active"
      	fi

      	echo
      	echo  "latest fax answer log  ($logfile) : "
      	echo

	grep ":" $logfile | tail -3
	echo
	if [ $# -ge 1 ]
	then 
		sleep $1
		exec $FAX status $1 
	else
		break
	fi

	;;

	esac	# common section
	;;

# fax make : convert text or Postscript to G3 format

	m*)

	if [ $# -lt 1 -o ! -r $1 ] 
	then
		echo "can't read file $1"
		ERR=2 ; break
	fi

	fname=$1

	if head -1 $fname | grep '^%!' >/dev/null
	then
		echo $fname is postscript
		$GS -q -sDEVICE=dfax$res -dNOPAUSE \
		-sOutputFile=${fname}.%03d $fname </dev/null >/dev/null

	else
		echo $fname is text

	# You may change the way text is formatted by changing the values
	# after the following variables in the Postscript program below.
	# The default is a 1 inch top margin and 66 lines per page.  The
	# default font size leaves a 1 inch bottom margin on 11 inch
	# paper.
	#
	#   /topM       - top margin (inches from bottom of page) 
	#   /leftM      - left margin (inches from left side of page)
	#   /lineskip   - font size (points)
	#   /maxlines   - number of lines per page

		 cat - $fname <<END-OF-PS | expand | \
			$GS -q -sDEVICE=dfax$res -dNOPAUSE \
				-sOutputFile=${fname}.%03d - >/dev/null

		/topM 10 72 mul def  /leftM 0.5 72 mul def  
		/lineskip 9.818 def /maxlines 66 def
		/Courier-Bold findfont lineskip scalefont setfont 
		/line 1 def  /infile (%stdin) (r) file def 
		/buf 5000 string def /incr {dup load 1 add def} def 
		/dopage {gsave showpage grestore /line 1 def} def
		/nextline {leftM topM lineskip line mul sub moveto} def
		/get1 {infile buf readline not exch} def 
		/put1 {line maxlines gt {dopage} if nextline 
		       show /line incr} def
		/main {leftM topM moveto {get1 put1 {exit} if}loop 
		       line 2 gt {dopage} if } def 
		main
END-OF-PS

	fi

	for f in ${fname}.[0-9][0-9][0-9]
	do
		dd if=$f of=efax.tmp bs=32 skip=1 2>/dev/null
		/bin/rm -f $f
		mv efax.tmp $f
	done

	break
	;;

# fax send : fax files to given number, converting to g3 first if necessary

	s*) 
	
	if [ $# -lt 2 ] ; then
		echo "missing phone number or file name(s)"
		ERR=2 ; break
	fi

	if [ x$1 = x-m ] ; then
		MANDL="-iX3"
		TELNO=""
		shift
	else
		TELNO="$DIALPREFIX$1$DIALSUFFIX"
		shift
	fi		

	# use `fax make' to convert files if they need to be updated

	FILES=""
	for f in $*
	do
		if [ ! -r $f ] ; then
	      		echo "can't read file $f" ; ERR=2 ; break 2 
		fi
		if echo $f | grep '\.[0-9][0-9][0-9]$' >/dev/null
	   	then 
			FILES="$FILES $f"	# skip image files
	   	else
			if echo ${f}.001: $f \; x | make -r -q -f -
			then
				echo ${f}.nnn up to date
			else
	 			/bin/rm -f ${f}.[0-9][0-9][0-9]
	 			/bin/sh $FAX make $f
			fi
			if [ -r $f.001 ]
	      		then 
                		FILES="$FILES $f.[0-9][0-9][0-9]"
	      		else		# something's wrong, catch it later
	        		FILES="$FILES $f.001"
	      		fi
		fi
        done

	# check that all files are OK

	for f in $FILES ; do
	   if [ ! -r $f ] ; then
	      echo "can't read file $f" ; ERR=2 ; break 2 
	   fi
	done

	while true ; do   # retry busy numbers

		( trap "" $SIGNALS ; \
		$NICE $EFAX -d/dev/$DEV $VERB $LOCK $INIT $SPKR \
		$CLASSINIT $TXINIT \
		-c "$CAP" -l "$ID" $RESET \
		$MANDL -t "$TELNO" $FILES \
		2>&1 | tee $logfile )

		if grep Error: $logfile >/dev/null
		then 
			echo "Error. Check log ($logfile) for details."
			ERR=2 ; break
		fi

		if grep "is\ busy" $logfile >/dev/null
		then
			if [ 0$BUSYRETRIES -ge 1 ] ; then
			     echo Busy. Will try again in 0$BUSYDELAY seconds.
			     sleep 0$BUSYDELAY
			     BUSYRETRIES=`expr 0$BUSYRETRIES - 1`
			else
				echo Busy.  Giving up.
				ERR=2 ; break
			fi
		else
			break
		fi
	done

	break 
	;;

# fax [receive] : answer phone now and receive fax files

	r*)
	
	if [ $# -ge 1 ] ; then
		tstamp=$1
		logfile=$tstamp.log
	fi

	( trap "" $SIGNALS ; \
	$NICE $EFAX -d/dev/$DEV $VERB $LOCK $INIT $SPKR \
	$CLASSINIT $RXINIT \
	-c "$CAP" -l "$ID" $RESET \
	-r $tstamp \
	2>&1 | tee $logfile )

	if grep Error: $logfile >/dev/null
	then
		echo "There were errors, check log (${logfile}) for details."
		ERR=2 ; break 
	fi

	if grep "^+FDCS:\ *0" $logfile || \
	grep "session\ *98lpi" $logfile >/dev/null
	then
		echo $tstamp received at low resolution.
		echo use -l option to print or to re-send
	fi

	break

	;;

# fax print : print G3 files from current directory

        p*) 

	if [ x$res = xlow ] ; then
		stretch="-stretch"
	fi 

	if [ -f $FAXDIR/$1 ]
	then 
		cd $FAXDIR
	fi
	
	for f in $*
	do
  		echo -n "$f ... "
		if [ -r $f ] ; then
			$G3TOPBM $stretch $f | $PRTCVT | $PRTPRT
			if [ $? -gt 0 ] ; then
				ERR=2
			 	echo ": ERROR: can't print."
				break 2
			fi
		else
			echo -n "(cant read, skipping) "
			ERR=2 
		fi
	done

	echo 
	break

	;;

# fax view (undocumented) : use xloadimage (a.k.a. xview) to
# display image.  use g3topbm first since xloadimage v3.3 doesn't
# detect G3 reliably.

	v*)
	
	yzoomlow=100
	yzoomhigh=50
	xzoom=50

	if [ -f $FAXDIR/$1 ]
	then 
		cd $FAXDIR
	fi
	
	for f in $* 
	do
		$G3TOPBM $f | xloadimage -yzoom yzoom$res -xzoom $xzoom stdin 
#		echo -ne "\000\001" | cat - $f | xloadimage \
#			-yzoom yzoom$res -xzoom $xzoom stdin 
	done

	break
	;;

# fax test : make list of shell variables/values and modem features

	t*)

	sed -n -e '/^# --- End/q' -e '/^[A-Z]*\=/p' $0 

	C2CMDS="
	-i+FAA=?  -i+FAA? \
	-i+FAXERR=?  -i+FAXERR?  -i+FBADLIN=?  -i+FBADLIN? \
	-i+FBADMUL=?  -i+FBADMUL?  -i+FBOR=?  -i+FBOR? \
	-i+FBUF?  -i+FBUG=?  -i+FBUG?  -i+FCIG=?  -i+FCIG? \
	-i+FCLASS=?  -i+FCLASS?  -i+FCQ=?  -i+FCQ?  -i+FCR=? \
	-i+FCR?  -i+FCTCRTY=?  -i+FCTCRTY?  -i+FDCC=?  -i+FDCC? \
	-i+FDCS=?  -i+FDCS?  -i+FDFFC=?  -i+FDFFC?  -i+FDIS=? \
	-i+FDIS?  -i+FDT=?  -i+FECM=?  -i+FECM? \
	-i+FET=?  -i+FET?  -i+FLID=?  -i+FLID?  -i+FLNFC=? \
	-i+FLNFC?  -i+FLPL=?  -i+FLPL?  -i+FMDL?  -i+FMFR? \
	-i+FMINSP=?  -i+FMINSP? -i+FPHCTO=?  -i+FPHCTO?  \
	-i+FPTS=?  -i+FPTS?  -i+FRBC=?  -i+FRBC?  -i+FREL=? \
	-i+FREL?  -i+FREV?  -i+FSPL=?  -i+FSPL?  -i+FTBC=? \
	-i+FTBC?  -i+FVRFC=?  -i+FVRFC?  -i+FWDFC=?  -i+FWDFC? \
	"

	C1CMDS="
	-i+FAE=? -i+FAE? -i+FCLASS=?  -i+FCLASS? \
	-i+FTM=? -i+FRM=? \
	-i+FTH=? -i+FRH=? \
	-i+FRS=? -i+FRS=? \
	"

	if echo $CLASSINIT | grep '[-]o *1' 
	then
		CCMDS="$C1CMDS"
	else
		CCMDS="$C2CMDS"
	fi

	$EFAX -vewica -d/dev/$DEV $LOCK $INIT $SPKR \
	$CLASSINIT $TXINIT \
	-iI0 -iI1 -iI2 -iI3 -iI4 -iI5 -iI6 -iI7 -iI8 -iI9 \
	$CCMDS \
	-i"&V" -z"Z" -oe -t ";-" $0 2>&1

	break
	;;	

	*)

	echo "Error. Invalid command ($cmd)"
	ERR=2

	break
	;;

esac	
done

exit $ERR
