/*
 * buffer: try to stream data
 *
 * usage:
 *  <generator> | buffer -i<blksize> -o<blksize> > output
 *  buffer -i<blksize> -o<blksize> < input | <user
 *
 *
 * How it works:  Buffer preforks 4-6 children which spend their
 * existance waiting on a flock(); when they get the lock, they
 * fill their buffer, release their lock. write out the data 
 * they read, then wait on the lock again.
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <errno.h>


int ibs = 512,
    obs = 512;

void
consumer(int childno)
{
    static char buffer[20480];	/* tape blocksize of 40 */
    int status, size, chunk;
    char *ptr;
    
    while (1) {

	if (flock(0, LOCK_EX) == -1)	/* grab control of our input */
	    if (errno = EINTR)
		continue;
	    else {
		kill(getppid(), SIGHUP);
		exit(0);
	    }

	chunk = sizeof buffer;
	ptr = buffer;

	while (chunk > 0) {
	    if ((size = read(0, ptr, chunk)) > 0) {
		chunk -= size;
		ptr += size;
	    }
	    else
		break;
	}

	while (flock(1, LOCK_EX) == -1)
	    if (errno != EINTR) {
		kill(getppid(), SIGHUP);
		exit(0);
	    }
	flock(0, LOCK_UN);	/* release input to the next thread */

	if (ptr > buffer)
	    write(1, buffer, ptr-buffer);

	flock(1, LOCK_UN);

	if (ptr == buffer) {
	    exit(0);
	}
    }
}


main()
{
    int x;
#define NRCHILD	16
    pid_t children[NRCHILD];
    pid_t child;
    int nrchild;

    for (x=0; x < NRCHILD; x++) {
	child = fork();
	if (child == 0) {
	    consumer(x);
	    exit(0);
	}
	else if (child > 0) {
	    children[x] = child;
	    nrchild++;
	}
	else {
	    children[x] = 0;
	}
    }

    if (nrchild == 0) {
	perror("forking consumers");
	exit(1);
    }

    while (nrchild > 0)
	if ((child = wait(&x)) > 0) {
	    for (x = 0; x < NRCHILD; x++)
		if (children[x] == child) {
		    children[x] = 0;
		    nrchild--;
		}
	}
    exit(0);
}