home *** CD-ROM | disk | FTP | other *** search
- /*
- 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
- }
- }
- }
- }
-