/*======================================================================
                    L B U F F E R . C 
                    doc: Mon Apr 26 16:11:25 1993
                    dlm: Mon May 17 10:01:05 1993
                    (c) 1993 ant@notylia
                    uE-Info: 66 6 T 0 0 72 2 2 8 ofnI
======================================================================*/

#include	<stdio.h>
#include	<signal.h>
#include	<sys/types.h>
#include	<sys/time.h>

/*#define DEBUG						/* show ops */
/*#define	MLIMIT	100				/* memory limit */

#define	LBSIZE	(1024-sizeof(struct lBlock *)-sizeof(int)) /* block size */
#define LBALLOC() (lBlock *)malloc(sizeof(lBlock))	/* alloc block */
#define	FOREVER	while(1)

#define	IN	0
#define	OUT	1

typedef struct lBlock {					/* live block */
	struct lBlock *next;
	int	used;
	char	buf[LBSIZE];
} lBlock;

static lBlock *head;

static void fill(in,statusP)				/* fill buffer */
int in,statusP;
{
	lBlock 	*tail;
	int	nRead,remain,nBlocks=0,maxFd;
	fd_set	selRead;
	
	head = tail = LBALLOC();			/* alloc 1st */
	if (head == NULL) {
		fprintf(stderr,"lBuffer malloc() failed\n");
		exit(1);
	} else nBlocks++;
	head->used = 0;
	head->next = NULL;
	remain = LBSIZE;

#ifdef DEBUG
	fprintf(stderr,"Filling lBuffer...\n");
#endif
	do {						/* build buffer */
		FD_ZERO(&selRead);			/* detect death */
		FD_SET(in,&selRead); maxFd = in;
		if (statusP >= 0)
			FD_SET(statusP,&selRead);
		if (statusP > maxFd)
			maxFd = statusP;
		if (select(maxFd+1,&selRead,NULL,NULL,NULL) < 0) {
			perror("lBuffer select");
			exit(1);
		}
		if ((statusP >= 0) &&			/* father dead */
		    FD_ISSET(statusP,&selRead)) {
#ifdef DEBUG
		    	fprintf(stderr,"Exiting due to parent death...\n");
#endif
			exit(1);
		}

		nRead = read(in,&tail->buf[tail->used],	/* read block */
			        remain);
		if (nRead < 0) {
			perror("lBuffer read");
			exit(1);
		}
		tail->used += nRead;
		remain -= nRead;

		if (nRead == 0) break;			/* eof */
		if (tail->buf[tail->used-1] == '\0') {
			tail->used--;
			break;
		}

		if (remain == 0) {			/* next block */
			tail->next = LBALLOC();
			if (tail->next == NULL) {
				fprintf(stderr,"lBuffer malloc() failed\n");
				exit(1);
			} else {
				nBlocks++;
#ifdef MLIMIT
				if (nBlocks > MLIMIT) {
					fprintf(stderr,"lBuffer MLIMIT\n");
					exit(1);
				}
#endif
                        }
			tail = tail->next;
			tail->used = 0;
			tail->next = NULL;
			remain = LBSIZE;
		}
	} FOREVER;
	close(in);
}

static void drain(out)					/* drain buffer */
int out;
{
	lBlock *cur;

	cur = head;
#ifdef DEBUG
	fprintf(stderr,"Draining lBuffer...\n");
#endif
	do {
		if (write(out,cur->buf,cur->used) != cur->used) {
			perror("lBuffer write");
			exit(1);
		}
		cur = cur->next;
	} while (cur != NULL);
	close(out);
}
	
/*======================================================================*/

static killed()						/* killed nicely */
{
	exit(0);
}

lPreBuffer(out1,out2)					/* buf twice */
int out1[2],out2[2];
{
	int pid,statusP[2];

	if (pipe(statusP) < 0) {			/* status pipe */
		perror("lBuffer pipe");
		exit(1);
	}
	if ((pid = fork()) < 0) {			/* build proc */
		perror("lBuffer fork");
		exit(1);
	}
	if (pid > 0) {					/* father continues */
		close(statusP[IN]);
		close(out1[OUT]);
		close(out2[OUT]);
		return pid;
	}

	signal(SIGALRM,SIG_IGN);			/* child buffers */
	signal(SIGINT,SIG_IGN);				/* enable killing */
	signal(SIGUSR1,killed);
	close(statusP[OUT]);
	close(out1[IN]);	
	close(out2[IN]);
	fill(IN,statusP[IN]);
	signal(SIGUSR1,SIG_IGN);			/* disable killing */
	drain(out1[OUT]);
	drain(out2[OUT]);
	exit(0);
}

lPostBuffer(in,out)					/* buf once */
int in,out[2];
{
	int pid;

#ifdef DEBUG
	fprintf(stderr,"lPostBuffer()\n");
#endif
	if ((pid = fork()) < 0) {			/* build proc */
		perror("lBuffer fork");
		exit(1);
	}
	if (pid > 0) {					/* father continues */
		close(out[OUT]);
		return pid;
	}

	signal(SIGALRM,SIG_IGN);			/* child buffers */
	close(out[IN]);
	fill(in,-1);
	drain(out[OUT]);
#ifdef DEBUG
	fprintf(stderr,"lPostBuffer normal exit\n");
#endif
	exit(0);
}
