home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume22
/
multitee2.0
/
multitee.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-06-07
|
13KB
|
439 lines
/*
multitee.c: send multiple inputs to multiple outputs
*/
static char multiteeauthor[] =
"multitee was written by Daniel J. Bernstein.\n\
Internet address: brnstnd@acf10.nyu.edu.\n";
static char multiteeversion[] =
"multitee version 2.0, March 27, 1990.\n\
Copyright (c) 1990, Daniel J. Bernstein.\n\
All rights reserved.\n";
static char multiteecopyright[] =
"multitee version 2.0, March 27, 1990.\n\
Copyright (c) 1990, Daniel J. Bernstein.\n\
All rights reserved.\n\
\n\
Until January 1, 2095, you are granted the following rights: A. To make\n\
copies of this work in original form, so long as (1) the copies are exact\n\
and complete; (2) the copies include the copyright notice, this paragraph,\n\
and the disclaimer of warranty in their entirety. B. To distribute this\n\
work, or copies made under the provisions above, so long as (1) this is\n\
the original work and not a derivative form; (2) you do not charge a fee\n\
for copying or for distribution; (3) you ensure that the distributed form\n\
includes the copyright notice, this paragraph, and the disclaimer of\n\
warranty in their entirety. These rights are temporary and revocable upon\n\
written, oral, or other notice by Daniel J. Bernstein. These rights are\n\
automatically revoked on January 1, 2095. This copyright notice shall be\n\
governed by the laws of the state of New York.\n\
\n\
If you have questions about multitee or about this copyright notice,\n\
or if you would like additional rights beyond those granted above,\n\
please feel free to contact the author at brnstnd@acf10.nyu.edu\n\
on the Internet.\n";
static char multiteewarranty[] =
"To the extent permitted by applicable law, Daniel J. Bernstein disclaims\n\
all warranties, explicit or implied, including but not limited to the\n\
implied warranties of merchantability and fitness for a particular purpose.\n\
Daniel J. Bernstein is not and shall not be liable for any damages,\n\
incidental or consequential, arising from the use of this program, even\n\
if you inform him of the possibility of such damages. This disclaimer\n\
shall be governed by the laws of the state of New York.\n\
\n\
In other words, use this program at your own risk.\n\
\n\
If you have questions about multitee or about this disclaimer of warranty,\n\
please feel free to contact the author at brnstnd@acf10.nyu.edu\n\
on the Internet.\n";
static char multiteeusage[] =
"Usage: multitee [ -osz ] [ -bBvACHUVW ] [ fd-fd,fd,fd... ] ... \n\
Help: multitee -H\n";
static char multiteehelp[] =
"multitee sends multiple inputs to multiple outputs.\n\
\n\
multitee -A: print authorship notice\n\
multitee -C: print copyright notice\n\
multitee -H: print this notice\n\
multitee -U: print short usage summary\n\
multitee -V: print version number\n\
multitee -W: print disclaimer of warranty\n\
\n\
multitee [ -osz ] [ -bBv ][ fd-fd,fd,fd... ] ... : read and write descriptors\n\
-osz: limit size of each output buffer to sz (default 16384)\n\
-b: never block, no matter what (default)\n\
-B: don't worry so much about blocking (forced on some systems)\n\
-v: print error messages to stderr\n\
fdin-fdout,fdout,fdout...: read from fdin, write to each fdout\n\
(0 can be left out, - can be replaced by :)\n\
Examples: multitee 0:1,2 like normal tee\n\
multitee -- -1,2 same thing (-- ends options; -1,2 is 0-1,2)\n\
multitee 0-6 6-1 send 0 in to 6 out while sending 6 in to 1 out\n\
\n\
If you have questions about or suggestions for multitee, please feel free\n\
to contact the author, Daniel J. Bernstein, at brnstnd@acf10.nyu.edu\n\
on the Internet.\n";
#include <stdio.h> /* just for EOF, sigh */
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
extern char *malloc(); /* sigh */
#include <errno.h>
extern int errno;
extern int getopt();
extern char *optarg; /* these should be in getopt.h! */
extern int optind;
#include "djberr.h"
#define copy(dst,src,num) bcopy(src,dst,num)
#ifndef FDTABLESIZE
#define FDTABLESIZE FD_SETSIZE /* or NOFILE from param.h? */
#endif
struct fdlist
{
int fd;
struct fdlist *next;
}
;
#define FDNULL ((struct fdlist *) 0)
#ifndef BUFINSIZE
#define BUFINSIZE 4096
#endif
#ifndef MAXBUFOUTSIZE
#define MAXBUFOUTSIZE 16384
#endif
/* The following kludges always work and are better than nothing. */
#ifndef EINVAL
#define EINVAL 0
#endif
#ifndef EBADF
#define EBADF 0
#endif
#ifndef EINTR
#define EINTR 0
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK 0
#endif
#ifndef EFAULT
#define EFAULT 0
#endif
#ifndef EIO
#define EIO 0
#endif
#ifndef EPIPE
#define EPIPE 0
#endif
#ifndef EFBIG
#define EFBIG 0
#endif
#ifndef ENOSPC
#define ENOSPC 0
#endif
#ifndef EDQUOT
#define EDQUOT 0
#endif
void nothing()
{
}
main(argc,argv,envp)
int argc;
char *argv[];
char *envp[];
{
int opt;
char *bufin;
int bufinsize;
char *oldbuf;
char *bufout[FDTABLESIZE];
int bufoutsize[FDTABLESIZE];
int bufoutpos[FDTABLESIZE];
fd_set rfds;
fd_set wfds;
int numrfds;
int numwfds;
int numfds;
struct fdlist *fds[FDTABLESIZE];
register struct fdlist *t;
register int i;
register int j;
register int r;
struct itimerval it;
int flagneverblock = 1;
int flagverbose = 0;
int maxbufoutsize = MAXBUFOUTSIZE;
int flagpastmax = 0;
int flagdie = 0;
while ((opt = getopt(argc,argv,"o:bBvACHUVW")) != EOF)
switch(opt)
{
case 'o': maxbufoutsize = atoi(optarg); break;
case 'b': flagneverblock = 1; break;
case 'B': flagneverblock = 0; break;
case 'v': flagverbose = 1; break;
case 'A': (void) err(multiteeauthor); exit(1);
case 'C': (void) err(multiteecopyright); exit(1);
case 'H': (void) err(multiteehelp); exit(1);
case 'U': (void) err(multiteeusage); exit(1);
case 'V': (void) err(multiteeversion); exit(1);
case 'W': (void) err(multiteewarranty); exit(1);
case '?': (void) err(multiteeusage); exit(1);
}
argv += optind, argc -= optind;
numrfds = numwfds = 0;
while (*argv)
{
i = 0;
while (**argv >= '0' && **argv <= '9')
i = 10 * i + *((*argv)++) - '0';
if (i < 0 || i >= FDTABLESIZE)
{ if (flagverbose) err(multiteeusage); exit(1); }
if (**argv != '-' && **argv != ':')
{ if (flagverbose) err(multiteeusage); exit(1); }
if (i + 1 > numrfds) numrfds = i + 1;
do
{
(*argv)++;
j = 0;
while (**argv >= '0' && **argv <= '9')
j = 10 * j + *((*argv)++) - '0';
if (j < 0 || j >= FDTABLESIZE)
{ if (flagverbose) err(multiteeusage); exit(1); }
if (!(t = (struct fdlist *) malloc(sizeof(struct fdlist))))
/* XXXXXX: should have manually allocated avail list */
{ if (flagverbose) errn("multitee: fatal: no memory"); exit(3); }
t->fd = j;
t->next = fds[i];
fds[i] = t;
if (j + 1 > numwfds) numwfds = j + 1;
}
while(**argv == ',');
if (**argv) /* not a comma but not a zero */
{ if (flagverbose) err(multiteeusage); exit(1); }
argv++;
}
bufinsize = BUFINSIZE;
if (!(bufin = malloc((unsigned) bufinsize)))
{ if (flagverbose) errn("multitee: fatal: no memory"); exit(3); }
if (numrfds > numwfds)
numfds = numrfds;
else
numfds = numwfds;
if (flagneverblock)
{
#ifdef NOSIGINTERRUPT
if (flagverbose) errn("multitee: fatal: nonblocking I/O not supported");
exit(5);
#else
(void) siginterrupt(SIGALRM,1);
(void) signal(SIGALRM,nothing);
#endif
}
for (;;)
{
FD_ZERO(&rfds);
if (flagpastmax == 0) /* as long as somebody's past, we'll block */
for (i = 0;i < numrfds;i++)
if (fds[i] != FDNULL)
FD_SET(i,&rfds);
r = 0;
FD_ZERO(&wfds);
for (i = 0;i < numwfds;i++)
if (bufoutpos[i])
{
r++;
FD_SET(i,&wfds);
}
if (flagdie && !r)
exit(0); /* aaah, a normal exit */
r = select(numfds,&rfds,&wfds,(fd_set *) 0,(struct timeval *) 0);
if (r == -1)
switch(errno)
{
case EINTR:
#ifndef NOSIGINTERRUPT
it.it_value.tv_sec = it.it_interval.tv_sec = 0;
it.it_value.tv_usec = it.it_interval.tv_usec = 0;
(void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
#endif
break; /* turn ALRM off---usually unnecessary */
case EINVAL:
break; /* impossible */
case EBADF:
if (flagverbose) errn("multitee: fatal: unopened fd");
exit(2); /* annoying: no way to find the bad fd! */
default:
break; /* XXXXXX */
}
else if (r > 0)
{
if (flagneverblock)
{
#ifndef NOSIGINTERRUPT
it.it_value.tv_sec = it.it_interval.tv_sec = 0;
it.it_value.tv_usec = it.it_interval.tv_usec = 10000;
(void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
#endif
}
for (i = 0;i < numrfds;i++)
if (FD_ISSET(i,&rfds))
{
r = read(i,bufin,bufinsize);
if (r == -1)
switch(errno)
{
case EINTR: case EWOULDBLOCK:
break; /* why readable, then? */
case EBADF:
if (flagverbose) errn("multitee: weird: unopened read fd");
exit(4); /* impossible: select would know */
case EINVAL:
if (flagverbose) errn("multitee: weird: negative read fd");
exit(4); /* impossible: select would know */
case EFAULT:
if (flagverbose) errn("multitee: weird: read memory fault");
exit(4); /* impossible: bufin is allocated */
case EIO:
if (flagverbose) errn("multitee: fatal: read I/O error");
exit(6); /* XXXXXX: there's almost certainly a better way */
default:
if (flagverbose) perrn2("%s","multitee: weird: unknown error");
exit(7); /* if we dunno what's happening, too bad */
}
else if (r == 0) /* EOF */
{
while (fds[i] != FDNULL)
{
t = fds[i];
fds[i] = t->next;
(void) free((char *) t);
}
/* XXXXXX: should have option for ``die on eof here'' */
while (fds[numrfds - 1] == FDNULL)
numrfds--;
if (numrfds == 0)
flagdie = 1;
}
else
for (t = fds[i];t != FDNULL;t = t->next)
{
if (bufoutpos[t->fd] > maxbufoutsize) /* anti-aack! */
flagpastmax--; /* otherwise we'd count this pastmax twice */
if (bufoutsize[t->fd] - bufoutpos[t->fd] < r)
{
bufoutsize[t->fd] = ((bufoutpos[t->fd] + r + 255) / 256) * 256;
/* can't use realloc: it doesn't return sensible errors */
oldbuf = bufout[t->fd];
bufout[t->fd] = malloc((unsigned) bufoutsize[t->fd]);
if (bufout[t->fd])
{
(void) copy(bufout[t->fd],oldbuf,bufoutpos[t->fd]);
(void) free(oldbuf);
}
else
{ /* damn, out of memory */
bufout[t->fd] = oldbuf;
/* XXXXXX: shouldn't just die... */
if (flagverbose) errn("multitee: fatal: no memory");
exit(3);
}
}
(void) copy(bufout[t->fd] + bufoutpos[t->fd],bufin,r);
bufoutpos[t->fd] += r;
if (bufoutpos[t->fd] > maxbufoutsize) /* aack! */
flagpastmax++;
}
}
for (i = 0;i < numwfds;i++)
if (FD_ISSET(i,&wfds))
{
r = write(i,bufout[i],bufoutpos[i]);
if (r == -1)
switch(errno)
{
case EINTR: case EWOULDBLOCK:
break; /* why writable, then? */
case EBADF:
if (flagverbose) errn("multitee: weird: unopened write fd");
exit(4); /* impossible: select would know */
case EINVAL:
if (flagverbose) errn("multitee: weird: negative write fd");
exit(4); /* impossible: select would know */
case EFAULT:
if (flagverbose) errn("multitee: weird: write memory fault");
exit(4); /* impossible: bufout[i] is allocated */
case EPIPE:
for (j = 0;j < numrfds;j++)
{
t = fds[j];
/* XXXXXX: should have option ``die on broken pipe here'' */
while (t != FDNULL)
if (t->fd == i)
{
free((char *) t);
t = fds[j]; /* should use extra variable, not retrace */
}
else
t = t->next;
if (fds[j] == FDNULL)
{
/* basically same as eof on read */
while (fds[numrfds - 1] == FDNULL)
numrfds--;
if (numrfds == 0)
flagdie = 1;
}
}
break;
case EFBIG: case ENOSPC: case EDQUOT:
if (flagverbose) errn("multitee: fatal: out of space");
exit(8); /* XXXXXX: what to do? this is difficult */
case EIO:
if (flagverbose) errn("multitee: fatal: write I/O error");
exit(6); /* XXXXXX: there's almost certainly a better way */
default:
if (flagverbose) perrn2("%s","multitee: weird: unknown error");
exit(7); /* if we dunno what's happening, too bad */
}
else if (r == 0)
{
; /* wtf does this mean? */
}
else
{
if (bufoutpos[i] > maxbufoutsize) /* anti-aack! */
flagpastmax--;
bufoutpos[i] -= r;
if (bufoutpos[i] > 0) /* worth trading test for a function call */
copy(bufout[i],bufout[i] + r,bufoutpos[i]);
if (bufoutpos[i] > maxbufoutsize) /* aack! */
flagpastmax++;
}
}
if (flagneverblock)
{
#ifndef NOSIGINTERRUPT
it.it_value.tv_sec = it.it_interval.tv_sec = 0;
it.it_value.tv_usec = it.it_interval.tv_usec = 0;
(void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
#endif
}
}
}
}