home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume22 / multitee2.0 / multitee.c < prev    next >
C/C++ Source or Header  |  1990-06-07  |  13KB  |  439 lines

  1. /*
  2. multitee.c: send multiple inputs to multiple outputs
  3. */
  4.  
  5. static char multiteeauthor[] =
  6. "multitee was written by Daniel J. Bernstein.\n\
  7. Internet address: brnstnd@acf10.nyu.edu.\n";
  8.  
  9. static char multiteeversion[] = 
  10. "multitee version 2.0, March 27, 1990.\n\
  11. Copyright (c) 1990, Daniel J. Bernstein.\n\
  12. All rights reserved.\n";
  13.  
  14. static char multiteecopyright[] =
  15. "multitee version 2.0, March 27, 1990.\n\
  16. Copyright (c) 1990, Daniel J. Bernstein.\n\
  17. All rights reserved.\n\
  18. \n\
  19. Until January 1, 2095, you are granted the following rights: A. To make\n\
  20. copies of this work in original form, so long as (1) the copies are exact\n\
  21. and complete; (2) the copies include the copyright notice, this paragraph,\n\
  22. and the disclaimer of warranty in their entirety. B. To distribute this\n\
  23. work, or copies made under the provisions above, so long as (1) this is\n\
  24. the original work and not a derivative form; (2) you do not charge a fee\n\
  25. for copying or for distribution; (3) you ensure that the distributed form\n\
  26. includes the copyright notice, this paragraph, and the disclaimer of\n\
  27. warranty in their entirety. These rights are temporary and revocable upon\n\
  28. written, oral, or other notice by Daniel J. Bernstein. These rights are\n\
  29. automatically revoked on January 1, 2095. This copyright notice shall be\n\
  30. governed by the laws of the state of New York.\n\
  31. \n\
  32. If you have questions about multitee or about this copyright notice,\n\
  33. or if you would like additional rights beyond those granted above,\n\
  34. please feel free to contact the author at brnstnd@acf10.nyu.edu\n\
  35. on the Internet.\n";
  36.  
  37. static char multiteewarranty[] =
  38. "To the extent permitted by applicable law, Daniel J. Bernstein disclaims\n\
  39. all warranties, explicit or implied, including but not limited to the\n\
  40. implied warranties of merchantability and fitness for a particular purpose.\n\
  41. Daniel J. Bernstein is not and shall not be liable for any damages,\n\
  42. incidental or consequential, arising from the use of this program, even\n\
  43. if you inform him of the possibility of such damages. This disclaimer\n\
  44. shall be governed by the laws of the state of New York.\n\
  45. \n\
  46. In other words, use this program at your own risk.\n\
  47. \n\
  48. If you have questions about multitee or about this disclaimer of warranty,\n\
  49. please feel free to contact the author at brnstnd@acf10.nyu.edu\n\
  50. on the Internet.\n";
  51.  
  52. static char multiteeusage[] =
  53. "Usage: multitee [ -osz ] [ -bBvACHUVW ] [ fd-fd,fd,fd... ] ... \n\
  54. Help:  multitee -H\n";
  55.  
  56. static char multiteehelp[] =
  57. "multitee sends multiple inputs to multiple outputs.\n\
  58. \n\
  59. multitee -A: print authorship notice\n\
  60. multitee -C: print copyright notice\n\
  61. multitee -H: print this notice\n\
  62. multitee -U: print short usage summary\n\
  63. multitee -V: print version number\n\
  64. multitee -W: print disclaimer of warranty\n\
  65. \n\
  66. multitee [ -osz ] [ -bBv ][ fd-fd,fd,fd... ] ... : read and write descriptors\n\
  67.   -osz: limit size of each output buffer to sz (default 16384)\n\
  68.   -b: never block, no matter what (default)\n\
  69.   -B: don't worry so much about blocking (forced on some systems)\n\
  70.   -v: print error messages to stderr\n\
  71.   fdin-fdout,fdout,fdout...: read from fdin, write to each fdout\n\
  72.                              (0 can be left out, - can be replaced by :)\n\
  73. Examples: multitee 0:1,2    like normal tee\n\
  74.       multitee -- -1,2  same thing (-- ends options; -1,2 is 0-1,2)\n\
  75.           multitee 0-6 6-1  send 0 in to 6 out while sending 6 in to 1 out\n\
  76. \n\
  77. If you have questions about or suggestions for multitee, please feel free\n\
  78. to contact the author, Daniel J. Bernstein, at brnstnd@acf10.nyu.edu\n\
  79. on the Internet.\n";
  80.  
  81. #include <stdio.h> /* just for EOF, sigh */
  82. #include <sys/types.h>
  83. #include <sys/time.h>
  84. #include <signal.h>
  85. extern char *malloc(); /* sigh */
  86. #include <errno.h>
  87. extern int errno;
  88. extern int getopt();
  89. extern char *optarg; /* these should be in getopt.h! */
  90. extern int optind;
  91. #include "djberr.h"
  92.  
  93. #define copy(dst,src,num) bcopy(src,dst,num)
  94.  
  95. #ifndef FDTABLESIZE
  96. #define FDTABLESIZE FD_SETSIZE /* or NOFILE from param.h? */
  97. #endif
  98.  
  99. struct fdlist
  100.  {
  101.   int fd;
  102.   struct fdlist *next;
  103.  }
  104. ;
  105. #define FDNULL ((struct fdlist *) 0)
  106.  
  107. #ifndef BUFINSIZE
  108. #define BUFINSIZE 4096
  109. #endif
  110.  
  111. #ifndef MAXBUFOUTSIZE
  112. #define MAXBUFOUTSIZE 16384
  113. #endif
  114.  
  115. /* The following kludges always work and are better than nothing. */
  116. #ifndef EINVAL
  117. #define EINVAL 0
  118. #endif
  119. #ifndef EBADF
  120. #define EBADF 0
  121. #endif
  122. #ifndef EINTR
  123. #define EINTR 0
  124. #endif
  125. #ifndef EWOULDBLOCK
  126. #define EWOULDBLOCK 0
  127. #endif
  128. #ifndef EFAULT
  129. #define EFAULT 0
  130. #endif
  131. #ifndef EIO
  132. #define EIO 0
  133. #endif
  134. #ifndef EPIPE
  135. #define EPIPE 0
  136. #endif
  137. #ifndef EFBIG
  138. #define EFBIG 0
  139. #endif
  140. #ifndef ENOSPC
  141. #define ENOSPC 0
  142. #endif
  143. #ifndef EDQUOT
  144. #define EDQUOT 0
  145. #endif
  146.  
  147. void nothing()
  148. {
  149. }
  150.  
  151. main(argc,argv,envp)
  152. int argc;
  153. char *argv[];
  154. char *envp[];
  155. {
  156.  int opt;
  157.  char *bufin;
  158.  int bufinsize;
  159.  char *oldbuf;
  160.  char *bufout[FDTABLESIZE];
  161.  int bufoutsize[FDTABLESIZE];
  162.  int bufoutpos[FDTABLESIZE];
  163.  fd_set rfds;
  164.  fd_set wfds;
  165.  int numrfds;
  166.  int numwfds;
  167.  int numfds;
  168.  struct fdlist *fds[FDTABLESIZE];
  169.  register struct fdlist *t;
  170.  register int i;
  171.  register int j;
  172.  register int r;
  173.  struct itimerval it;
  174.  int flagneverblock = 1;
  175.  int flagverbose = 0;
  176.  int maxbufoutsize = MAXBUFOUTSIZE;
  177.  int flagpastmax = 0;
  178.  int flagdie = 0;
  179.  
  180.  while ((opt = getopt(argc,argv,"o:bBvACHUVW")) != EOF)
  181.    switch(opt)
  182.     {
  183.      case 'o': maxbufoutsize = atoi(optarg); break;
  184.      case 'b': flagneverblock = 1; break;
  185.      case 'B': flagneverblock = 0; break;
  186.      case 'v': flagverbose = 1; break;
  187.      case 'A': (void) err(multiteeauthor); exit(1);
  188.      case 'C': (void) err(multiteecopyright); exit(1);
  189.      case 'H': (void) err(multiteehelp); exit(1);
  190.      case 'U': (void) err(multiteeusage); exit(1);
  191.      case 'V': (void) err(multiteeversion); exit(1);
  192.      case 'W': (void) err(multiteewarranty); exit(1);
  193.      case '?': (void) err(multiteeusage); exit(1);
  194.     }
  195.  argv += optind, argc -= optind;
  196.  
  197.  numrfds = numwfds = 0;
  198.  while (*argv)
  199.   {
  200.    i = 0;
  201.    while (**argv >= '0' && **argv <= '9')
  202.      i = 10 * i + *((*argv)++) - '0';
  203.    if (i < 0 || i >= FDTABLESIZE)
  204.     { if (flagverbose) err(multiteeusage); exit(1); }
  205.    if (**argv != '-' && **argv != ':')
  206.     { if (flagverbose) err(multiteeusage); exit(1); }
  207.    if (i + 1 > numrfds) numrfds = i + 1;
  208.    do
  209.     {
  210.      (*argv)++;
  211.      j = 0;
  212.      while (**argv >= '0' && **argv <= '9')
  213.        j = 10 * j + *((*argv)++) - '0';
  214.      if (j < 0 || j >= FDTABLESIZE)
  215.       { if (flagverbose) err(multiteeusage); exit(1); }
  216.      if (!(t = (struct fdlist *) malloc(sizeof(struct fdlist))))
  217.        /* XXXXXX: should have manually allocated avail list */
  218.       { if (flagverbose) errn("multitee: fatal: no memory"); exit(3); }
  219.      t->fd = j;
  220.      t->next = fds[i];
  221.      fds[i] = t;
  222.      if (j + 1 > numwfds) numwfds = j + 1;
  223.     }
  224.    while(**argv == ',');
  225.    if (**argv) /* not a comma but not a zero */
  226.     { if (flagverbose) err(multiteeusage); exit(1); }
  227.    argv++;
  228.   }
  229.  
  230.  bufinsize = BUFINSIZE;
  231.  if (!(bufin = malloc((unsigned) bufinsize)))
  232.   { if (flagverbose) errn("multitee: fatal: no memory"); exit(3); }
  233.  if (numrfds > numwfds)
  234.    numfds = numrfds;
  235.  else
  236.    numfds = numwfds;
  237.  if (flagneverblock)
  238.   {
  239. #ifdef NOSIGINTERRUPT
  240.    if (flagverbose) errn("multitee: fatal: nonblocking I/O not supported");
  241.    exit(5);
  242. #else
  243.    (void) siginterrupt(SIGALRM,1);
  244.    (void) signal(SIGALRM,nothing);
  245. #endif
  246.   }
  247.  for (;;)
  248.   {
  249.    FD_ZERO(&rfds);
  250.    if (flagpastmax == 0) /* as long as somebody's past, we'll block */
  251.      for (i = 0;i < numrfds;i++)
  252.        if (fds[i] != FDNULL)
  253.          FD_SET(i,&rfds);
  254.    r = 0;
  255.    FD_ZERO(&wfds);
  256.    for (i = 0;i < numwfds;i++)
  257.      if (bufoutpos[i])
  258.       {
  259.        r++;
  260.        FD_SET(i,&wfds);
  261.       }
  262.    if (flagdie && !r)
  263.      exit(0); /* aaah, a normal exit */
  264.    r = select(numfds,&rfds,&wfds,(fd_set *) 0,(struct timeval *) 0);
  265.    if (r == -1)
  266.      switch(errno)
  267.       {
  268.        case EINTR: 
  269. #ifndef NOSIGINTERRUPT
  270.          it.it_value.tv_sec = it.it_interval.tv_sec = 0;
  271.          it.it_value.tv_usec = it.it_interval.tv_usec = 0;
  272.          (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
  273. #endif
  274.          break; /* turn ALRM off---usually unnecessary */
  275.        case EINVAL:
  276.      break; /* impossible */
  277.        case EBADF:
  278.      if (flagverbose) errn("multitee: fatal: unopened fd");
  279.      exit(2); /* annoying: no way to find the bad fd! */
  280.        default: 
  281.      break; /* XXXXXX */
  282.       }
  283.    else if (r > 0)
  284.     {
  285.      if (flagneverblock)
  286.       {
  287. #ifndef NOSIGINTERRUPT
  288.        it.it_value.tv_sec = it.it_interval.tv_sec = 0;
  289.        it.it_value.tv_usec = it.it_interval.tv_usec = 10000;
  290.        (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
  291. #endif
  292.       }
  293.      for (i = 0;i < numrfds;i++)
  294.        if (FD_ISSET(i,&rfds))
  295.     {
  296.      r = read(i,bufin,bufinsize);
  297.      if (r == -1)
  298.        switch(errno)
  299.         {
  300.          case EINTR: case EWOULDBLOCK:
  301.            break; /* why readable, then? */
  302.              case EBADF:
  303.            if (flagverbose) errn("multitee: weird: unopened read fd");
  304.            exit(4); /* impossible: select would know */
  305.              case EINVAL:
  306.            if (flagverbose) errn("multitee: weird: negative read fd");
  307.            exit(4); /* impossible: select would know */
  308.              case EFAULT:
  309.            if (flagverbose) errn("multitee: weird: read memory fault");
  310.            exit(4); /* impossible: bufin is allocated */
  311.          case EIO:
  312.            if (flagverbose) errn("multitee: fatal: read I/O error");
  313.            exit(6); /* XXXXXX: there's almost certainly a better way */
  314.          default: 
  315.            if (flagverbose) perrn2("%s","multitee: weird: unknown error");
  316.            exit(7); /* if we dunno what's happening, too bad */
  317.         }
  318.      else if (r == 0) /* EOF */
  319.       {
  320.        while (fds[i] != FDNULL)
  321.         {
  322.          t = fds[i];
  323.          fds[i] = t->next;
  324.          (void) free((char *) t);
  325.         }
  326.        /* XXXXXX: should have option for ``die on eof here'' */
  327.        while (fds[numrfds - 1] == FDNULL)
  328.          numrfds--;
  329.        if (numrfds == 0)
  330.          flagdie = 1;
  331.       }
  332.      else
  333.        for (t = fds[i];t != FDNULL;t = t->next)
  334.         {
  335.          if (bufoutpos[t->fd] > maxbufoutsize) /* anti-aack! */
  336.            flagpastmax--; /* otherwise we'd count this pastmax twice */
  337.          if (bufoutsize[t->fd] - bufoutpos[t->fd] < r)
  338.           {
  339.            bufoutsize[t->fd] = ((bufoutpos[t->fd] + r + 255) / 256) * 256;
  340.            /* can't use realloc: it doesn't return sensible errors */
  341.            oldbuf = bufout[t->fd];
  342.            bufout[t->fd] = malloc((unsigned) bufoutsize[t->fd]);
  343.            if (bufout[t->fd])
  344.         {
  345.          (void) copy(bufout[t->fd],oldbuf,bufoutpos[t->fd]);
  346.          (void) free(oldbuf);
  347.         }
  348.            else
  349.         { /* damn, out of memory */
  350.          bufout[t->fd] = oldbuf;
  351.          /* XXXXXX: shouldn't just die... */
  352.          if (flagverbose) errn("multitee: fatal: no memory");
  353.          exit(3);
  354.         }
  355.           }
  356.          (void) copy(bufout[t->fd] + bufoutpos[t->fd],bufin,r);
  357.          bufoutpos[t->fd] += r;
  358.          if (bufoutpos[t->fd] > maxbufoutsize) /* aack! */
  359.            flagpastmax++;
  360.         }
  361.     }
  362.      for (i = 0;i < numwfds;i++)
  363.        if (FD_ISSET(i,&wfds))
  364.     {
  365.      r = write(i,bufout[i],bufoutpos[i]);
  366.      if (r == -1)
  367.        switch(errno)
  368.         {
  369.          case EINTR: case EWOULDBLOCK:
  370.            break; /* why writable, then? */
  371.              case EBADF:
  372.            if (flagverbose) errn("multitee: weird: unopened write fd");
  373.            exit(4); /* impossible: select would know */
  374.              case EINVAL:
  375.            if (flagverbose) errn("multitee: weird: negative write fd");
  376.            exit(4); /* impossible: select would know */
  377.              case EFAULT:
  378.            if (flagverbose) errn("multitee: weird: write memory fault");
  379.            exit(4); /* impossible: bufout[i] is allocated */
  380.          case EPIPE:
  381.            for (j = 0;j < numrfds;j++)
  382.         {
  383.          t = fds[j];
  384.              /* XXXXXX: should have option ``die on broken pipe here'' */
  385.          while (t != FDNULL)
  386.            if (t->fd == i)
  387.             {
  388.              free((char *) t);
  389.              t = fds[j]; /* should use extra variable, not retrace */
  390.             }
  391.            else
  392.              t = t->next;
  393.          if (fds[j] == FDNULL)
  394.            {
  395.             /* basically same as eof on read */
  396.                 while (fds[numrfds - 1] == FDNULL)
  397.                   numrfds--;
  398.                 if (numrfds == 0)
  399.                   flagdie = 1;
  400.            }
  401.         }
  402.            break;
  403.          case EFBIG: case ENOSPC: case EDQUOT:
  404.            if (flagverbose) errn("multitee: fatal: out of space");
  405.            exit(8); /* XXXXXX: what to do? this is difficult */
  406.          case EIO:
  407.            if (flagverbose) errn("multitee: fatal: write I/O error");
  408.            exit(6); /* XXXXXX: there's almost certainly a better way */
  409.          default: 
  410.            if (flagverbose) perrn2("%s","multitee: weird: unknown error");
  411.            exit(7); /* if we dunno what's happening, too bad */
  412.         }
  413.      else if (r == 0)
  414.       {
  415.        ; /* wtf does this mean? */
  416.       }
  417.      else
  418.       {
  419.        if (bufoutpos[i] > maxbufoutsize) /* anti-aack! */
  420.          flagpastmax--;
  421.        bufoutpos[i] -= r;
  422.        if (bufoutpos[i] > 0) /* worth trading test for a function call */
  423.          copy(bufout[i],bufout[i] + r,bufoutpos[i]);
  424.        if (bufoutpos[i] > maxbufoutsize) /* aack! */
  425.          flagpastmax++;
  426.       }
  427.     }
  428.      if (flagneverblock)
  429.       {
  430. #ifndef NOSIGINTERRUPT
  431.        it.it_value.tv_sec = it.it_interval.tv_sec = 0;
  432.        it.it_value.tv_usec = it.it_interval.tv_usec = 0;
  433.        (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
  434. #endif
  435.       }
  436.     }
  437.   }
  438. }
  439.