#! /bin/sh
# patch script for converting a15 to a19
TOP=.

Patch()
{
    echo "Patch $1:"; patch -s $TOP/$1
}

NewFile()
{
    echo "New file $1"; cat >$TOP/$1
}

NewDir()
{
    echo "New directory $1"; mkdir $TOP/$1
}

RmFile()
{
    echo "Remove $1"; rm -f $TOP/$1.orig; mv $TOP/$1 $TOP/$1.orig
}

Patch MODEMS<<'EOF-EOF-EOF'
1c1
< $Header: /usr/people/sam/fax/RCS/MODEMS,v 1.19 93/05/12 09:08:31 sam Exp $
---
> $Header: /usr/people/sam/fax/RCS/MODEMS,v 1.20 93/07/15 10:50:26 sam Exp $
40c40
< o received HDLC frames do NOT include the FCS bytes
---
> o received HDLC frames do NOT include the FCS bytes (fixed in rev 133).
EOF-EOF-EOF
Patch distrules<<'EOF-EOF-EOF'
1c1
< #	$Header: /usr/people/sam/fax/RCS/distrules,v 1.62 93/07/11 13:04:08 sam Exp $
---
> #	$Header: /usr/people/sam/fax/RCS/distrules,v 1.64 93/07/19 11:33:59 sam Exp $
131d130
<     port/linux/osfcn.h				\
133c132
<     port/linux/sys/param.h			\
---
>     port/linux/sys/types.h			\
260a260
> 	util/recvstats.sh			\
263a264
> 	util/xferstats.sh			\
269,270d269
< 	util/xfer.awk				\
< 	util/xferdest.awk			\
EOF-EOF-EOF
Patch dist/flexfax.alpha<<'EOF-EOF-EOF'
1c1
< define ALPHA 015
---
> define ALPHA 019
EOF-EOF-EOF
Patch faxd/Class2.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/Class2.c++,v 1.60 93/05/16 11:51:30 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/Class2.c++,v 1.61 93/07/13 11:42:41 sam Exp $
213,214d212
<  * Note that we do not touch bit rate or min scanline time
<  * because we leave these to the modems to handle.
220c218
<     params.br = (u_int) -1;
---
>     params.br = getBestSignallingRate();
224,226c222,224
<     params.ec = (u_int) -1;
<     params.bf = (u_int) -1;
<     params.st = (u_int) -1;
---
>     params.ec = EC_DISABLE;		// XXX
>     params.bf = BF_DISABLE;
>     params.st = getBestScanlineTime();
EOF-EOF-EOF
Patch faxd/Class2Send.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/Class2Send.c++,v 1.56 93/07/09 14:51:00 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/Class2Send.c++,v 1.57 93/07/13 11:45:12 sam Exp $
145,146d144
<     u_int br = next.br;			// saved for caller use
<     next.br = (u_int) -1;		// put in what's expected below
157,164d154
< 	    /*
< 	     * XXX don't touch these, some modems such as the
< 	     * ZyXEL get confused and hangup the phone (argh).
< 	     */
< 	    next.br = (u_int) -1;
< 	    next.ec = (u_int) -1;
< 	    next.bf = (u_int) -1;
< 	    next.st = (u_int) -1;
221d210
<     next.br = br;			// restore original
EOF-EOF-EOF
Patch faxd/FaxMachineLog.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxMachineLog.c++,v 1.9 93/07/09 17:30:28 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxMachineLog.c++,v 1.10 93/07/15 10:25:15 sam Exp $
110c110
<     vsprintf(buf+strlen(buf), fmt, ap);
---
>     vsprintf(buf+strlen(buf), (char*) fmt, ap);
EOF-EOF-EOF
Patch faxd/FaxModem.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxModem.c++,v 1.75 93/06/22 18:21:08 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxModem.c++,v 1.76 93/07/13 17:28:06 sam Exp $
1004c1004
< FaxModem::recvCSI(const fxStr& csi)
---
> FaxModem::recvCSI(fxStr& csi)
1006,1010c1006,1009
<     fxStr s(csi);
<     s.remove(0, s.skip(0,' '));		// strip leading white space
<     u_int pos = s.skipR(s.length(),' ');
<     s.remove(pos, s.length() - pos);	// and trailing white space
<     protoTrace("REMOTE CSI \"%s\"", (char*) s);
---
>     csi.remove(0, csi.skip(0,' '));	// strip leading white space
>     u_int pos = csi.skipR(csi.length(),' ');
>     csi.remove(pos, csi.length() - pos);// and trailing white space
>     protoTrace("REMOTE CSI \"%s\"", (char*) csi);
EOF-EOF-EOF
Patch faxd/FaxModem.h<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxModem.h,v 1.66 93/07/09 14:51:02 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxModem.h,v 1.67 93/07/13 17:27:43 sam Exp $
249c249
<     void	recvCSI(const fxStr&);
---
>     void	recvCSI(fxStr&);
EOF-EOF-EOF
Patch faxd/FaxSend.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxSend.c++,v 1.77 93/07/09 18:03:56 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/FaxSend.c++,v 1.81 93/07/19 13:57:41 sam Exp $
64a65,67
> 	     *
> 	     * NB: we fetch the client information here because
> 	     *     it's needed in preparing the document.
65a69,70
> 	    fxStr canon = canonicalizePhoneNumber(fax.number);
> 	    clientInfo.restore(canon);
69,71c74,75
< 		 * Construct the phone number to dial by crafting the
< 		 * canonical form (+<country><areacode><number>), and
< 		 * preparing the form to use in dialing the telephone.
---
> 		 * Construct the phone number to dial by applying the
> 		 * dialing rules to the user-specified dialing string.
73d76
< 		fxStr canon = canonicalizePhoneNumber(fax.number);
106a110
>     u_int pagesSent = npages - fax.npages;
108a113,114
> 	pagesSent,
> 	clientInfo.getCSI(),
139d144
<     clientInfo.restore(canon);
543c548,549
< 	    "REJECT: %s, max remote page width %u, image width %lu",
---
> 	    "REJECT: %s, max remote page width %u pixels"
> 		 ", image width %lu pixels",
576c582
< 		"REJECT: %s, image resolution %g line/mm",
---
> 		"REJECT: %s, image resolution %g lines/mm",
599,600c605,607
< 		"REJECT: %s, max remote page length %d, image length %lu",
< 		(char*) emsg, clientInfo.getMaxPageLength(), h);
---
> 		"REJECT: %s, max remote page length %d mm"
> 		    ", image length %lu rows (%.2f mm)",
> 		(char*) emsg, clientInfo.getMaxPageLength(), h, len);
EOF-EOF-EOF
Patch faxd/faxServerApp.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/faxServerApp.c++,v 1.109 93/07/09 18:27:57 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/faxServerApp.c++,v 1.112 93/07/19 13:58:18 sam Exp $
793a794,806
> struct FaxAcctInfo {
>     const char*	user;		// sender/receiver identity
>     time_t	start;		// starting time
>     time_t	duration;	// job duration (minutes)
>     const char*	device;		// modem device
>     const char*	dest;		// receiver phone number
>     const char*	csi;		// remote csi
>     u_int	npages;		// pages successfully sent/rcvd
>     u_int	sigrate;	// negotiated signalling rate
>     const char*	df;		// negotiated data format
>     const char*	status;		// status info (optional)
> };
> 
798c811,812
< faxServerApp::notifySendDone(FaxRequest* req, u_int sigrate, const char* protocol)
---
> faxServerApp::notifySendDone(FaxRequest* req,
>     u_int npages, const char* csi, u_int sigrate, const char* df)
801,803c815,825
< 	time_t now = time(0);
< 	float dt = (now - jobStart) / 60.;
< 	const char* reason = req->notice;
---
> 	FaxAcctInfo ai;
> 	ai.user = req->mailaddr;
> 	ai.start = jobStart;
> 	ai.duration = time(0) - jobStart;
> 	ai.device = devID;
> 	ai.dest = req->number;
> 	ai.csi = csi;
> 	ai.npages = npages;
> 	ai.sigrate = sigrate;
> 	ai.df = df;
> 	ai.status = req->notice;
805,807c827,828
< 	    reason = "";
< 	record("SEND", req->mailaddr, req->number,
< 	    sigrate, protocol, req->npages, dt, reason);
---
> 	    ai.status = "";
> 	account("SEND", ai);
1287,1288c1308,1320
<     record("RECV", ri.sender, server->getModemNumber(),
< 	ri.sigrate, ri.protocol, ri.npages, ri.time / 60., ri.reason);
---
> 
>     FaxAcctInfo ai;
>     ai.user = "fax";
>     ai.duration = (time_t) ri.time;
>     ai.start = time(0) - ai.duration;
>     ai.device = devID;
>     ai.dest = server->getModemNumber();
>     ai.csi = ri.sender;
>     ai.npages = ri.npages;
>     ai.sigrate = ri.sigrate;
>     ai.df = ri.protocol;
>     ai.status = ri.reason;
>     account("RECV", ai);
1507c1539,1540
< 	*cp++ = digits[v / 10];
---
> 	if (v >= 10)
> 	    *cp++ = digits[v / 10];
1513c1546
<     if (v > 10 || cp > tbuf)
---
>     if (v >= 10 || cp > tbuf)
1567,1571c1600
< faxServerApp::record(const char* cmd,
<     const char* from, const char* to,
<     int sigrate, const char* protocol,
<     int npages, float duration,
<     const char* reason)
---
> faxServerApp::account(const char* cmd, const FaxAcctInfo& ai)
1576d1604
< 	time_t now = time(0);
1578,1580c1606,1614
< 	strftime(buf, sizeof (buf), "%D %R", localtime(&now));
< 	fprintf(flog, "%s\t%s\t\"%s\"\t\"%s\"\t%d\t\"%s\"\t%d\t%.2f\t\"%s\"\n",
< 	    buf, cmd, from, to, sigrate, protocol, npages, duration, reason);
---
> 	strftime(buf, sizeof (buf), "%D %R", localtime(&ai.start));
> 	fprintf(flog,
> 	    "%s\t%s\t%s\t%s\t\"%s\"\t\"%s\"\t%d\t%s\t%d\t%s\t\"%s\"\n",
> 	    buf, cmd, ai.device,
> 	    ai.user,
> 	    ai.dest, ai.csi,
> 	    ai.sigrate, ai.df,
> 	    ai.npages, fmtTime(ai.duration),
> 	    ai.status);
EOF-EOF-EOF
Patch faxd/faxServerApp.h<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/faxd/RCS/faxServerApp.h,v 1.39 93/07/09 18:04:35 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/faxd/RCS/faxServerApp.h,v 1.41 93/07/19 13:58:19 sam Exp $
94c94
<     time_t	fileStart;		// starting time for file
---
>     time_t	fileStart;		// starting time for file/poll
119,123c119
<     void	record(const char* cmd,
< 		    const char* from, const char* to,
< 		    int rate, const char* protocol,
< 		    int npages, float time,
< 		    const char* reason);
---
>     void	account(const char* cmd, const struct FaxAcctInfo&);
176c172,173
<     void	notifySendDone(FaxRequest* req, u_int sigrate, const char* protoocol);
---
>     void	notifySendDone(FaxRequest* req, u_int npages,
> 		    const char* csi, u_int sigrate, const char* df);
EOF-EOF-EOF
Patch gs/gdevtiff.c<<'EOF-EOF-EOF'
1c1
< /* $Header: /usr/people/sam/fax/gs/RCS/gdevtiff.c,v 1.4 93/07/06 21:11:00 sam Exp $ */
---
> /* $Header: /usr/people/sam/fax/gs/RCS/gdevtiff.c,v 1.6 93/07/19 08:20:44 sam Exp $ */
136c136
<     { 85,  117, 1728 },
---
>     { 85,  116, 1728 },
171c171
< 	ddev->fax.iwidth = pi->iw;
---
> 	ddev->fax->iwidth = pi->iw;
EOF-EOF-EOF
Patch man/textfmt.1<<'EOF-EOF-EOF'
1c1
< .\"	$Header: /usr/people/sam/fax/man/RCS/textfmt.1,v 1.3 93/07/08 17:06:43 sam Exp $
---
> .\"	$Header: /usr/people/sam/fax/man/RCS/textfmt.1,v 1.4 93/07/16 10:14:06 sam Exp $
150c150
< this file will almost certainly result
---
> this metric will almost certainly result
EOF-EOF-EOF
Patch port/linux/README<<'EOF-EOF-EOF'
6,7c6,7
< 	- the kernel needs two small big fixes.  See the file
< 	  kernel.diffs for details.  From the directory in which
---
> 	- the kernel needs a small bug fix.  See the file
> 	  kernel.patch for details.  From the directory in which
10c10
< 	  These fixes are needed for versions <= 99p10.
---
> 	  This fix is needed for versions <= 99p10.
EOF-EOF-EOF
Patch port/linux/kernel.patch<<'EOF-EOF-EOF'
1c1
< Two small bug fixes for kernels less than or equal to patchlevel 10.
---
> A small bug fix for kernels less than or equal to patchlevel 10.
5,17c5,9
< 	1. In the current code, select() on a pipe returns true (ie data
< 	   available) if there are no reader/writers.  In the case of
< 	   named pipes (FIFOs), it is quite permissible for there to be
< 	   no reader/writers, and select() should block until data is
< 	   available.  In the case of normal pipes, exfds can be used to
< 	   check for pipes that have no reader/writers.
< 	2. The nice call doesn't do high boundary checking.  This causes
< 	   the reccommended method for setting a nice value to 0 to
< 	   fail {nice(-40); nice(20); nice(0);}.  Also note that the
< 	   nice limits are now 1 to 35, not the normal U*X values of
< 	   0 to 39, values determined by the choice of PZERO - the man
< 	   page should be updated.  And ps prints the nice value with
< 	   the wrong sign.
---
> In the current code, select() on a pipe returns true (ie data available)
> if there are no reader/writers.  In the case of named pipes (FIFOs), it
> is quite permissible for there to be no reader/writers, and select()
> should block until data is available.  Similar exceptions apply to
> reads/writes.
18a11,29
> *** fs/fifo.c.orig	Wed Mar 31 20:08:45 1993
> --- fs/fifo.c	Wed Jul 14 08:29:38 1993
> ***************
> *** 156,162 ****
>   void init_fifo(struct inode * inode)
>   {
>   	inode->i_op = &fifo_inode_operations;
> ! 	inode->i_pipe = 1;
>   	PIPE_BASE(*inode) = NULL;
>   	PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
>   	PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
> --- 156,162 ----
>   void init_fifo(struct inode * inode)
>   {
>   	inode->i_op = &fifo_inode_operations;
> ! 	inode->i_pipe = 2;
>   	PIPE_BASE(*inode) = NULL;
>   	PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
>   	PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
20c31
< --- fs/pipe.c	Sun May 23 01:13:43 1993
---
> --- fs/pipe.c	Thu Jul 15 00:43:26 1993
21a33,83
> *** 20,26 ****
>   	if (!(filp->f_flags & O_NONBLOCK))
>   		while (!PIPE_SIZE(*inode)) {
>   			wake_up(& PIPE_WRITE_WAIT(*inode));
> ! 			if (!PIPE_WRITERS(*inode)) /* are there any writers? */
>   				return 0;
>   			if (current->signal & ~current->blocked)
>   				return -ERESTARTSYS;
> --- 20,26 ----
>   	if (!(filp->f_flags & O_NONBLOCK))
>   		while (!PIPE_SIZE(*inode)) {
>   			wake_up(& PIPE_WRITE_WAIT(*inode));
> ! 			if (inode->i_pipe == 1 && !PIPE_WRITERS(*inode)) /* are there any writers? */
>   				return 0;
>   			if (current->signal & ~current->blocked)
>   				return -ERESTARTSYS;
> ***************
> *** 51,57 ****
>   {
>   	int chars, size, written = 0;
>   
> ! 	if (!PIPE_READERS(*inode)) { /* no readers */
>   		send_sig(SIGPIPE,current,0);
>   		return -EPIPE;
>   	}
> --- 51,57 ----
>   {
>   	int chars, size, written = 0;
>   
> ! 	if (inode->i_pipe == 1 && !PIPE_READERS(*inode)) { /* no readers */
>   		send_sig(SIGPIPE,current,0);
>   		return -EPIPE;
>   	}
> ***************
> *** 62,68 ****
>   		size = PAGE_SIZE-1;
>   	while (count>0) {
>   		while (PIPE_SIZE(*inode) >= size) {
> ! 			if (!PIPE_READERS(*inode)) { /* no readers */
>   				send_sig(SIGPIPE,current,0);
>   				return written?written:-EPIPE;
>   			}
> --- 62,68 ----
>   		size = PAGE_SIZE-1;
>   	while (count>0) {
>   		while (PIPE_SIZE(*inode) >= size) {
> ! 			if (inode->i_pipe == 1 && !PIPE_READERS(*inode)) { /* no readers */
>   				send_sig(SIGPIPE,current,0);
>   				return written?written:-EPIPE;
>   			}
> ***************
39c101
< ! 			if (!PIPE_EMPTY(*inode))
---
> ! 			if (!PIPE_EMPTY(*inode) || (inode->i_pipe == 1 && !PIPE_WRITERS(*inode)))
44c106
< ! 			if (!PIPE_FULL(*inode))
---
> ! 			if (!PIPE_FULL(*inode) || (inode->i_pipe == 1 && !PIPE_READERS(*inode)))
48,72d109
< *** kernel/sched.c.orig	Sat Apr 24 09:52:29 1993
< --- kernel/sched.c	Tue Jun 22 23:58:12 1993
< ***************
< *** 504,512 ****
<   {
<   	if (increment < 0 && !suser())
<   		return -EPERM;
< - 	if (increment >= current->priority)
< - 		increment = current->priority-1;
<   	current->priority -= increment;
<   	return 0;
<   }
<   
< --- 504,514 ----
<   {
<   	if (increment < 0 && !suser())
<   		return -EPERM;
<   	current->priority -= increment;
< + 	if (current->priority < 1)
< + 		current->priority = 1;
< + 	if (current->priority > 35)
< + 		current->priority = 35;
<   	return 0;
<   }
<   
EOF-EOF-EOF
RmFile port/linux/osfcn.h
Patch port/linux/paths.h<<'EOF-EOF-EOF'
4c4
< #include </usr/include/paths.h>
---
> #include_next <paths.h>
EOF-EOF-EOF
RmFile port/linux/sys/param.h
NewFile port/linux/sys/types.h<<'EOF-EOF-EOF'
#include_next <sys/types.h>
/*
 * Macros for counting and rounding.
 */
#ifndef howmany
#define	howmany(x, y)	(((x)+((y)-1))/(y))
#endif
EOF-EOF-EOF
Patch recvfax/main.c<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/recvfax/RCS/main.c,v 1.38 93/07/08 17:06:27 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/recvfax/RCS/main.c,v 1.39 93/07/16 10:17:07 sam Exp $
89c89
< #ifdef SO_LINGER
---
> #if defined(SO_LINGER) && !defined(__linux__)
EOF-EOF-EOF
Patch util/FaxClient.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/util/RCS/FaxClient.c++,v 1.27 93/07/08 16:49:15 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/util/RCS/FaxClient.c++,v 1.28 93/07/16 10:29:53 sam Exp $
267a268,277
> #ifdef __linux__
> 	/*
> 	 * Linux kernel bug: can get short writes on
> 	 * stream sockets when setup for blocking i/o.
> 	 */
> 	cc -= n;
> 	for (int cnt, sent = 0; n; sent += cnt, n -= cnt) 
> 	    if ((cnt = write(fdOut, buf + sent, n)) <= 0)
> 	        fxFatal("Protocol botch (data write).");
> #else
270a281
> #endif
EOF-EOF-EOF
Patch util/Makefile<<'EOF-EOF-EOF'
2c2
< #	$Header: /usr/people/sam/fax/util/RCS/Makefile,v 1.52 93/07/09 18:27:41 sam Exp $
---
> #	$Header: /usr/people/sam/fax/util/RCS/Makefile,v 1.53 93/07/19 11:34:17 sam Exp $
102a103,104
> 	${PUTSERV} -F ${SPOOL}/bin -src xferstats.sh -O xferstats
> 	${PUTSERV} -F ${SPOOL}/bin -src recvstats.sh -O recvstats
EOF-EOF-EOF
Patch util/SendFaxClient.c++<<'EOF-EOF-EOF'
1c1
< /*	$Header: /usr/people/sam/fax/util/RCS/SendFaxClient.c++,v 1.8 93/06/18 11:44:20 sam Exp $
---
> /*	$Header: /usr/people/sam/fax/util/RCS/SendFaxClient.c++,v 1.9 93/07/16 10:29:56 sam Exp $
39a40
> #ifndef __linux__
40a42
> #endif
EOF-EOF-EOF
Patch util/ps2fax.gs.sh<<'EOF-EOF-EOF'
2c2
< #	$Header: /usr/people/sam/fax/util/RCS/ps2fax.gs.sh,v 1.9 93/07/08 16:48:33 sam Exp $
---
> #	$Header: /usr/people/sam/fax/util/RCS/ps2fax.gs.sh,v 1.10 93/07/16 10:30:03 sam Exp $
43c43
< opt=
---
> out=ps.fax		# default output filename
50c50
<     -o)	shift; opt="$opt -sOutputFile=$1" ;;
---
>     -o)	shift; out="$1" ;;
60,65c60,68
< # 279.4mm is actually correct, but many people round up!
< 1728x280|1728x279) paper=letter;;
< *x297) paper=a4;;
< *x364) paper=b4;;
< *)	echo "$0: Unsupported page size: $pagewidth x $pagelength";
< 	exit 1;;
---
> 1728x280|1728x279)		# 279.4mm is actually correct...
>     paper=letter;;
> *x296|*x297)			# more roundoff problems...
>     paper=a4;;
> *x364)
>     paper=b4;;
> *)
>     echo "$0: Unsupported page size: $pagewidth x $pagelength";
>     exit 1;;
68c71
<     -sPAPERSIZE=$paper -r204x$vres -dSAFER=true $opt -
---
>     -sPAPERSIZE=$paper -r204x$vres -dSAFER=true "-sOutputFile=$out" -
EOF-EOF-EOF
NewFile util/recvstats.sh<<'EOF-EOF-EOF'
#! /bin/sh
#	$Header: /usr/people/sam/fax/util/RCS/recvstats.sh,v 1.2 93/07/19 11:56:57 sam Exp $
#
# FlexFAX Facsimile Software
#
# Copyright (c) 1990, 1991, 1992, 1993 Sam Leffler
# Copyright (c) 1991, 1992, 1993 Silicon Graphics, Inc.
# 
# Permission to use, copy, modify, distribute, and sell this software and 
# its documentation for any purpose is hereby granted without fee, provided
# that (i) the above copyright notices and this permission notice appear in
# all copies of the software and related documentation, and (ii) the names of
# Sam Leffler and Silicon Graphics may not be used in any advertising or
# publicity relating to the software without the specific, prior written
# permission of Sam Leffler and Silicon Graphics.
# 
# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
# 
# IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
# ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
# LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
# OF THIS SOFTWARE.
#

#
# Print Statistics about Received Facsimile.
#
SPOOL=/usr/spool/fax
AWK=nawk

PATH=/bin:/usr/bin:/etc
test -d /usr/ucb  && PATH=$PATH:/usr/ucb		# Sun and others
test -d /usr/bsd  && PATH=$PATH:/usr/bsd		# Silicon Graphics
test -d /usr/5bin && PATH=/usr/5bin:$PATH:/usr/etc	# Sun and others
test -d /usr/sbin && PATH=/usr/sbin:$PATH		# 4.4BSD-derived

FILES=
SORTKEY=-sender

while [ x"$1" != x"" ] ; do
    case $1 in
    -send*|-csi|-dest*|-speed|-rate|-format)
	    SORTKEY=$1;;
    -*)	    echo "Usage: $0 [-sortkey] [files]"; exit 1;;
    *)	    FILES="$FILES $1";;
    esac
    shift
done
if [ -z "$FILES" ]; then
    FILES=$SPOOL/etc/xferlog
fi

#
# Construct awk rules to collect information according
# to the desired sort key.  There are two rules for
# each; to deal with the two different formats that
# have existed over time.
#
case $SORTKEY in
-send*|-csi)
    AWKRULES='$2 == "RECV" && NF == 9  { acct($3, $7, $8, $5, $6, $9); }
     	      $2 == "RECV" && NF == 11 { acct($6, $9, $10, $7, $8, $11); }'
    ;;
-dest*)
    AWKRULES='$2 == "RECV" && NF == 9  { acct($4, $7, $8, $5, $6, $9); }
    	      $2 == "RECV" && NF == 11 { acct($5, $9, $10, $7, $8, $11); }'
    ;;
-speed|-rate)
    AWKRULES='$2 == "RECV" && NF == 9  { acct($5, $7, $8, $5, $6, $9); }
    	      $2 == "RECV" && NF == 11 { acct($7, $9, $10, $7, $8, $11); }'
    ;;
-format)
    AWKRULES='$2 == "RECV" && NF == 9  { acct($6, $7, $8, $5, $6, $9); }
    	      $2 == "RECV" && NF == 11 { acct($8, $9, $10, $7, $8, $11); }'
    ;;
esac

#
# Generate an awk program to process the statistics file.
#
tmpAwk=/tmp/xfer$$
trap "rm -f $tmpAwk; exit 1" 0 1 2 15

(cat<<'EOF'
#
# Convert hh:mm:ss to minutes.
#
func cvtTime(s)
{
    t = 0;
    for (n = split(s, a, ":"); n > 1; n--)
	t = t*60 + a[n];
    if (n > 0)
	t += t/60;
    return t;
}

func setupDigits()
{
  digits[0] = "0"; digits[1] = "1"; digits[2] = "2";
  digits[3] = "3"; digits[4] = "4"; digits[5] = "5";
  digits[6] = "6"; digits[7] = "7"; digits[8] = "8";
  digits[9] = "9";
}

#
# Format seconds as hh:mm:ss.
#
func fmtTime(t)
{
    v = int(t/3600);
    result = "";
    if (v > 0) {
	if (v >= 10)
	    result = digits[int(v/10)];
	result = result digits[int(v%10)] ":";
	t -= v*3600;
    }
    v = int(t/60);
    if (v >= 10 || result != "")
	result = result digits[int(v/10)];
    result = result digits[int(v%10)];
    t -= v*60;
    return (result ":" digits[int(t/10)] digits[int(t%10)]);
}

#
# Setup a map for histogram calculations.
#
func setupMap(s, map)
{
    n = split(s, a, ":");
    for (i = 1; i <= n; i++)
	map[a[i]] = i;
}

#
# Add pages to a histogram.
#
func addToMap(key, ix, pages, map)
{
    if (key == "") {
	for (i in map)
	    key = key ":";
    }
    n = split(key, a, ":");
    a[map[ix]] += pages;
    t = a[1];
    for (i = 2; i <= n; i++)
      t = t ":" a[i];
    return t;
}

#
# Return the name of the item with the
# largest number of accumulated pages.
#
func bestInMap(totals, map)
{
   n = split(totals, a, ":");
   imax = 1; max = -1;
   for (j = 1; j <= n; j++)
       if (a[j] > max) {
	   max = a[j];
	   imax = j;
       }
   split(map, a, ":");
   return a[imax];
}

#
# Sort array a[l..r]
#
function qsort(a, l, r) {
    i = l;
    k = r+1;
    item = a[l];
    for (;;) {
	while (i < r) {
            i++;
	    if (a[i] >= item)
		break;
        }
	while (k > l) {
            k--;
	    if (a[k] <= item)
		break;
        }
        if (i >= k)
	    break;
	t = a[i]; a[i] = a[k]; a[k] = t;
    }
    t = a[l]; a[l] = a[k]; a[k] = t;
    if (k != 0 && l < k-1)
	qsort(a, l, k-1);
    if (k+1 < r)
	qsort(a, k+1, r);
}

#
# Accumulate a statistics record.
#
func acct(key, pages, time, br, df, status)
{
    gsub("\"", "", key);
    gsub("^ +", "", key);
    gsub(" +$", "", key);
    recvpages[key] += pages;
    if (pages == 0 && time > 60)
	time = 0;
    recvtime[key] += time;
    gsub("\"", "", status);
    if (status != "")
	recverrs[key]++;
    gsub("\"", "", br);
    recvrate[key] = addToMap(recvrate[key], br, pages, rateMap);
    gsub("\"", "", df);
    recvdata[key] = addToMap(recvdata[key], df, pages, dataMap);
}

#
# Print a rule between the stats and the totals line.
#
func printRule(n, s)
{
    r = "";
    while (n-- >= 0)
	r = r s;
    printf "%s\n", r;
}

BEGIN		{ FS="\t";
		  rates = "2400:4800:7200:9600:12000:14400";
		  setupMap(rates, rateMap);
		  datas = "1-D MR:2-D MR:2-D Uncompressed Mode:2-D MMR";
		  setupMap(datas, dataMap);
		}
END		{ OFS="\t"; setupDigits();
		  maxlen = 15;
		  nsorted = 0;
		  for (i in recvpages) {
		      l = length(i);
		      if (l > maxlen)
			maxlen = l;
		      sorted[nsorted++] = i;
		  }
		  qsort(sorted, 0, nsorted-1);
		  fmt = "%-" maxlen "." maxlen "s";	# e.g. %-24.24s
		  printf fmt " %5s %8s %6s %4s %7s %7s\n",
		      "Sender", "Pages", "Time", "Pg/min",
		      "Errs", "TypRate", "TypData";
		  tpages = 0;
		  ttime = 0;
		  terrs = 0;
		  for (k = 0; k < nsorted; k++) {
		      i = sorted[k];
		      t = recvtime[i]; if (t == 0) t = 1;
		      n = recvpages[i]; if (n == 0) n = 1;
		      brate = best
		      printf fmt " %5d %8s %6.1f %4d %7d %7s\n",
			  i, recvpages[i], fmtTime(recvtime[i]*60),
			  recvpages[i] / t, recverrs[i],
			  bestInMap(recvrate[i], rates),
			  bestInMap(recvdata[i], datas);
			tpages += recvpages[i];
			ttime += recvtime[i];
			terrs += recverrs[i];
		  }
		  printRule(maxlen+1+5+1+8+6+1+4+1+7+1+7, "-");
		  t = ttime; if (t == 0) t = 1;
		  printf fmt " %5d %8s %6.1f %4d\n",
		      "Total", tpages, fmtTime(ttime*60), tpages/t, terrs;
		}
EOF
echo "$AWKRULES"
)>$tmpAwk
$AWK -f $tmpAwk $FILES
EOF-EOF-EOF
RmFile util/xfer.awk
RmFile util/xferdest.awk
NewFile util/xferstats.sh<<'EOF-EOF-EOF'
#! /bin/sh
#	$Header: /usr/people/sam/fax/util/RCS/xferstats.sh,v 1.2 93/07/19 11:56:59 sam Exp $
#
# FlexFAX Facsimile Software
#
# Copyright (c) 1990, 1991, 1992, 1993 Sam Leffler
# Copyright (c) 1991, 1992, 1993 Silicon Graphics, Inc.
# 
# Permission to use, copy, modify, distribute, and sell this software and 
# its documentation for any purpose is hereby granted without fee, provided
# that (i) the above copyright notices and this permission notice appear in
# all copies of the software and related documentation, and (ii) the names of
# Sam Leffler and Silicon Graphics may not be used in any advertising or
# publicity relating to the software without the specific, prior written
# permission of Sam Leffler and Silicon Graphics.
# 
# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
# 
# IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
# ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
# LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
# OF THIS SOFTWARE.
#

#
# Print Statistics about Transmitted Facsimile.
#
SPOOL=/usr/spool/fax
AWK=nawk

PATH=/bin:/usr/bin:/etc
test -d /usr/ucb  && PATH=$PATH:/usr/ucb		# Sun and others
test -d /usr/bsd  && PATH=$PATH:/usr/bsd		# Silicon Graphics
test -d /usr/5bin && PATH=/usr/5bin:$PATH:/usr/etc	# Sun and others
test -d /usr/sbin && PATH=/usr/sbin:$PATH		# 4.4BSD-derived

FILES=
SORTKEY=-sender

while [ x"$1" != x"" ] ; do
    case $1 in
    -send*|-csi|-dest*|-speed|-rate|-format)
	    SORTKEY=$1;;
    -*)	    echo "Usage: $0 [-sortkey] [files]"; exit 1;;
    *)	    FILES="$FILES $1";;
    esac
    shift
done
if [ -z "$FILES" ]; then
    FILES=$SPOOL/etc/xferlog
fi

#
# Construct awk rules to collect information according
# to the desired sort key.  There are two rules for
# each; to deal with the two different formats that
# have existed over time.
#
case $SORTKEY in
-send*)
    AWKRULES='$2 == "SEND" && NF == 9  { acct($3, $7, $8, $5, $6, $9); }
     	      $2 == "SEND" && NF == 11 { acct($4, $9, $10, $7, $8, $11); }'
    ;;
-csi)
    AWKRULES='$2 == "SEND" && NF == 9  { acct($4, $7, $8, $5, $6, $9); }
    	      $2 == "SEND" && NF == 11 { acct($6, $9, $10, $7, $8, $11); }'
    ;;
-dest*)
    AWKRULES='$2 == "SEND" && NF == 9  { acct($4, $7, $8, $5, $6, $9); }
    	      $2 == "SEND" && NF == 11 { acct($5, $9, $10, $7, $8, $11); }'
    ;;
-speed|-rate)
    AWKRULES='$2 == "SEND" && NF == 9  { acct($5, $7, $8, $5, $6, $9); }
    	      $2 == "SEND" && NF == 11 { acct($7, $9, $10, $7, $8, $11); }'
    ;;
-format)
    AWKRULES='$2 == "SEND" && NF == 9  { acct($6, $7, $8, $5, $6, $9); }
    	      $2 == "SEND" && NF == 11 { acct($8, $9, $10, $7, $8, $11); }'
    ;;
esac

#
# Generate an awk program to process the statistics file.
#
tmpAwk=/tmp/xfer$$
trap "rm -f $tmpAwk; exit 1" 0 1 2 15

(cat<<'EOF'
#
# Convert hh:mm:ss to minutes.
#
func cvtTime(s)
{
    t = 0;
    for (n = split(s, a, ":"); n > 1; n--)
	t = t*60 + a[n];
    if (n > 0)
	t += t/60;
    return t;
}

func setupDigits()
{
  digits[0] = "0"; digits[1] = "1"; digits[2] = "2";
  digits[3] = "3"; digits[4] = "4"; digits[5] = "5";
  digits[6] = "6"; digits[7] = "7"; digits[8] = "8";
  digits[9] = "9";
}

#
# Format seconds as hh:mm:ss.
#
func fmtTime(t)
{
    v = int(t/3600);
    result = "";
    if (v > 0) {
	if (v >= 10)
	    result = digits[int(v/10)];
	result = result digits[int(v%10)] ":";
	t -= v*3600;
    }
    v = int(t/60);
    if (v >= 10 || result != "")
	result = result digits[int(v/10)];
    result = result digits[int(v%10)];
    t -= v*60;
    return (result ":" digits[int(t/10)] digits[int(t%10)]);
}

#
# Setup a map for histogram calculations.
#
func setupMap(s, map)
{
    n = split(s, a, ":");
    for (i = 1; i <= n; i++)
	map[a[i]] = i;
}

#
# Add pages to a histogram.
#
func addToMap(key, ix, pages, map)
{
    if (key == "") {
	for (i in map)
	    key = key ":";
    }
    n = split(key, a, ":");
    a[map[ix]] += pages;
    t = a[1];
    for (i = 2; i <= n; i++)
      t = t ":" a[i];
    return t;
}

#
# Return the name of the item with the
# largest number of accumulated pages.
#
func bestInMap(totals, map)
{
   n = split(totals, a, ":");
   imax = 1; max = -1;
   for (j = 1; j <= n; j++)
       if (a[j] > max) {
	   max = a[j];
	   imax = j;
       }
   split(map, a, ":");
   return a[imax];
}

#
# Sort array a[l..r]
#
function qsort(a, l, r) {
    i = l;
    k = r+1;
    item = a[l];
    for (;;) {
	while (i < r) {
            i++;
	    if (a[i] >= item)
		break;
        }
	while (k > l) {
            k--;
	    if (a[k] <= item)
		break;
        }
        if (i >= k)
	    break;
	t = a[i]; a[i] = a[k]; a[k] = t;
    }
    t = a[l]; a[l] = a[k]; a[k] = t;
    if (k != 0 && l < k-1)
	qsort(a, l, k-1);
    if (k+1 < r)
	qsort(a, k+1, r);
}

#
# Accumulate a statistics record.
#
func acct(key, pages, time, br, df, status)
{
    gsub("\"", "", key);
    gsub("^ +", "", key);
    gsub(" +$", "", key);
    sendpages[key] += pages;
    if (pages == 0 && time > 60)
	time = 0;
    sendtime[key] += time;
    gsub("\"", "", status);
    if (status != "")
	senderrs[key]++;
    gsub("\"", "", br);
    sendrate[key] = addToMap(sendrate[key], br, pages, rateMap);
    gsub("\"", "", df);
    senddata[key] = addToMap(senddata[key], df, pages, dataMap);
}

#
# Print a rule between the stats and the totals line.
#
func printRule(n, s)
{
    r = "";
    while (n-- >= 0)
	r = r s;
    printf "%s\n", r;
}

BEGIN		{ FS="\t";
		  rates = "2400:4800:7200:9600:12000:14400";
		  setupMap(rates, rateMap);
		  datas = "1-D MR:2-D MR:2-D Uncompressed Mode:2-D MMR";
		  setupMap(datas, dataMap);
		}
END		{ OFS="\t"; setupDigits();
		  maxlen = 15;
		  nsorted = 0;
		  for (i in sendpages) {
		      l = length(i);
		      if (l > maxlen)
			maxlen = l;
		      sorted[nsorted++] = i;
		  }
		  qsort(sorted, 0, nsorted-1);
		  fmt = "%-" maxlen "." maxlen "s";	# e.g. %-24.24s
		  printf fmt " %5s %8s %6s %4s %7s %7s\n",
		      "Destination", "Pages", "Time", "Pg/min",
		      "Errs", "TypRate", "TypData";
		  tpages = 0;
		  ttime = 0;
		  terrs = 0;
		  for (k = 0; k < nsorted; k++) {
		      i = sorted[k];
		      t = sendtime[i]; if (t == 0) t = 1;
		      n = sendpages[i]; if (n == 0) n = 1;
		      brate = best
		      printf fmt " %5d %8s %6.1f %4d %7d %7.7s\n",
			  i, sendpages[i], fmtTime(sendtime[i]*60),
			  sendpages[i] / t, senderrs[i],
			  bestInMap(sendrate[i], rates),
			  bestInMap(senddata[i], datas);
			tpages += sendpages[i];
			ttime += sendtime[i];
			terrs += senderrs[i];
		  }
		  printRule(maxlen+1+5+1+8+6+1+4+1+7+1+7, "-");
		  t = ttime; if (t == 0) t = 1;
		  printf fmt " %5d %8s %6.1f %4d\n",
		      "Total", tpages, fmtTime(ttime*60), tpages/t, terrs;
		}
EOF
echo "$AWKRULES"
)>$tmpAwk
$AWK -f $tmpAwk $FILES
EOF-EOF-EOF
