#ifndef version
#define version rcsid
#endif

/*
 * Enhanced dd, Double bufferrd.
 * 
 * (C) Copyright 1989 SAITOH,Akinori saitoh@osaka-u.ac.jp 
 *
 * Permit anyone to use, modify, re-distribute this software.
 * $B$3$N%=%U%H%&%'%"$r:#8eL54|8B$K<+M3$KMxMQ!"2~B$!":FG[I[$9$k;v$r5v$7$^(B
 * $B$9!#(B
 * $B$3$N%=%U%H%&%'%"$O!"(BAT&T System V $B5Z$S(BUCB 4.3BSD $B$N%=!<%9%3!<%I%i%$%;(B
 * $B%s%9$r<hF@$7$F$$$k7W;;5!$r;H$C$F3+H/$7$^$7$?!#$7$+$7!"$3$N%=%U%H%&%'(B
 * $B%"$O!"(BAT&T ,BSD $B$K8B$i$:!"B><T$NCx:n8"$,5Z$V%=!<%9%W%m%0%i%`$OMxMQ$7(B
 * $B$F$$$^$;$s!#(B
 * ver 2.0  Sat Jan  6  1990
 * correct termination protocol to shut up illegal error message
 * ver 2.1  Tue Jan  9 1990
 * Bug fix
 * Ver 2.2 Thu Apr 12 1990
 * System V port
 * On BSD or SUN OS: cc edd.c -o edd
 * On System V : cc -DSYSV -DNBPG=4096 edd.c -o edd.c 
 * 4096 should be changed to your virtual memory page size
 * ver 2.3 Jun  24 1990; bug(hung on abort) fix + message change
 * ver 2.4 Jul 4   1990; add bytes report;
 * ver 2.5 Jul 5   1990; fake SUN rst media end
 * ver 2.6 Jul 20  1990; add padunit option
 * ver 2.7 Nov  6  1990; multivolume bug fix, protocol change
 * ver 3.1 Nov 7  1991; change pad flag, Socket buffer size, on err exit
 * ver 3.3 Dec 22 1992; Add help message, etc.
 * ver 3.5 Jun  1 1993; can handle LONG medium. ~ 4GB max.
 *			Flush stderr.
 *  
 */
static char    *copyright[] = {
	"(C) Copyright 1989 SAITOH,Akinori saitoh@ics.osaka-u.ac.jp\n",
"Permit anyone to use, modify, re-distribute this software.\n"};

static char rcsid[]= "$Header: /usr/home/madoka/saitoh/src/edd/edd.c,v 3.6 93/07/02 18:54:54 saitoh Exp $";

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#ifdef SYSV
#include <fcntl.h>
#include <string.h>
#else
#include <sys/file.h>
#include <strings.h>
#endif
#include <sys/errno.h>
extern int      errno;


usage(s)
	char           *s;
{
	fputs(s, stderr);
	fprintf(stderr, "edd version %s\n", version);
	fputs(
"edd [if=file] [of=file] [ibs=inblk] [obs=outblk] [bs=bufsiz] \\\n\
\t[pad[=subopt,subopt,,]] [rewind] [volume=size] [concat=n] \\\n\
\t[isbs=isockbuf] [osbs=osockbuf]\n",
	      stderr);
	fputs("Obs must be a multiple of ibs.\n", stderr);
	xerror(s);
}

extern char    *valloc();	/* page aligned alloc */
char           *databuf;
int             infd = 0, outfd = 1, volcount = 0, involcount = 0, notty;
int             pipein = -1, pipeout = -1, parent = -1;
FILE           *ftty;
unsigned long	inbytes = 0, outbytes = 0;
unsigned long	inbytes_total = 0, outbytes_total = 0;


#define WHOAMI (parent ? "parent" : "child")
#define IN_OK 'I'
#define OUT_OK 'O'
#define EOF_APPEAR 'E'

#define ISBS_DEFAULT	8192
#define OSBS_DEFAULT	8192
#define ISBS_MAX	32768
#define OSBS_MAX	32768
#define MAXEPIPE	3

long            obs = 0, ibs = 0, bs = 0, isbs=0, osbs=0;
int             concat = 1, verbose = 0, noss = 0, dofsync=0;
unsigned long   volume = 0;
int             bunit;
char           *infile = NULL;
char           *outfile = NULL;
char           *inproc = NULL;
char           *outproc = NULL;
int             rewindtape = 0;
int             padunit = 0;
int             padmode = 0;
#define PAD_NORMAL	1
#define PAD_TAR	2


#define INFILE ((infile==NULL)?"stdin":infile)
#define OUTFILE ((outfile==NULL)?"stdout":outfile)

#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif

char *numparse();
int setpad();
int help();

struct {
	char           *name;
	enum {
		NUM, STR, BOOL,SPL
	}               attrib;
	char           *objptr;
	char           *msg;
}               options[] = {
	{ "help", SPL, (char *) help, "display help message"}, 
	{ "-help", SPL, (char *) help, "display help message"}, 
	{ "of", STR, (char *) &outfile, "output file"},
	{ "if", STR, (char *) &infile, "input file"},
	{ "op", STR, (char *) &outproc, "output to process"},
	{ "ip", STR, (char *) &inproc, "input from process"},
	{ "obs", NUM, (char *) &obs, "output block size"},
	{ "ibs", NUM, (char *) &ibs, "input block size"},
	{ "osbs", NUM, (char *) &osbs, "output socket buffer size"},
	{ "isbs", NUM, (char *) &isbs, "input socket buffer size"},
	{ "bs", NUM, (char *) &bs, "buffer size"},
	{ "concat", NUM, (char *) &concat, "concat # input volume"},
	{ "volume", NUM, (char *) &volume, "volume size"},
	{ "verbose", BOOL, (char *) &verbose, ""},
	{ "noss", BOOL, (char *) &noss, "NO Slow Start"},
	{ "fsync", BOOL, (char *) &dofsync, "fsync on each write"},
	{ "rewind", BOOL, (char *) &rewindtape, "rewind input device at start and end"},
	{ "pad", SPL, (char *) setpad, "padding mode. pad, pad=2b"}, 
	{ "padunit", NUM, (char *) &padunit, "for backward compatibility"},
	{ "", NUM, NULL, ""},
};


char           *progname;

main(argc, argv)
	int             argc;
	char           *argv[];
{
	char           *s;
	int             inputeof;

	progname = argv[0];
	while ((s = index(progname, '/')) != NULL)
		progname = s + 1;
	if (argc < 2)
		usage("");
	notty = 0;
	setbuf(stderr, NULL);
	if ((ftty = fopen("/dev/tty", "r")) == NULL) {
		notty = 1;
		if ((ftty = fopen("/dev/null", "r")) == NULL) {
			perror("/dev/null");
			xerror("/dev/null open failed\n");
		}
	} else if (freopen("/dev/tty", "w", stderr) == NULL) {
		perror("/dev/tty");
		xerror("control tty open failed\n");
	}
	argparse(argc, argv);
	optioncheck();
	if (volume == 0) {
		volume = (~(unsigned)0) / bs;
		volume *= bs;
	}
	if ((databuf = valloc((unsigned) bs)) == NULL) {
		xerror("can not alloc buffer\n");
	}
	if (verbose) {
		fprintf(stderr, "ibs=%db obs=%db bs=%db\n",
			ibs / 512, obs / 512, bs / 512);
	}
	signal(SIGPIPE, SIG_IGN);
	volcount = 1;
	involcount = 1;
	openinput();
	if (infd < 0)
		xerror("input open failed\n");
	if (outfile != NULL)
		outfd = -1;
	else
		outfd = openoutput();
	
	for (inputeof = 0; concat > 0;) {
		register int    nin, nout;
		register unsigned long ebs;
		register int    last;

		if (infd < 0) {
			involcount++;
			openinput();
			inbytes = 0;
			inputeof = 0;
			if (infd < 0)
				xerror("input open failed\n");
		}
		if (outfd < 0) {/* end of out volume */
			openoutput();
			outbytes = 0;
		}
		setup_pipe();
		if(!parent)
			inbytes=outbytes=0;
		bzero(databuf, bs);
		if (noss)
			ebs = bs;
		else {
			for (ebs = bunit; ebs < bs / 16; ebs *= 2);
		}
		for (last = 0; !last;) {
			if (ebs > bs)
				ebs = bs;
			if (parent) {
				if (outbytes + ebs * 2 > volume/2)
					last++;
			} 
			if (get_token(IN_OK, EOF_APPEAR) == EOF_APPEAR) {
				nin = 0;
				if (!parent) 
					goto child_doexit;
					/* wait child write complete */
				(void) get_token(OUT_OK, OUT_OK);
				inputeof++;
				break;
			}
			nin = fillbuffer(infd, databuf, ebs, ibs);
			if (nin < ebs) {
				inputeof++;
if (verbose) {
fprintf(stderr,"%s:%s:nin=%d,ebs=%d,inputeof++\n", progname,
	WHOAMI, nin,ebs);
}
			}
			if (nin < 0) {
				if (inbytes != 0)
					xerror("read error\n");
				/* fake eof */
				put_token(EOF_APPEAR, 0);
				goto read_error_at_top;
			}
			inbytes += nin;
			if (nin == 0 && inbytes == 0 && inproc != NULL) {
				fprintf(stderr,
					"%s:%s:Got null input from program\n", progname,
					progname);
				/* fake eof */
				put_token(EOF_APPEAR, 0);
				goto read_error_at_top;
			}
			if (verbose) {
				fprintf(stderr,"%s:%s:got %d bytes\n", progname,
					WHOAMI, nin);
			}
			if (nin == ebs && !last){	/* not outvolume end */
				put_token(IN_OK, 0);
			}else {
				put_token(EOF_APPEAR, 0);
			}

			if (nin % obs) {
				bzero(databuf + nin, obs - (nin % obs));
			}
			(void) get_token(OUT_OK, OUT_OK);
			if(nin==0)
				nout=0;
			else {
				nout = ewrite(outfd, databuf, nin, obs);
				if (nout < 0) {
					xerror("aborting\n");
				}
			}
			if (verbose) {
				fprintf(stderr, "%s:%s:wrote %d bytes\n",
					progname, WHOAMI, nout);
				fflush(stderr);
			}
			outbytes += nout;
			if (! inputeof && !last || !parent)
				put_token(OUT_OK, 0);
			if (inputeof)
				break;
			if (nout > nin) {	/* padding can occur only  on
						 * end */
				break;
			}
			ebs *= 2;
		}
		if (!parent) {
	child_doexit:
			put_int(inbytes);
			put_int(outbytes);
			child_exit(0);
			/* never return */
		}
		inbytes += get_int();
		outbytes += get_int();
		if (inputeof) {	/* input eof */
			inbytes_total += inbytes;
			inbytes = 0;
			closeinput();
			infd = -1;
			concat--;
		}
		if (last || concat <= 0) {	/* end of out volume */
			outbytes_total += outbytes;
			outbytes = 0;
			closeoutput();
			outfd = -1;
			volcount++;
		}
		disconnect_child();
		continue;

read_error_at_top:
		disconnect_child();
		reopeninput();
		inbytes = 0;
	}
	close(pipein);
	close(pipeout);
	if (ftty != NULL) {
		fprintf(stderr, "%d blks+%d bytes in (%d KB)\n",
			inbytes_total / ibs, inbytes_total % ibs
			, (inbytes_total+1023) / 1024);
		fprintf(stderr, "%d blks+%d bytes out (%d KB)\n",
			outbytes_total / obs, outbytes_total % obs
			, (outbytes_total+1023) / 1024);
		fflush(stderr);
	}
	if (parent)
		wait(0);
	exit(0);
}

help()
{
	char  *pa, *po;
	int    i;

	fprintf(stderr, "option parameters for edd\n");
	for (i = 0; options[i].objptr != NULL; i++)
		fprintf(stderr,"%s\t: %s\n", options[i].name, options[i].msg);
	exit(1);
}


disconnect_child()
{
	put_token(EOF_APPEAR, 0);
	close(pipein);
	close(pipeout);
	{
		int             st;
		while ((st = wait(0)) > 0 && st != parent);
	}
}

child_exit(e)
{
	if (verbose)
		fprintf(stderr, "%s:child pid=%d: exiting\n", progname, getpid());
	/* wait parent done */
	if (get_token(EOF_APPEAR, -1) == EOF_APPEAR)
		(void) get_token(EOF_APPEAR, -1);
	if (verbose)
		fprintf(stderr, "%s:child pid=%d: exit\n", progname, getpid());
	exit(e);
}

optioncheck()
{

	if (inproc != NULL) {
		infile = inproc;
	}
	if ((infile == NULL) && (concat != 1)) {
		xerror("Can not concat stdin");
	}
	if (outproc != NULL) {
		outfile = outproc;
	}
	if ((outfile == NULL) && (volume != 0)) {
		xerror("Can not split stdout");
	}
	if (concat < 1)
		xerror("Illegai concat value");
	if (bs <= 0) {
		bs = MAX(ibs, obs);
	}
	if (ibs <= 0) {
		ibs = obs > 0 ? obs : bs;
	}
	if (obs <= 0) {
		obs = ibs > 0 ? ibs : bs;
	}
	if (bs <= 0 || bs < obs || bs < ibs)
		usage("Illegal buffersize\n");
	if (ibs <= 0)
		usage("illegal ibs\n");
	if (obs <= 0)
		usage("illegal obs\n");
	{
		int             x, y;

		x = ibs;
		y = obs;
		while (x != y)
			if (x > y)
				x -= y;
			else
				y -= x;
		bunit = (ibs / x) * obs;
	}
	if (bs % ibs || bs % obs) {
		bs = ((bs / bunit) + 1) * bunit;
		fprintf(stderr, "%s:Buffer size rounded up to %d\n", progname, bs);
	}
	if (bs % ibs) {
		usage("bs bust be a multiple of ibs\n");
	}
	if (bs % obs) {
		usage("bs bust be a multiple of obs\n");
	}
	if (padmode && padunit == 0)
		padunit = obs;
	if(isbs==0 ) {
		if(ibs <= ISBS_MAX)
			isbs=ibs;
		else
			isbs=ISBS_DEFAULT;
	}
	if(osbs==0 ) {
		if(obs <= OSBS_MAX)
			osbs=obs;
		else
			osbs=OSBS_DEFAULT;
	}
}

/*
 * read(infd) (ibs * count)bytes try to get ibs bytes on each read except
 * last flagment.
 * 
 * return:	read BYTES if no error -1 on read error
 */
fillbuffer(infd, buf, bytes, ibs)
	char           *buf;
	register        bytes, ibs;
{
	register char  *bufp;
	register int    n=0;

	for (bufp = buf; bytes > 0;) {
		n = read(infd, bufp, bytes < ibs ? bytes : ibs);
		if (n < 0) {
			char            msg[BUFSIZ];
			sprintf(msg, "%s:%s read %s", progname, WHOAMI, INFILE);
			perror(msg);
			break;
		}
		if (n == 0)
			break;
		bufp += n;
		bytes -= n;
	}
	if (n >= 0 || buf != bufp)
		return (bufp - buf);
	else
		return -1;
}

closeinput()
{
	if (verbose) {
		fprintf(stderr, "%s:%s:closing input\n", progname, parent ? "parent" : "child");
		fflush(stderr);
	}
	if (infile == NULL)
		return;
	if (close(infd) < 0 && parent)
		perror("close");
	if (inproc != NULL) {
		int             st;
		st = wait(0);
	}
	if (!rewindtape || !parent)
		return;
	if ((infd = open(infile, O_RDONLY)) >= 0)
		close(infd);
}

closeoutput()
{
	if (verbose) {
		fprintf(stderr, "%s:%s:closing output\n",
			progname, parent ? "parent" : "child");
		fflush(stderr);
	}
	if (outfile == NULL)
		return;
	if (close(outfd) < 0 && parent)
		perror("close");
	if (!rewindtape || !parent)
		return;
	if ((outfd = open(outfile, O_RDONLY)) >= 0)
		close(outfd);
}

reopenoutput()
{
	closeoutput();
	fprintf(stderr, "%s:write error on the top of media\n", progname);
	fprintf(stderr, "%s:remount and try again\n", progname);
	return (openoutput());

}

reopeninput()
{
	closeinput();
	fprintf(stderr, "%s:read error on top of media\n", progname);
	fprintf(stderr, "%s:remount and try again\n", progname);
	return (openinput());

}

askyes(msg)
	char           *msg;
{
	char            s[BUFSIZ];
	if (notty)
		return -1;
	for (;;) {
		fprintf(stderr, "%s\n", msg);
		fprintf(stderr, "OK? (y/n/q)");
		fflush(stderr);
		if (s != fgets(s, BUFSIZ - 1, ftty)) {
			perror("control tty read");
			xerror("job aborting");
		}
		if (*s == 'y' || *s == 'Y')
			return 0;
		if (*s == 'q' || *s == 'Q') {
			if (parent > 0)	/* kill child */
				kill(parent, SIGKILL);
			xerror("abort\n");
		}
	}
}

openoutput()
{
	char            s[BUFSIZ];

	if (outfile == NULL) {
		setsockbuf(1, SO_SNDBUF, osbs);
		return 1;
	}
	sprintf(s, "%s:mount %d%s volume on %s\n", progname
		,volcount
		,volcount < 3 ? (volcount == 1 ? "st" : "nd")
		: (volcount == 3 ? "rd" : "th"), outfile);
	if (!notty && volcount > 1)
		askyes(s);
	for (;; askyes(s)) {
		if (rewindtape) {
			if ((outfd = open(outfile, O_RDWR)) > 0)
				close(outfd);
			else {
				perror(outfile);
				if (!notty)
					continue;
			}
		}
		if (outproc != NULL)
			outfd = procopen(outproc, "w");
		else
			outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0777);
		if (outfd < 0) {
			perror(outfile);
		} else {
			return outfd;
		}
		if (notty)
			xerror("job aborted\n");
	}
}

procopen(prog, rw)
	char           *prog;
	char           *rw;
{
	int             p[2];
	int             pid;

	if (pipe(p) < 0)
		return -1;
	if ((pid = fork()) < 0)
		return -1;
	if (pid == 0) {
		if (*rw == 'w') {
			close(0);
			dup(p[0]);
			close(p[1]);
			close(p[0]);
			close(1);
			open("/dev/null", O_WRONLY);
		} else {
			close(1);
			dup(p[1]);
			close(p[1]);
			close(p[0]);
			close(0);
			open("/dev/null", 0);
		}
		execl("/bin/sh", "sh", "-c", prog, 0);
		perror("exec");
		exit(1);
	}
	if (*rw == 'w') {
		close(p[0]);
		setsockbuf(p[1], SO_SNDBUF, osbs);
		return (p[1]);
	} else {
		close(p[1]);
		setsockbuf(p[0], SO_RCVBUF, isbs);
		return (p[0]);
	}
}

setup_pipe()
{
	int             p1[2], p2[2];
	if (pipe(p1) < 0 || pipe(p2) < 0) {
		perror("pipe");
		xerror("");
	}
	if ((parent = fork()) < 0) {
		perror("fork");
		xerror("");
	}
	if (parent) {
		if (verbose)
			fprintf(stderr, "%s:forked child %d\n", progname, parent);
		close(p1[0]);
		pipeout = p1[1];
		pipein = p2[0];
		close(p2[1]);
	} else {
		static char     s[2] = {IN_OK, OUT_OK};
		fclose(ftty);
		if (!notty) {
			freopen("/dev/tty", "w", stderr);
		}
		pipein = p1[0];
		close(p1[1]);
		close(p2[0]);
		pipeout = p2[1];
		if (write(pipeout, s, 2) != 2) {
			perror("child cntrl pipe write");
			xerror("");
		}
	}
}

openinput()
{
	char            s[BUFSIZ];

	if (infile == NULL)  {
		setsockbuf(0, SO_RCVBUF, isbs);
		return (0);
	}
	sprintf(s, inproc == NULL ?
	     "%s:mount %d%s volume on %s\n" : "%s:%d%s invocation of '%s'\n"
		,progname
		,involcount
		,involcount < 3 ? (involcount == 1 ? "st" : "nd")
		: (involcount == 3 ? "rd" : "th")
		,infile);
	if (!notty && involcount > 1)
		askyes(s);
	for (;; askyes(s)) {
		if (rewindtape) {
			if ((infd = open(infile, O_RDONLY)) < 0) {
				perror(infile);
				if (notty)
					break;
				else
					continue;
			}
			close(infd);
		}
		if (inproc != NULL)
			infd = procopen(inproc, "r");
		else
			infd = open(infile, O_RDONLY);
		if (infd > 0)
			break;
		perror(infile);
		if (notty)
			break;
	}
	if (infd < 0)
		xerror("job aborted\n");
	return (infd);
}

sizeparse(str)
{
	int i;
	char *p;
		char            buf[256];

	if((p=numparse(str, &i))!=NULL && *p=='\0')
		return i;


	(void) sprintf(buf, "%s:Unparsable number:%s\n", progname, str);
	xerror(buf);
	return (-1);		/* to shut up LINT */
}

char *numparse(str, np)
	char           *str;
	int *np;
{
	register char  *s = str;
	register int    i = 0;

	if (!isdigit(*s)) {
		return NULL;
	}
	while (isdigit(*s))
		i = i * 10 + *s++ - '0';
	if (*s != '\0') {
		switch (*s++) {
		case 'b':
		case 'B':
			i *= 512;
			break;
		case 'k':
		case 'K':
			i *= 1024;
			break;
		case 'm':
		case 'M':
			i *= 1024 * 1024;
			break;
		case '+':
			break;
		default:
			break;
		}
	}
	if (*s == '+') {
		int j;
		char *p;

		p=  numparse(s + 1, &j);
		*np= i+j;
		return s;
	}

	*np = i;
	return (s);
}

setpad(opt)
	char *opt;
{
	if(opt==NULL || *opt=='\0') {
		padmode= PAD_NORMAL;
		return 0;
	}
	do {
		if (strncmp(opt,"tar",3)==0) { 
			padmode= PAD_TAR;
			opt+=3;
			continue;
		} 
		if (isdigit(*opt)) {
			int i;
			if( (opt =  numparse(opt , &i)) !=NULL) {
				padunit = i;
				continue;
			}
		}
		else
		xerror("Unknown pad sub-opion\n");
	} while( *opt==',' && opt++);
	return 0;
}

xerror(errstr)
	char           *errstr;
{
	if (errstr != NULL)
		fputs(errstr, stderr);
	closeinput();
	closeoutput();
	close(pipein);
	close(pipeout);
	close(0);
	close(1);
	if (parent > 0) {
		kill(parent, SIGINT);
		if (ftty != NULL) {
			inbytes_total += inbytes;
			outbytes_total += outbytes;
			fprintf(stderr, "%d blks+%d bytes in\n",
				inbytes_total / ibs, inbytes_total % ibs);
			fprintf(stderr, "%d blks+%d bytes out\n",
				outbytes_total / obs, outbytes_total % obs);
			fflush(stderr);
		}
	}
	alarm(10);	
	wait(0);
	exit(1);
}

argparse(argc, argv)
	int             argc;
	char           *argv[];
{
	register char  *pa, *po;
	register int    i;

	for (argc--, argv++; argc > 0; argc--, argv++) {
		for (i = 0; options[i].objptr != NULL; i++) {
			for (pa = *argv, po = options[i].name;
			     *pa && *po && *pa == *po;
			     pa++, po++);
			if (*po == '\0' && !isalpha(*pa))
				break;
		}
		if (options[i].objptr == NULL) {
			usage("unknown option\n");
		}
		switch (options[i].attrib) {
		case NUM:
			if (*pa != '=')
				xerror("illegal option format\n");
			*(int *) options[i].objptr = sizeparse(pa + 1);
			break;
		case STR:
			if (*pa != '=')
				xerror("illegal option format\n");
			*(char **) options[i].objptr = pa + 1;
			break;
		case BOOL:
			{
				int            *p;
				p = (int *) options[i].objptr;
				if (*pa == '\0')
					*p = !*p;
				else if (*pa == '+')
					*p = 1;
				else if (*pa == '-')
					*p = 0;
				else
					xerror("illegal arg format\n");
			} break;
		case SPL:
			if (*pa == '=' )
				pa++;
			else if ( *pa!='\0')
				xerror("illegal option format\n");
			((int (*)())options[i].objptr)(pa );
			break;
		}
	}
}

setsockbuf(fd, op, size)
{
       while (size > 1024 &&
	      setsockopt(fd, SOL_SOCKET, op, &size, sizeof (size)) < 0)
	      size = (size -1024) & ~ 1023 ;
}

ewrite(fd, buf, cnt, obs)
	register int    fd, obs;
	int             cnt;
	register char  *buf;
{
	register int    rest, n;
	static nEPIPE=0;

	for (rest = cnt; rest > 0; rest -= n, buf += n) {
		if (rest < obs) {
			if (padunit == 0)
				obs = rest;
			else
				obs = rest + padunit - 1 - ((rest - 1) % padunit);
		}
		if ((n = write(fd, buf, obs)) > 0) {
			nEPIPE =0;
			continue;
		}
		if (n == 0) {
			if ((n = write(fd, buf, obs)) > 0)
				continue;
			if (n == 0) {
				fprintf(stderr, "May be tape end\n");
				return (-1);
			}
		}
		if (n < 0) {
			char            msg[BUFSIZ];

			sprintf(msg, "%s write %s", WHOAMI, OUTFILE);
			perror(msg);
			if (errno == EPIPE && ++nEPIPE < MAXEPIPE)
				break;
			return -1;
		}
	}
	if (dofsync)
		fsync(fd);
	return cnt - rest;
}

get_token(t1, t2)
{
	char            c = 0;
	if (verbose) {
		fprintf(stderr, "%s:%s:waiting '%c'\n", progname,
			WHOAMI, t1);
		fflush(stderr);
	}
	if (read(pipein, &c, 1) != 1) {
		char            msg[BUFSIZ];
		if (t2 == -1) {
			if (verbose)
				fprintf(stderr, "%s:%s:got eof\n", progname, WHOAMI);
			return -1;

		}
		sprintf(msg, "%s:%s:control pipe ", progname, WHOAMI);
		perror(msg);
		xerror("");
	} else if (verbose) {
		fprintf(stderr, "%s:%s:got %c\n", progname,
			WHOAMI, c);
	}
	if (c != t1 && c != t2) {
		fprintf(stderr, "%s:%s Syncronization error\n", progname, WHOAMI);
		xerror("");
	}
	return c & 0xFF;
}

get_int()
{
	char            c = 0;
	int i,n;
	char            msg[BUFSIZ];

	for (i=0;(n=read(pipein, &c, 1) )== 1;) {
		if(!isdigit(c))
			break;
		i=i*10 + c-'0';
	}
	if(n<0) {
		sprintf(msg, "%s:%s:control pipe read", progname, WHOAMI);
		perror(msg);
		xerror("");
	} else if (c!=' ') {
		fprintf(stderr, "%s:%s:number terminated with %c\n", progname,
			WHOAMI, c);
	} else if (verbose) {
		fprintf(stderr, "%s:%s:got %d\n", progname,
			WHOAMI, i);
	}
	return i;
}

put_token(t, ignoreerr)
{
	char            c;
	c = t;
	if (verbose) {
		fprintf(stderr, "%s:%s:write '%c' \n", progname,
			WHOAMI, c);
	}
	if (write(pipeout, &c, 1) != 1 && !ignoreerr) {
		char            buf[256];
		sprintf(buf, "%s:Control pipe write", WHOAMI);
		perror(buf);
		xerror("");
	}
}

put_int( num )
{
	char            buf[256];
	if (verbose) {
		fprintf(stderr, "%s:%s:write '%d' \n", progname,
			WHOAMI,  num );
	}
	sprintf(buf,"%d ", num );
	if (write(pipeout, buf, strlen(buf)) != strlen(buf)) {
		sprintf(buf, "%s:Control pipe write", WHOAMI);
		perror(buf);
		xerror("");
	}
}


#ifdef SYSV
#ifndef NBPG
#define NBPG 4096
#endif
char           *
valloc(sz)
	unsigned        sz;
{
	char           *p;

	if ((p = malloc(sz + NBPG)) == NULL)
		return NULL;
	return (char *) (~(NBPG - 1) & (NBPG + (long) p));
}
#endif
#if 0
termination protocol


CASE 1.

parent		child

	OUT_OK ->
	IN_OK  <-
read input
DETECT EOF
	EOF_APPEAR->
			write last-1 output
	OUT_OK <-
write last output
close input & output
			enter termination mode
	inbytes, outbytes <-
close pipe
			SIGPIPE & exit

CASE 2.

parent		child

	OUT_OK  <-
	IN_OK  ->
write last-1 output
		read input
		DETECT EOF
	DETECT_EOF <-
	OUT_OK ->
			write last output
	OUT_OK <-
			enter termination mode
	inbytes, outbytes <-
close input & output
	EOF_APPEAR->
			discarded
close pipe
			& exit

	
#endif
