multitee-3.0/ 40755 1752 1752 0 6472172450 11717 5ustar roverrovermultitee-3.0/config/ 40755 1752 1752 0 6472172447 13172 5ustar roverrovermultitee-3.0/config/fdsettrouble.h100644 1752 1752 264 6472172447 16124 0ustar roverrover#ifndef CONFIG_FD_SET_TROUBLE_H #define CONFIG_FD_SET_TROUBLE_H #undef LACKING_FD_ZERO #undef DESPERATE_FD_SET /* sysconf expects lines 4 and 5 */ /* XXX: deprecated */ #endif multitee-3.0/fmt.c100644 1752 1752 13224 6472172447 12776 0ustar roverrover/* fmt.c, fmt.h: formatting library Daniel J. Bernstein, brnstnd@nyu.edu. No dependencies. No environment requirements. 10/5/91: Cleaned up fmt_rvis, added fmt_unrvis. 9/1/91: Added fmt_nvis, fmt_rvis. 8/28/91: Added fmt_vis. 7/18/91: Baseline. fmt 1.0, public domain. No known patent problems. XXX: still need floating-point formatting */ #include "fmt.h" /* To find out the actual length of the formatted value, pass a first argument of (char *) 0. */ #define zero '0' #define alow 'a' #define plus '+' #define minus '-' #define xlow 'x' unsigned int fmt_ulong(s,u) char *s; unsigned long u; { unsigned int len; unsigned long q; len = 1; q = u; while (q > 9) { ++len; q /= 10; } if (s) { s += len; do { *--s = zero + (u % 10); u /= 10; } while(u); /* handles u == 0 */ } return len; } unsigned int fmt_xlong(s,u) char *s; unsigned long u; { unsigned int len; unsigned long q; unsigned long c; len = 1; q = u; while (q > 15) { ++len; q /= 16; } if (s) { s += len; do { c = u & 15; *--s = (c > 9 ? alow - 10 : zero) + c; u /= 16; } while(u); } return len; } unsigned int fmt_nbblong(s,n,base,bext,u) char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned long u; /* I hope this meaning of n (min, not max) doesn't hurt anything. */ { unsigned int len; unsigned long q; unsigned long c; len = 1; q = u; bext += base; while (q > bext - 1) { ++len; q /= bext; } if (len < n) len = n; if (s) { s += len; do { c = u % bext; *--s = (c >= base ? alow - base : zero) + c; u /= bext; } while(u); } return len; } unsigned int fmt_ushort(s,u) char *s; unsigned short u; { unsigned long l; l = u; return fmt_ulong(s,l); } unsigned int fmt_xshort(s,u) char *s; unsigned short u; { unsigned long l; l = u; return fmt_xlong(s,l); } unsigned int fmt_nbbshort(s,n,base,bext,u) char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned short u; { unsigned long l; l = u; return fmt_nbblong(s,n,base,bext,l); } unsigned int fmt_uint(s,u) char *s; unsigned int u; { unsigned long l; l = u; return fmt_ulong(s,l); } unsigned int fmt_xint(s,u) char *s; unsigned int u; { unsigned long l; l = u; return fmt_xlong(s,l); } unsigned int fmt_nbbint(s,n,base,bext,u) char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned int u; { unsigned long l; l = u; return fmt_nbblong(s,n,base,bext,l); } unsigned int fmt_plusminus(s,sign) char *s; int sign; { if (s) *s = ((sign < 0) ? minus : plus); return 1; } unsigned int fmt_minus(s,sign) char *s; int sign; { if (sign > 0) return 0; if (s) *s = minus; return 1; } unsigned int fmt_0x(s,base) char *s; int base; { if (base == 10) return 0; if (s) *s = zero; if (base == 8) return 1; if (s) s[1] = xlow; return 2; } unsigned int fmt_strncpy(s,t,n) char *s; char *t; unsigned int n; { unsigned int len; len = 0; if (s) { while (s[len] = t[len]) if (++len == n) break; } else { while (t[len]) if (++len == n) break; } return len; } unsigned int fmt_memcpy(s,t,n) char *s; char *t; unsigned int n; /* This and fmt_vis are the only functions where n == 0 means do nothing. */ { unsigned int len; if (s) for (len = 0;len < n;++len) s[len] = t[len]; return n; } static unsigned int fmt_xvis(s,t,n,x) char *s; char *t; unsigned int n; int x; { unsigned int len; for (len = 0;n;--n,++t) { int ch; ch = (int) (unsigned int) (unsigned char) *t; /* XXX: ASCII dependent! */ if (ch > 127) { if (s) { s[len] = 'M'; s[len + 1] = '-'; } len += 2; ch -= 128; } if (((ch >= 32) && (ch <= 126)) || (ch == x)) { if (s) s[len] = ch; ++len; continue; } if (s) s[len] = '^'; ++len; if (s) s[len] = 64 + (ch & 31) - 32 * (ch == 127); ++len; } return len; } unsigned int fmt_vis(s,t,n) char *s; char *t; unsigned int n; { return fmt_xvis(s,t,n,-1); } unsigned int fmt_nvis(s,t,n) char *s; char *t; unsigned int n; { return fmt_xvis(s,t,n,'\n'); } /* invertible! */ unsigned int fmt_rvis(s,t,n) char *s; char *t; unsigned int n; { unsigned int len; for (len = 0;n;--n,++t) { int ch; ch = (int) (unsigned int) (unsigned char) *t; /* XXX: ASCII dependent! */ if ((ch >= 32) && (ch <= 126) && (ch != '^')) { if (s) s[len] = ch; ++len; continue; } if (ch == 127) { if (s) { s[len] = '^'; s[len + 1] = '?'; } len += 2; continue; } if (ch == '^') { if (s) { s[len] = '^'; s[len + 1] = ' '; } len += 2; continue; } if (ch == 10) { if (s) { s[len] = '^'; s[len + 1] = '$'; } len += 2; continue; } if ((ch >= 0) && (ch <= 31)) { if (s) { s[len] = '^'; s[len + 1] = 64 + (ch & 31); } len += 2; continue; } if (s) { s[len] = '^'; s[len + 1] = 'x'; } len += 2; if (s) { s[len] = (ch >= 160) ? alow + ((ch/16) - 10) : zero + (ch/16); } ++len; ch = ch & 15; if (s) { s[len] = (ch >= 10) ? alow + (ch - 10) : zero + ch; } ++len; } s[len] = '\n'; return len + 1; } unsigned int fmt_unrvis(s,t,n) char *s; char *t; unsigned int n; { unsigned int len; for (len = 0;n;--n,++t) { if (*t == '\n') continue; if (*t != '^') { if (s) *s++ = *t; ++len; continue; } if (n < 2) return len; ++t; --n; if (*t == '?') { if (s) *s++ = 127; ++len; continue; } if (*t == ' ') { if (s) *s++ = '^'; ++len; continue; } if ((*t >= 64) && (*t <= 95)) { if (s) *s++ = *t - 64; ++len; continue; } if (*t == '$') { if (s) *s++ = 10; ++len; continue; } if (n < 3) return len; if (*t != 'x') return len; /* XXX */ ++t; if (s) if (*t < alow) *s = *t - zero; else *s = *t - alow + 10; if (s) *s <<= 4; ++t; if (s) if (*t < alow) *s += *t - zero; else *s += *t - alow + 10; if (s) ++s; ++len; n -= 2; } return len; } multitee-3.0/fmt.h100644 1752 1752 1401 6472172447 12755 0ustar roverrover#ifndef FMT_H #define FMT_H #define FMT_ULONG 39 /* enough space to hold 2^128 - 1 in decimal */ #define FMT_LEN ((char *) 0) /* convenient abbreviation */ extern unsigned int fmt_uint(); extern unsigned int fmt_xint(); extern unsigned int fmt_nbbint(); extern unsigned int fmt_ushort(); extern unsigned int fmt_xshort(); extern unsigned int fmt_nbbshort(); extern unsigned int fmt_ulong(); extern unsigned int fmt_xlong(); extern unsigned int fmt_nbblong(); extern unsigned int fmt_plusminus(); extern unsigned int fmt_minus(); extern unsigned int fmt_0x(); extern unsigned int fmt_strncpy(); extern unsigned int fmt_memcpy(); extern unsigned int fmt_vis(); extern unsigned int fmt_nvis(); extern unsigned int fmt_rvis(); extern unsigned int fmt_unrvis(); #endif multitee-3.0/getopt.c100644 1752 1752 6314 6472172447 13474 0ustar roverrover/* getopt.c, getopt.h: (yet another) improved getopt clone Daniel J. Bernstein, brnstnd@nyu.edu. No dependencies. Requires stdio. 10/20/91: Removed -DGETOPT_SHUT_UP. [sigh] 8/26/91: Added -DGETOPT_SHUT_UP. 8/25/91: Changed getopt to not skip blank arguments. 7/6/91: Baseline. getopt 1.0, public domain. No known patent problems. This is a clone of the usual getopt() functions. It includes opterr so that you can turn off error handling, optpos so that you can find out exactly where the processing is up to (instead of just which argument), optproblem so that you can accurately diagnose errors yourself, and optprogname so that you can set the program name for getopt-generated errors. getopt() takes much more care to ensure that all the variables still make sense upon errors and EOF. (optproblem is a character. If argv[optind] is 0, the problem is a missing argument; otherwise it's an illegal option character.) Unless you define GETOPTORIGDEF, the header file redefines all names so that you don't have to worry about conflicts with libc. Finally, the code is public-domain, so you should feel free to use these extra features in your own programs and just attach a copy of this. Note that optind and optpos can be read (or set) any time, but the official getopt interface only defines optind when getopt() returns EOF. We define optproblem only when getopt() returns '?', optarg all the time, and optprogram only after getopt() has been called at least once. */ #include /* for EOF and stderr---talk about immodularity! */ #include "getopt.h" int optind = 1; int optpos = 0; int opterr = 1; char *optarg = 0; int optproblem = 0; char *optprogname = 0; int opteof = EOF; int getopt(argc,argv,opts) int argc; char **argv; char *opts; { int c; char *s; optarg = 0; if (!optprogname) { optprogname = *argv; if (!optprogname) /* oh boy */ optprogname = ""; /*XXX*/ for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1; } if (!argv || (optind >= argc) || !argv[optind]) return opteof; while (optpos && !argv[optind][optpos]) { /* we simply skip blank arguments... not any more */ ++optind; optpos = 0; if ((optind >= argc) || !argv[optind]) return opteof; } if (!optpos) { if (argv[optind][0] != '-') return opteof; ++optpos; c = argv[optind][1]; if ((c == '-') || (c == 0)) { /* XXX: this behavior of "-" is stupid */ if (c) ++optind; optpos = 0; return opteof; } /* otherwise c is reassigned below */ } c = argv[optind][optpos]; ++optpos; s = opts; while (*s) { if (c == *s) { if (s[1] == ':') { optarg = argv[optind] + optpos; ++optind; optpos = 0; if (!*optarg) { optarg = argv[optind]; if ((optind >= argc) || !optarg) /* argument past end */ { optproblem = c; if (opterr) fprintf(stderr,"%s: option requires an argument -- %c\n" ,optprogname,c); return '?'; } ++optind; } } return c; } ++s; if (*s == ':') ++s; } optproblem = c; if (opterr) fprintf(stderr,"%s: illegal option -- %c\n",optprogname,c); return '?'; } multitee-3.0/getopt.h100644 1752 1752 713 6472172447 13456 0ustar roverrover#ifndef GETOPT_H #define GETOPT_H #ifndef GETOPTORIGDEF #define getopt getoptmine #define optarg getoptarg #define optind getoptind #define opterr getopterr #define optpos getoptpos #define optproblem getoptproblem #define optprogname getoptprogname #define opteof getopteof #endif extern int getopt(); extern char *optarg; extern int optind; extern int opterr; extern int optpos; extern int optproblem; extern char *optprogname; extern int opteof; #endif multitee-3.0/multitee.c100644 1752 1752 23530 6472172447 14041 0ustar roverrover/* multitee.c: send multiple inputs to multiple outputs Daniel J. Bernstein, brnstnd@nyu.edu. Depends on sigsched.h, sigdfl.h, getopt.h, ralloc.h, scan.h, fmt.h, sod.h. Requires UNIX: read(), write(), perror(). 7/22/91: Baseline. multitee 3.0, public domain. No known patent problems. This is a complete rewrite of multitee 2.0, as distributed in comp.sources.unix volume 22. Documentation in multitee.1. XXX: should use optprogname */ #include #include "sigsched.h" #include "sigdfl.h" #include "getopt.h" #include "ralloc.h" #include "scan.h" #include "fmt.h" #include "sod.h" #include extern int errno; #ifndef EWOULDBLOCK #define EWOULDBLOCK 0 #endif #ifndef EPIPE #define EPIPE 0 #endif static int bufsize = 8192; /* We take pains to avoid all I/O copies. Each live buffer has an ending position; the buffer is read into as long as the ending position isn't the actual end of the buffer. Each live buffer also has an output schedule count, and each output descriptor for the buffer has a starting position. As soon as data is read in, it's scheduled to be written out on each of the output descriptors which isn't already scheduled. The schedule count is checked at this time and whenever it is decremented; if it is 0, the buffer is emptied, and all starting positions are set to 0. Upon an output scheduling, the buffer is written from the starting position to the ending position. (If this produces an error, die, except in cases like EINTR.) The starting position is incremented by the write count. If this brings it to the ending position, the schedule count is decremented. Otherwise the write is rescheduled. */ int flagverbose; void outofmem() { static char mfoom[] = "multitee: fatal: out of memory\n"; if (flagverbose) write(2,mfoom,sizeof(mfoom)); /* XXX: if it fails, tough luck */ exit(3); } void readerr(fd) int fd; { static char mwre[100 + FMT_ULONG]; char *t; if (!flagverbose) return; t = mwre + fmt_strncpy(mwre,"multitee: warning: cannot read descriptor "); t += fmt_uint(t,fd); *t = 0; perror(mwre); /*XXX*/ } void writeerr(fd) int fd; { static char mwwe[100 + FMT_ULONG]; char *t; if (!flagverbose) return; t = mwwe + fmt_strncpy(mwwe,"multitee: warning: cannot write descriptor "); t += fmt_uint(t,fd); *t = 0; perror(mwwe); /*XXX*/ } struct iosc { int in; int out; int flagsched; int startpos; int breakpipe; int dead; } ; SODdecl(intstack,int); SODdecl(ioscstack,struct iosc); ioscstack *fdio; intstack *fdeof; int *fdlive; int *fdendpos; int *fdschedcnt; char **fdbuf; int fdinmax; void nomorewrite(fdout) int fdout; { int i; ioscstack iosc; for (i = 0;i <= fdinmax;++i) if (fdlive[i]) for (iosc = fdio[i];iosc;iosc = SODnext(iosc)) if (SODdata(iosc).out == fdout) SODdata(iosc).dead = 1; } /* forward declarations of rescheduling routines */ extern void donoread(); extern void checksched(); extern void doeof(); extern void dowritesched(); extern void dowriteunsched(); /* I/O routines */ void readit(fd) int fd; { int r; int endpos; ioscstack iosc; endpos = fdendpos[fd]; if (fdlive[fd]) { r = read(fd,fdbuf[fd] + endpos,bufsize - endpos); /* XXX: this'll be slow if endpos is not aligned */ if (r == -1) { if (errno == EWOULDBLOCK) return; /* sample case where this can happen: someone reads before we do */ readerr(fd); r = 0; } } else r = 0; if (r == 0) { donoread(fd); doeof(fd); return; } fdendpos[fd] = endpos + r; if (fdio[fd]) { for (iosc = fdio[fd];iosc;iosc = SODnext(iosc)) if (SODdata(iosc).flagsched == 0) /* ``scheduled to be written out on each of the output descriptors'' */ dowritesched(&(SODdata(iosc))); /* it says ``schedule count is checked at this time...'' */ /* but as an optimization, since fdio[fd], schedcnt must be positive */ } else fdendpos[fd] = 0; /* throwing it away immediately */ if (fdendpos[fd] >= bufsize) donoread(fd); /* ``buffer is read into as long as...'' */ } void writeit(p) ss_idptr p; { struct iosc *ioscp; int in; int w; ioscp = (ioscstack) p; in = ioscp->in; if (ioscp->dead) w = fdendpos[in] - ioscp->startpos; else w = write(ioscp->out,fdbuf[in] + ioscp->startpos,fdendpos[in] - ioscp->startpos); /*``buffer is written from the starting position to the ending position...''*/ if (w == -1) { if (errno == EWOULDBLOCK) w = write(ioscp->out,fdbuf[in],1); /* XXX: kludge alert! */ if (w == -1) { if ((errno == EPIPE) && ioscp->breakpipe) sigdfl(SIGPIPE); writeerr(ioscp->out); nomorewrite(ioscp->out); w = 0; } } if (w == 0) ; /* XXX: wtf? */ ioscp->startpos += w; /* ``starting pos is incremented by write count'' */ if (ioscp->startpos == fdendpos[in]) /* ``if this brings it to the ending position, schedcnt is decremented'' */ dowriteunsched(ioscp); } /* rescheduling routines */ void donoread(fd) int fd; { ss_unsched(ss_sigread(fd),readit,fd); } void checksched(fd) int fd; { ioscstack iosc; /* ``if it is 0, buffer is emptied, and all startpos are set to 0.'' */ if (!fdschedcnt[fd]) { if (fdendpos[fd] == bufsize) if (ss_schedwait(ss_sigread(fd),readit,fd,1) == -1) outofmem(); fdendpos[fd] = 0; for (iosc = fdio[fd];iosc;iosc = SODnext(iosc)) SODdata(iosc).startpos = 0; /* schedcnt must be 0 */ } } void doeof(fd) int fd; { intstack eof; fdlive[fd] = 0; /* just in case */ for (eof = fdeof[fd];eof;eof = SODnext(eof)) if (fdlive[SODdata(eof)]) { fdlive[SODdata(eof)] = 0; donoread(SODdata(eof)); if (ss_schedonce(ss_asap(),readit,SODdata(eof)) == -1) outofmem(); } } void dowritesched(ioscp) struct iosc *ioscp; { if (!ioscp->flagsched) ++fdschedcnt[ioscp->in]; ioscp->flagsched = 1; if (ss_schedvwait(ss_sigwrite(ioscp->out),writeit,0,0,(ss_idptr) ioscp,1) == -1) outofmem(); /* Exercise for the reader: Show that this ``works'' for signals but */ /* can fail miserably with interrupts. This is one situation where */ /* the behavior of multiple threads scheduled upon one signal is */ /* very important. */ } void dowriteunsched(ioscp) struct iosc *ioscp; { int in; ss_unschedv(ss_sigwrite(ioscp->out),writeit,0,0,(ss_idptr) ioscp); in = ioscp->in; ioscp->flagsched = 0; --fdschedcnt[in]; checksched(in); /* ``schedule count is checked [when] decremented...'' */ } /* and the function which pulls it all together */ SODdecl(opstack,struct { int fdin; int fd; int special; }); main(argc,argv) int argc; char *argv[]; { int opt; char ch; char *t; int fd; intstack fdinhead; intstack fdin; opstack iohead; opstack eofhead; opstack op; ioscstack iosc; /* XXXXXXXXX: set non-blocking I/O? */ fdinhead = 0; iohead = 0; eofhead = 0; flagverbose = 1; while ((opt = getopt(argc,argv,"b:vqQ")) != opteof) switch(opt) { case 'b': if (optarg[scan_uint(optarg,&bufsize)]) ; /* extra chars; no meaning at this time */ break; case 'v': flagverbose = 2; break; case 'q': flagverbose = 0; break; case 'Q': flagverbose = 1; break; case '?': /*XXX*/ default: exit(1); /*XXX*/ } argc -= optind; argv += optind; while (*argv) { fdin = SODalloc(intstack,fdin,ralloc); if (!fdin) outofmem(); t = *argv; t += scan_uint(t,&(SODdata(fdin))); SODpush(fdinhead,fdin); while (ch = *t) { op = SODalloc(opstack,op,ralloc); if (!op) outofmem(); SODdata(op).fdin = SODdata(fdin); SODdata(op).special = 0; ++t; t += scan_uint(t,&(SODdata(op).fd)); switch(ch) { case ':': SODdata(op).special = 1; case '-': case ',': SODpush(iohead,op); break; case 'e': SODpush(eofhead,op); break; /* XXX: here's the greatest possibilities for future development */ default: /* XXX: ? */ ; } } ++argv; } if (!fdinhead) exit(0); /* might as well take the easy way out */ fdinmax = SODdata(fdinhead); for (fdin = fdinhead;fdin;fdin = SODnext(fdin)) { fd = SODdata(fdin); if (fd > fdinmax) fdinmax = fd; } fdio = (ioscstack *) ralloc((fdinmax + 1) * sizeof(ioscstack)); if (!fdio) outofmem(); for (fd = 0;fd <= fdinmax;++fd) fdio[fd] = 0; fdeof = (intstack *) ralloc((fdinmax + 1) * sizeof(intstack)); if (!fdeof) outofmem(); for (fd = 0;fd <= fdinmax;++fd) fdeof[fd] = 0; fdlive = (int *) ralloc((fdinmax + 1) * sizeof(int)); if (!fdlive) outofmem(); for (fd = 0;fd <= fdinmax;++fd) fdlive[fd] = 0; fdendpos = (int *) ralloc((fdinmax + 1) * sizeof(int)); if (!fdendpos) outofmem(); for (fd = 0;fd <= fdinmax;++fd) fdendpos[fd] = 0; fdschedcnt = (int *) ralloc((fdinmax + 1) * sizeof(int)); if (!fdschedcnt) outofmem(); for (fd = 0;fd <= fdinmax;++fd) fdschedcnt[fd] = 0; fdbuf = (char **) ralloc((fdinmax + 1) * sizeof(char *)); if (!fdbuf) outofmem(); /* no fdbuf initialization needed */ while (fdinhead) { SODpop(fdinhead,fdin); fdlive[SODdata(fdin)] = 1; SODfree(fdin,rfree); } for (fd = 0;fd <= fdinmax;++fd) if (fdlive[fd]) if (!(fdbuf[fd] = ralloc(bufsize))) outofmem(); while (iohead) { SODpop(iohead,op); iosc = SODalloc(ioscstack,iosc,ralloc); if (!iosc) outofmem(); SODdata(iosc).in = SODdata(op).fdin; SODdata(iosc).out = SODdata(op).fd; SODdata(iosc).dead = 0; SODdata(iosc).breakpipe = SODdata(op).special; /*XXX*/ SODdata(iosc).startpos = 0; SODdata(iosc).flagsched = 0; SODpush(fdio[SODdata(op).fdin],iosc); SODfree(op,rfree); } while (eofhead) { SODpop(eofhead,op); fdin = SODalloc(intstack,fdin,ralloc); if (!fdin) outofmem(); SODdata(fdin) = SODdata(op).fd; SODpush(fdeof[SODdata(op).fdin],fdin); SODfree(op,rfree); } for (fd = 0;fd <= fdinmax;++fd) if (fdlive[fd]) ss_schedwait(ss_sigread(fd),readit,fd,1); ss_addsig(SIGPIPE); if (ss_exec() == -1) outofmem(); exit(0); } multitee-3.0/ralloc.c100644 1752 1752 7303 6472172447 13445 0ustar roverrover/* ralloc.c, ralloc.h: recovering alloc Daniel J. Bernstein, brnstnd@nyu.edu. Depends on sod.h. Requires malloc/free. 8/26/91: Changed exit() to _exit(). 8/26/91: Made rallocneverfail() overwrite any previous handler. 7/24/91: Added rallocneverfail(). 7/18/91: Baseline. ralloc 1.0, public domain. No known patent problems. Lots of library routines allocate space for temporary objects: compiled regular expressions, for example. They don't destroy the objects between each call---wouldn't it be a waste to reallocate and recompile those regular expressions on every single pattern match? But when space gets tight, you don't want all those temporary objects cluttering the heap. You've got to deallocate them as soon as possible. Sure, library X might have some deallocation routines---but if X is hidden below library Y and separate library A runs out of space, do you expect A to know about X and call X's routines? Of course not. How can A and X coordinate? The solution is ralloc. ralloc works just like malloc, except that when it runs out of memory, it tries to recover space from anyone who's willing to give a little slack. If f is a deallocation function, you can call rallocinstall(f), and ralloc(n) will call f() if there aren't n bytes free. f() should return a non-zero integer if it could free some memory, 0 if not. Several libraries can rallocinstall their deallocation routines, and ralloc will cycle between all of them. Make sure that f actually frees some memory if it returns non-zero---otherwise ralloc() will loop, trying f again and again and wondering why malloc() never has enough space. (In a future implementation I might add a loop counter and have ralloc give up after trying f enough times.) According to John F. Haugh, ralloc is a Bad Thing, because it inherently requires static variables, hence can't be put into a ``pure'' shared library. Face it, John: ralloc() solves a real problem, and if you can't put it in a shared library, it's not because ralloc() is somehow evil. It's because your shared libraries aren't good enough. */ #include "ralloc.h" #include "sod.h" extern char *malloc(); /*XXXX*/ extern void free(); typedef int (*foo)(); SODdecl(funlist,foo); static funlist funhead = 0; static funlist funlast = 0; /* last fun to successfully recover */ static int ralloccount = 0; int rcount() { return ralloccount; } void rfree(s) char *s; { /* This is for completeness, and for another reason: so that you only */ /* have to modify this file if you want a debugging malloc-free. */ --ralloccount; /* for instance */ free(s); } static int crit = 0; /* just to be safe */ static int (*neverfail)() = 0; static void die(n) unsigned n; { if (neverfail) neverfail(n); _exit(1); /*XXX*/ } char *ralloc(n) unsigned n; { char *t; funlist fun; if(t = malloc(n)) { ++ralloccount; return t; } if (crit) if (neverfail) die(n); else return 0; if (!funhead) if (neverfail) die(n); else return 0; crit = 1; fun = (funlast ? SODnext(funlast) : funhead); do { if(!fun) fun = funhead; if((*SODdata(fun))()) /* XXX: can we make use of args or return code? */ funlast = fun; else if(fun == funlast) { crit = 0; if (neverfail) die(n); else return 0; /* gaack! */ } fun = SODnext(fun); t = malloc(n); } while(!t); ++ralloccount; crit = 0; return t; } void rallocneverfail(f) int (*f)(); { neverfail = f; /* possibly overwriting previous handler */ } #define malloc ralloc int rallocinstall(f) int (*f)(); { funlist fun; fun = SODalloc(funlist,fun,ralloc); if(!fun) return -1; SODdata(fun) = f; SODpush(funhead,fun); funlast = funhead; /* need to set it to something */ return 0; } multitee-3.0/ralloc.h100644 1752 1752 401 6472172447 13422 0ustar roverrover#ifndef RALLOC_H #define RALLOC_H extern char *ralloc(); extern void rfree(); extern int rcount(); extern int rallocinstall(); extern void rallocneverfail(); #define RFREE(x) rfree((char *) (x)) #define RALLOC(t,x) ((t *) ralloc((x) * sizeof(t))) #endif multitee-3.0/scan.c100644 1752 1752 11725 6472172447 13140 0ustar roverrover/* scan.c, scan.h: scanning library Daniel J. Bernstein, brnstnd@nyu.edu. No dependencies. No environment requirements. 7/18/91: Baseline. scan 1.0, public domain. No known patent problems. XXX: still need floating-point scanning */ #include "scan.h" /* just to keep track of what special characters we're using */ #define zero '0' #define plus '+' #define minus '-' #define alow 'a' #define acap 'A' #define space ' ' #define tab '\t' #define xlow 'x' #define xcap 'x' /* Note that the digits here are defined as '0', '0' + 1, '0' + 2, etc. */ /* The letters are defined similarly, starting from 'a' and 'A'. */ /* This may produce unintuitive results with a weird character set. */ unsigned int scan_plusminus(s,sign) char *s; int *sign; { if (*s == plus) { *sign = 1; return 1; } if (*s == minus) { *sign = -1; return 1; } *sign = 1; return 0; } unsigned int scan_0x(s,base) char *s; unsigned int *base; { if (*s == zero) { if ((s[1] == xlow) || (s[1] == xcap)) { *base = 16; return 2; } *base = 8; return 1; } *base = 10; return 0; } unsigned int scan_ulong(s,u) char *s; unsigned long *u; { unsigned int pos; unsigned long result; unsigned long c; pos = 0; result = 0; while ((c = (unsigned long) (unsigned char) (s[pos] - zero)) < 10) { result = result * 10 + c; ++pos; } *u = result; return pos; } unsigned int scan_xlong(s,u) char *s; unsigned long *u; { unsigned int pos; unsigned long result; unsigned long c; pos = 0; result = 0; while (((c = (unsigned long) (unsigned char) (s[pos] - zero)) < 10) ||(((c = (unsigned long) (unsigned char) (s[pos] - alow)) < 6) &&(c = c + 10)) ||(((c = (unsigned long) (unsigned char) (s[pos] - acap)) < 6) &&(c = c + 10)) ) /* XXX: this gets the job done */ { result = result * 16 + c; ++pos; } *u = result; return pos; } unsigned int scan_nbblong(s,n,base,bext,u) char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned long *u; /* Note that n == 0 means scan forever. Hopefully this is a good choice. */ { unsigned int pos; unsigned long result; unsigned long c; pos = 0; result = 0; while (((c = (unsigned long) (unsigned char) (s[pos] - zero)) < base) ||(((c = (unsigned long) (unsigned char) (s[pos] - alow)) < bext) &&(c = c + base)) ||(((c = (unsigned long) (unsigned char) (s[pos] - acap)) < bext) &&(c = c + base)) ) /* XXX: this gets the job done */ { result = result * (base + bext) + c; ++pos; if (pos == n) break; } *u = result; return pos; } unsigned int scan_uint(s,u) char *s; unsigned int *u; { unsigned int pos; unsigned long result; pos = scan_ulong(s,&result); *u = result; return pos; } unsigned int scan_xint(s,u) char *s; unsigned int *u; { unsigned int pos; unsigned long result; pos = scan_xlong(s,&result); *u = result; return pos; } unsigned int scan_nbbint(s,n,base,bext,u) char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned int *u; { unsigned int pos; unsigned long result; pos = scan_nbblong(s,n,base,bext,&result); *u = result; return pos; } unsigned int scan_ushort(s,u) char *s; unsigned short *u; { unsigned int pos; unsigned long result; pos = scan_ulong(s,&result); *u = result; return pos; } unsigned int scan_xshort(s,u) char *s; unsigned short *u; { unsigned int pos; unsigned long result; pos = scan_xlong(s,&result); *u = result; return pos; } unsigned int scan_nbbshort(s,n,base,bext,u) char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned short *u; { unsigned int pos; unsigned long result; pos = scan_nbblong(s,n,base,bext,&result); *u = result; return pos; } unsigned int scan_charsetnskip(s,chars,n) char *s; char *chars; unsigned int n; { unsigned int pos; pos = 0; while (chars[s[pos]]) /* user's responsibility to check for null */ if (++pos == n) break; return pos; } unsigned int scan_noncharsetnskip(s,chars,n) char *s; char *chars; unsigned int n; { unsigned int pos; pos = 0; while (!chars[s[pos]]) /* again, user's responsibility to check for null */ if (++pos == n) break; return pos; } unsigned int scan_whitenskip(s,n) char *s; unsigned int n; { unsigned int pos; char c; pos = 0; while (((c = s[pos]) == space) || (c == tab)) /* XXX: this is slow */ if (++pos == n) break; return pos; } unsigned int scan_nonwhitenskip(s,n) char *s; unsigned int n; { unsigned int pos; char c; pos = 0; /* This is the only function without ``str'' in its name where we check specially for nulls. */ while ((c = s[pos]) && (c != space) && (c != tab)) /* XXX: this is slow */ if (++pos == n) break; return pos; } unsigned int scan_strncmp(s,t,n) char *s; char *t; unsigned int n; { unsigned int pos; char c; pos = 0; while ((c = s[pos]) && (c == t[pos])) if (++pos == n) break; return pos; } unsigned int scan_memcmp(s,t,n) char *s; char *t; unsigned int n; /* This is the only function where n == 0 means do nothing. */ { unsigned int pos; pos = 0; while (n) if (s[pos] != t[pos]) break; else { --n; ++pos; } return pos; } multitee-3.0/scan.h100644 1752 1752 1334 6472172447 13120 0ustar roverrover#ifndef SCAN_H #define SCAN_H extern unsigned int scan_uint(); extern unsigned int scan_xint(); extern unsigned int scan_nbbint(); extern unsigned int scan_ushort(); extern unsigned int scan_xshort(); extern unsigned int scan_nbbshort(); extern unsigned int scan_ulong(); extern unsigned int scan_xlong(); extern unsigned int scan_nbblong(); extern unsigned int scan_plusminus(); extern unsigned int scan_0x(); extern unsigned int scan_whitenskip(); extern unsigned int scan_nonwhitenskip(); extern unsigned int scan_charsetnskip(); extern unsigned int scan_noncharsetnskip(); /* XXX: these aren't unique in the first 8 characters. i don't care. */ extern unsigned int scan_strncmp(); extern unsigned int scan_memcmp(); #endif multitee-3.0/sigdfl.c100644 1752 1752 5305 6472172450 13433 0ustar roverrover/* sigdfl.c, sigdfl.h: default signal library Daniel J. Bernstein, brnstnd@nyu.edu. No dependencies. Requires BSD signal syscalls. 7/18/91: Baseline. sigdfl 1.0, public domain. No known patent problems. Documentation in sigdfl.3. If you port this to System V, you don't have to worry about stop signals; you can just have sigdfl_tstp() and friends return -1 with ENOTTY. You are, however, faced with an incomplete, nearly prehistoric signal interface. Have fun. */ #include #include "sigdfl.h" static int cont = 0; static sigcont() /* XXX: should declare with right signal type */ { cont = 1; } int sigdfl(sig) int sig; { int oldmask; struct sigvec oldvec; struct sigvec vec; struct sigvec contvec; if (sig == SIGCONT) return 0; /* strategy below simply cannot work for CONT */ if ((sig == SIGURG) || (sig == SIGCHLD) || (sig == SIGIO) #ifdef SIGWINCH || (sig == SIGWINCH) #endif ) return 0; /* the above signals are ignored */ /* XXX: If we're still going now, and sig can be delivered without */ /* killing the process and without stopping the process so that it'll */ /* receive CONT later, then we will enter an infinite loop. [sigh] */ /* XXX: put maximum time wastage on this? */ oldmask = sigblock(0); sigblock(~0); /* now we won't receive any signals */ vec.sv_handler = SIG_DFL; vec.sv_mask = ~0; vec.sv_flags = 0; if (sigvec(sig,&vec,&oldvec) == -1) if ((sig != SIGSTOP) && (sig != SIGKILL)) return -1; vec.sv_handler = sigcont; vec.sv_mask = ~0; vec.sv_flags = 0; if (sigvec(SIGCONT,&vec,&contvec) == -1) return -1; cont = 0; if (kill(getpid(),sig) == -1) return -1; /* now a sig should be queued, and we have control over sig and CONT */ /* exception: SIGSTOP and SIGKILL can't be blocked, so those signals might already have been delivered. in the SIGSTOP case, if we've reached this point, sigcont() might already have been run. that's why cont must be set to 0 before the kill(). */ /* after this next bit we may receive sig and/or CONT */ sigsetmask(~(sigmask(sig) | sigmask(SIGCONT))); /* in the near future, sig will in fact be received */ while (!cont) /* dead loop until we receive CONT */ ; /* XXX: there should be a syscall so we don't have to loop here */ sigblock(~0); /* now we won't receive any signals */ (void) sigvec(sig,&oldvec,&vec); /* we don't care if it fails */ (void) sigvec(SIGCONT,&contvec,&vec); /* now signal handlers are back to normal */ (void) sigsetmask(oldmask); return 0; } int sigdfl_tstp() { return sigdfl(SIGTSTP); } int sigdfl_stop() { return sigdfl(SIGSTOP); } int sigdfl_ttin() { return sigdfl(SIGTTIN); } int sigdfl_ttou() { return sigdfl(SIGTTOU); } int sigdfl_abrt() { return sigdfl(SIGABRT); } multitee-3.0/sigdfl.h100644 1752 1752 352 6472172450 13415 0ustar roverrover#ifndef SIGDFL_H #define SIGDFL_H extern int sigdfl(); extern int sigdfl_tstp(); extern int sigdfl_stop(); extern int sigdfl_ttin(); extern int sigdfl_ttou(); extern int sigdfl_abrt(); /* professional version of abort() */ #endif multitee-3.0/sigsched.c100644 1752 1752 31751 6472172450 14000 0ustar roverrover/* sigsched.c, sigsched.h: signal-schedule thread library Daniel J. Bernstein, brnstnd@nyu.edu. Depends on ralloc.h, sod.h, config/fdsettrouble.h. Requires BSDish environment: reliable signals, sig{vec,block,setmask}, select. 9/1/91: Added worst-case fdset, FD_ZERO, etc. definitions. 8/25/91: sigsched 1.1, public domain. 8/25/91: Fixed bug that sigs[sched->blah].r didn't force instant timeout. 8/25/91: Fixed bug that if select() returned -1 then fds were still checked. 7/21/91: Changed forever to a 1-hour wakeup. 7/19/91: Added isopen() to fix bug in case of bad descriptor. 7/18/91: Baseline. sigsched 1.0, public domain. No known patent problems. Documentation in sigsched.3. XXX: how well do we clean up upon ss_exec() exit? */ #include #include #include #include #include #include extern int errno; #include "config/fdsettrouble.h" #include "sigsched.h" #include "ralloc.h" #include "sod.h" /* XXX: should restore signal set exactly after ss_exec returns */ typedef int sigc_set; /*XXX */ #define sigc_ismember(x,i) (*(x) & (1 << ((i) - 1))) #define sigc_addset(x,i) (*(x) |= (1 << ((i) - 1))) #define sigc_emptyset(x) (*(x) = 0) /* sigprocmask(SIG_UNBLOCK,xxxx,(sigc_set *) 0); */ #define sigc_unblock(x) (sigsetmask(sigblock(0) & ~*(x))) /* sigprocmask(SIG_BLOCK,xxxx,(sigc_set *) 0); */ #define sigc_block(x) (sigblock(*(x))) #ifndef NSIG #define NSIG 64 /* it's not as if any sane system has more than 32 */ #endif #define NUMSIGS NSIG #ifndef FD_SETSIZE #define FD_SETSIZE 256 #endif #define NUMFDS FD_SETSIZE /* if select() can't handle it, we can't either */ #ifdef LACKING_FD_ZERO #define NFDBITS (sizeof(fd_mask) * NBBY) #define FD_SET(n,p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_ISSET(n,p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) bzero((caddr_t)(p),sizeof(*(p))) #endif #ifdef DESPERATION_FD_SET #undef NFDBITS #undef FD_SET #undef FD_ISSET #undef FD_ZERO #undef fd_set #define fd_set long #define FD_SET(n,p) ((*p) |= (1 << (n))) #define FD_ISSET(n,p) ((*p) & (1 << (n))) #define FD_ZERO(p) (*p = 0L) #endif #define ASAP 1 #define SIGNAL 2 #define READ 3 #define WRITE 4 #define EXCEPT 5 #define JUNK 6 #define EXTERN 7 typedef struct { ss_sig s; int r; } ss_sigplus; static ss_sigplus asap; static ss_sigplus sigs[NUMSIGS]; static ss_sigplus reads[NUMFDS]; static ss_sigplus writes[NUMFDS]; static ss_sigplus excepts[NUMFDS]; static ss_sigplus junk; /* special case for internal use */ static void initsigs() { int i; asap.s.type = ASAP; asap.s.u.n = 0; asap.r = 0; for (i = 0;i < NUMSIGS;++i) { sigs[i].s.type = SIGNAL; sigs[i].s.u.n = i; sigs[i].r = 0; } for (i = 0;i < NUMFDS;++i) { reads[i].s.type = READ; reads[i].s.u.n = i; reads[i].r = 0; } for (i = 0;i < NUMFDS;++i) { writes[i].s.type = WRITE; writes[i].s.u.n = i; writes[i].r = 0; } for (i = 0;i < NUMFDS;++i) { excepts[i].s.type = EXCEPT; excepts[i].s.u.n = i; excepts[i].r = 0; } junk.s.type = JUNK; junk.s.u.n = 0; junk.r = 0; } ss_sig *ss_asap() { return &(asap.s); } #define OKsig(i) ((i >= 0) && (i < NUMSIGS)) ss_sig *ss_signal(i) int i; { if (!OKsig(i)) return 0; return &(sigs[i].s); } #define OKfd(fd) ((fd >= 0) && (fd < NUMFDS)) ss_sig *ss_sigread(fd) int fd; { if (!OKfd(fd)) return 0; return &(reads[fd].s); } ss_sig *ss_sigwrite(fd) int fd; { if (!OKfd(fd)) return 0; return &(writes[fd].s); } ss_sig *ss_sigexcept(fd) int fd; { if (!OKfd(fd)) return 0; return &(excepts[fd].s); } void ss_externsetsig(sig,x) ss_sig *sig; ss_extern *x; { sig->type = EXTERN; sig->u.c = (char *) x; } struct sched { ss_sig *sig; ss_thread *t; union { ss_id i; ss_idptr p; } id; int flagi; int wait; } ; SODdecl(schedlist,struct sched); static schedlist schedhead = 0; static int schednum = 0; static int schedjunked = 0; static int numwait = 0; void ss_forcewait() { ++numwait; } void ss_unforcewait() { --numwait; } int ss_schedvwait(sig,t,flagi,i,p,wait) ss_sig *sig; ss_thread *t; int flagi; ss_id i; ss_idptr p; int wait; { schedlist s; if (sig->type == EXTERN) { ss_extern *x; x = (ss_extern *) sig->u.c; return x->sched(x,t,flagi,i,p,wait); } s = SODalloc(schedlist,s,ralloc); if (!s) return -1; SODdata(s).sig = sig; SODdata(s).t = t; if (SODdata(s).flagi = flagi) SODdata(s).id.i = i; else SODdata(s).id.p = p; SODdata(s).wait = wait; SODpush(schedhead,s); ++schednum; if (wait) ++numwait; return 0; } int ss_schedwait(sig,t,i,wait) ss_sig *sig; ss_thread *t; ss_id i; int wait; { return ss_schedvwait(sig,t,1,i,(ss_idptr) 0,wait); } int ss_sched(sig,t,i) ss_sig *sig; ss_thread *t; ss_id i; { return ss_schedvwait(sig,t,1,i,(ss_idptr) 0,0); } struct oncestuff { ss_sig *sig; ss_thread *t; ss_id i; } ; /* XXX: this is the same as some other struct */ static void once(p) ss_idptr p; { struct oncestuff *os; os = (struct oncestuff *) p; if (ss_unschedv(os->sig,once,0,0,p) == -1) ; /* impossible */ os->t(os->i); RFREE(os); } int ss_schedonce(sig,t,i) ss_sig *sig; ss_thread *t; ss_id i; { struct oncestuff *os; os = (struct oncestuff *) ralloc(sizeof(struct oncestuff)); if (!os) return -1; os->sig = sig; os->t = t; os->i = i; return ss_schedvwait(sig,once,0,0,(ss_idptr) os,1); } /* XXX: could rallocinstall() this, if it has the recvhead() test */ static int schedcleanup() { schedlist s; schedlist t; schedlist sprev; /* if (recvhead) return 0; XXX: needs recvhead in scope */ if (!schedjunked) return 0; sprev = 0; s = schedhead; while (s) { if (SODdata(s).sig == &(junk.s)) { if (sprev) { SODpop(SODnext(sprev),t); /* XXX: not part of official sod interface */ s = SODnext(sprev); } else { SODpop(s,t); schedhead = s; } SODfree(t,rfree); --schednum; --schedjunked; } else { sprev = s; s = SODnext(s); } } /* schednum -= schedjunked; now done dynamically inside loop */ /* schedjunked = 0; */ return 1; } static void nothing(id) ss_id id; { ; } int ss_unschedv(sig,t,flagi,i,p) ss_sig *sig; ss_thread *t; int flagi; ss_id i; ss_idptr p; { schedlist s; if (sig->type == EXTERN) { ss_extern *x; x = (ss_extern *) sig->u.c; return x->unsched(x,t,flagi,i,p); } for (s = schedhead;s;s = SODnext(s)) if (SODdata(s).sig == sig && SODdata(s).t == t) if (SODdata(s).flagi == flagi) if (flagi ? (SODdata(s).id.i == i) : (SODdata(s).id.p == p)) { SODdata(s).sig = &(junk.s); SODdata(s).t = nothing; /* just in case */ if (SODdata(s).wait) --numwait; SODdata(s).wait = 0; ++schedjunked; return 0; } return 1; } int ss_unsched(sig,t,i) ss_sig *sig; ss_thread *t; ss_id i; { return ss_unschedv(sig,t,1,i,(ss_idptr) 0); } static struct timeval timeout; static struct timeval instant = { 0, 0 }; static struct timeval forever = { 3600, 0 }; /* XXX: talk to me */ static void handle(i) int i; { timeout = instant; /* XXX: structure copying */ sigs[i].r = 1; } static sigc_set sigstorage; static sigc_set *xxxx = 0; int ss_addsig(i) int i; { if (!OKsig(i)) return -1; if (!xxxx) { xxxx = &sigstorage; sigc_emptyset(xxxx); } sigc_addset(xxxx,i); return 0; } static int isopen(fd) int fd; { /* XXX: should call this only if select() fails */ return fcntl(fd,F_GETFL,0) != -1; } SODdecl(recvlist,schedlist); int ss_exec() { int i; struct sigvec sv; recvlist recvhead; recvlist temp; schedlist sch; initsigs(); if (xxxx) { sigc_block(xxxx); sv.sv_handler = handle; sv.sv_mask = *xxxx; /* so handle won't interrupt itself */ sv.sv_flags = 0; /* XXX: Does anyone but me find it absolutely idiotic that POSIX doesn't provide a way to get each member of a signal set in turn? */ for (i = 0;i < NUMSIGS;i++) { if (sigc_ismember(xxxx,i)) if (sigvec(i,&sv,(struct sigvec *) 0) == -1) /*XXX: really trash orig? */ ; /* not our problem */ } } recvhead = 0; while (numwait) { if (recvhead) { int w; SODpop(recvhead,temp); sch = SODdata(temp); /* This is the only section where we call user code. */ #define DOIT \ if (SODdata(sch).flagi) \ SODdata(sch).t(SODdata(sch).id.i); \ else \ SODdata(sch).t(SODdata(sch).id.p); switch(SODdata(sch).sig->type) { case JUNK: break; /* has been unscheduled while waiting on the receive list */ case ASAP: DOIT break; case READ: if (reads[w = SODdata(sch).sig->u.n].r) DOIT reads[w].r = 0; break; case WRITE: if (writes[w = SODdata(sch).sig->u.n].r) DOIT writes[w].r = 0; break; case EXCEPT: if (excepts[w = SODdata(sch).sig->u.n].r) DOIT excepts[w].r = 0; break; case SIGNAL: if (sigs[w = SODdata(sch).sig->u.n].r) DOIT sigs[w].r = 0; /* ``after the end of the last...'' */ break; case EXTERN: /* by definition, an external library handles this */ break; default: /* XXX: huh? */ ; } SODfree(temp,rfree); } else { schedlist sp; static fd_set rfds; static fd_set wfds; static fd_set efds; static int maxfd; int r; if (schedjunked > 100) if (schednum / schedjunked < 3) (void) schedcleanup(); /* now's as good a time as any */ timeout = forever; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); maxfd = -1; for (sp = schedhead;sp;sp = SODnext(sp)) { switch(SODdata(sp).sig->type) { case JUNK: break; case ASAP: timeout = instant; break; case SIGNAL: if (sigs[SODdata(sp).sig->u.n].r) timeout = instant; break; case READ: if (isopen(SODdata(sp).sig->u.n)) FD_SET(SODdata(sp).sig->u.n,&rfds); if (SODdata(sp).sig->u.n > maxfd) maxfd = SODdata(sp).sig->u.n; break; case WRITE: if (isopen(SODdata(sp).sig->u.n)) FD_SET(SODdata(sp).sig->u.n,&wfds); if (SODdata(sp).sig->u.n > maxfd) maxfd = SODdata(sp).sig->u.n; break; case EXCEPT: if (isopen(SODdata(sp).sig->u.n)) FD_SET(SODdata(sp).sig->u.n,&efds); if (SODdata(sp).sig->u.n > maxfd) maxfd = SODdata(sp).sig->u.n; break; case EXTERN: break; default: /*XXX: huh? */ break; } } if (xxxx) sigc_unblock(xxxx); /* This is the only section where handle() can be called. */ /* XXX: If maxfd == -1, this select functions as a pause. */ /* XXX: If maxfd == -1 and timeout is instant, should skip select. */ /* XXX: Random bug of note: Real BSD systems will say that the fd is writable as soon as a network connect() fails. The first I/O will show the error (though it's rather stupid that you can't find out the error without doing I/O). What does Ultrix 4.1 do? It pauses for 75 seconds. Dolts. */ r = select(maxfd + 1,&rfds,&wfds,&efds,&timeout); /* XXX: does this necessarily prevent timeout race conditions? */ if (xxxx) sigc_block(xxxx); if (r == -1) { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); switch(errno) { case EINTR: /* fine, this will happen on any signal */ break; case EBADF: /* who knows? */ break; case EINVAL: /* simply impossible */ break; default: /*XXX*/ ; /* well, that was real useful */ } } for (sp = schedhead;sp;sp = SODnext(sp)) { switch(SODdata(sp).sig->type) { case JUNK: break; case ASAP: temp = SODalloc(recvlist,temp,ralloc); if (!temp) return -1; /*XXX*/ SODdata(temp) = sp; SODpush(recvhead,temp); break; case SIGNAL: if (sigs[SODdata(sp).sig->u.n].r) { temp = SODalloc(recvlist,temp,ralloc); if (!temp) return -1; /*XXX*/ SODdata(temp) = sp; SODpush(recvhead,temp); } break; case READ: if (FD_ISSET(SODdata(sp).sig->u.n,&rfds)) { FD_CLR(SODdata(sp).sig->u.n,&rfds); reads[SODdata(sp).sig->u.n].r = 1; temp = SODalloc(recvlist,temp,ralloc); if (!temp) return -1; /*XXX*/ SODdata(temp) = sp; SODpush(recvhead,temp); } break; case WRITE: if (FD_ISSET(SODdata(sp).sig->u.n,&wfds)) { FD_CLR(SODdata(sp).sig->u.n,&wfds); writes[SODdata(sp).sig->u.n].r = 1; temp = SODalloc(recvlist,temp,ralloc); if (!temp) return -1; /*XXX*/ SODdata(temp) = sp; SODpush(recvhead,temp); } break; case EXCEPT: if (FD_ISSET(SODdata(sp).sig->u.n,&efds)) { FD_CLR(SODdata(sp).sig->u.n,&efds); excepts[SODdata(sp).sig->u.n].r = 1; temp = SODalloc(recvlist,temp,ralloc); if (!temp) return -1; /*XXX*/ SODdata(temp) = sp; SODpush(recvhead,temp); } break; case EXTERN: break; default: break; } } } } if (xxxx) sigc_unblock(xxxx); /* XXX: should put this at other returns as well */ return 0; } multitee-3.0/sigsched.h100644 1752 1752 1334 6472172450 13757 0ustar roverrover#ifndef SIGSCHED_H #define SIGSCHED_H typedef struct { int type; union { int n; char *c; } u; } ss_sig; typedef struct { int (*sched)(); int (*unsched)(); union { int n; char *c; } u; } ss_extern; typedef void ss_thread(); typedef int ss_id; typedef char *ss_idptr; extern ss_sig *ss_asap(); extern ss_sig *ss_signal(); extern ss_sig *ss_sigread(); extern ss_sig *ss_sigwrite(); extern ss_sig *ss_sigexcept(); extern int ss_addsig(); extern void ss_externsetsig(); extern int ss_schedvwait(); extern int ss_schedwait(); extern int ss_sched(); extern int ss_schedonce(); extern int ss_unschedv(); extern int ss_unsched(); extern void ss_forcewait(); extern void ss_unforcewait(); extern int ss_exec(); #endif multitee-3.0/sod.h100644 1752 1752 774 6472172450 12742 0ustar roverrover#ifndef SOD_H #define SOD_H /* a half-hearted attempt at a generic stack library */ #define SODdecl(foostack,foo) \ typedef struct foostack { struct foostack *next; foo data; } *foostack /* note that user must supply semicolon */ #define SODnext(x) ((x)->next) #define SODdata(x) ((x)->data) #define SODalloc(t,x,ralloc) ((t) ((ralloc)(sizeof(*x)))) #define SODpush(x,y) ((y)->next = (x),(x) = (y)) #define SODpop(x,y) ((y) = (x),(x) = (x)->next) #define SODfree(u,rfree) ((rfree)((char *)(u))) #endif multitee-3.0/tee.c100644 1752 1752 4624 6472172450 12743 0ustar roverrover/* tee.c: clone of tee program Daniel J. Bernstein, brnstnd@nyu.edu. Depends on getopt.h, ralloc.h, fmt.h, sod.h. Requires multitee. Otherwise portable to any UNIX system. 7/22/91: Baseline. tee 1.0, public domain. No known patent problems. Documentation in tee.1. XXX: should use optprogname for program name */ #include /* for fprintf(stderr,...) */ #include /* for SIGINT */ #include #ifndef O_WRONLY #define O_WRONLY 1 /* XXX: aargh */ #endif #include "getopt.h" #include "ralloc.h" #include "fmt.h" #include "sod.h" SODdecl(argstack,char *); main(argc,argv,envp) int argc; char *argv[]; char *envp[]; { int opt; int flagappend; int flagign; argstack arghead; argstack arg; char *t; int fd; unsigned int numargs; static char problem[1024]; flagappend = 0; flagign = 0; while ((opt = getopt(argc,argv,"ai")) != opteof) switch(opt) { case 'a': flagappend = 1; break; case 'i': flagign = 1; break; case '?': default: exit(1); } argc -= optind; argv += optind; arghead = 0; if (flagign) signal(SIGINT,SIG_IGN); numargs = 2; while (*argv) { fd = open(*argv,O_WRONLY | O_CREAT | (flagappend ? O_APPEND : O_TRUNC),0666); if (fd == -1) { t = problem; t += fmt_strncpy(t,"tee: warning: cannot open ",0); t += fmt_strncpy(t,*argv,(problem + sizeof(problem)) - t - 3); *t = 0; perror(problem); } else { arg = SODalloc(argstack,arg,ralloc); if (!arg) { fprintf(stderr,"tee: fatal: out of memory\n"); exit(3); } SODdata(arg) = ralloc(3 + fmt_uint(FMT_LEN,fd)); if (!SODdata(arg)) { fprintf(stderr,"tee: fatal: out of memory\n"); exit(3); } t = SODdata(arg); t += fmt_strncpy(t,"0:",2); t += fmt_uint(t,fd); *t = 0; SODpush(arghead,arg); ++numargs; } ++argv; } argv = RALLOC(char *,numargs + 1); if (!argv) { fprintf(stderr,"tee: fatal: out of memory\n"); exit(3); } argv[numargs] = 0; while (arghead) { SODpop(arghead,arg); argv[--numargs] = SODdata(arg); SODfree(arg,rfree); } argv[--numargs] = "0:1"; argv[--numargs] = "multitee"; /* numargs is now 0 */ execvp(argv[0],argv); t = problem; t += fmt_strncpy(t,"tee: fatal: cannot execute ",0); t += fmt_strncpy(t,*argv,(problem + sizeof(problem)) - t - 3); *t = 0; perror(problem); exit(4); } multitee-3.0/multitee.1100644 1752 1752 7250 6472172450 13732 0ustar roverrover.TH multitee 1 .SH NAME multitee \- send multiple inputs to multiple outputs .SH SYNTAX multitee [ \fB\-b\fIsize ] [ \fB\-vQq\fI ] [ \fIfd-fd,fd,fd... ] ... .SH DESCRIPTION .B multitee sends multiple inputs to multiple outputs. Given an argument of the form .I fdin-fdout,fdout,fdout... it will send all input on file descriptor .I fdin to each descriptor .I fdout. It will exit when all .I fdin are closed. Several arguments may specify outputs from the same .I fdin. .I -fdout and .I ,fdout are equivalent. If there is an error of any sort (including SIGPIPE) in writing to .I fdout, .B multitee prints a warning on stderr and forgets .I fdout entirely. (This doesn't affect reads on .I fdin.) If .I -fdout is replaced by .I :fdout then .B multitee will exit upon any SIGPIPEs from that descriptor. Furthermore, .I \fBe\fIfd means that as soon as .I fdin reaches end of file, .I fd is considered to reach EOF as well. .B multitee will warn about any input errors and then treat them like EOF. Unlike .I tee, .B multitee tries its best to continue processing all descriptors even while some of them are blocked. However, it will get stuck reading if someone else is reading the descriptor and grabs the input first; it will get stuck writing if an input packet does not fit in an output pipe. (If the output descriptor has NDELAY set, and .B multitee receives EWOULDBLOCK, it writes one byte at a time to avoid pipe synchronization problems.) While it is tempting to set the descriptors to non-blocking mode, this is dangerous: other processes using the same open file may not be able to deal with NDELAY. It is incredible that none of the major UNIX vendors or standards committees has come up with true per-process non-blocking I/O. (Under BSD 4.3 and its variants, multitee could send timer signals to itself rapidly to interrupt any blocking I/O. However, this cannot work under BSD 4.2, and is generally more trouble than it's worth.) A program can set NDELAY before invoking .B multitee if it knows that no other processes will use the same open file. .B multitee will also temporarily stop reading an input descriptor if more than 8192 bytes are pending on one of its output descriptors. This does not affect independent .I fdin-fdout pairs. .B multitee has several flags: .TP 12 \fB\-b\fIsize Change input buffer size from 8192 to .I size. Unlike the previous version of .B multitee, this version does not require output buffers, and does not copy bytes anywhere between read() and write(). .TP \fB\-v\fI Verbose. .TP \fB\-q\fI Quiet. .B multitee will not use stderr in any way (except, of course, if descriptor 2 is specified in an argument). .TP \fB\-Q\fI Normal level of verbosity. .PP .SH "EXIT VALUE" 0 normally. 1 for usage messages. 3 if .B multitee runs out of memory. 4 in various impossible situations. .SH DIAGNOSTICS .TP .I fatal: out of memory .B multitee has run out of memory. .TP .I warning: cannot read descriptor Self-explanatory. .TP .I warning: cannot write descriptor Self-explanatory. .SH EXAMPLES .EX multitee 0-1,4,5 4>foo 5>bar .EE .PP Same as .I tee foo bar except for better blocking behavior. .PP .EX multitee 0:1 3:1 4:1,2 6:7 .EE .PP Merge several sources into the output, meanwhile copying 6 to 7 and recording 4's input in 2. .PP .EX tcpclient servermachine smtp multitee 0:7 6:1e0 .EE .PP Same as .I mconnect on Suns. The e0 tells multitee to quit as soon as the network connection closes. .SH RESTRICTIONS .B multitee expects all descriptors involved to be open. Currently a closed descriptor acts like an open descriptor which can never be written to. .SH BUGS None known. .SH VERSION multitee version 3.0, 7/22/91. .SH AUTHOR Placed into the public domain by Daniel J. Bernstein. .SH "SEE ALSO" tee(1) multitee-3.0/tee.1100644 1752 1752 2026 6472172450 12653 0ustar roverrover.TH tee 1 .SH NAME tee \- record input in files .SH SYNTAX tee [ \fB\-ai\fI ] [ \fIfilename ] ... .SH DESCRIPTION .B tee copies its input to its output (without buffering), with records in zero or more files. Each .I filename argument specifies a record file. Any .I filename which does not exist is created. .B tee has two flags: .TP 12 \fB\-a\fI Append to each .I filename instead of truncating it. .TP \fB\-i\fI Ignore the SIGINT signal. .PP This version of .B tee uses .B multitee to perform all input and output. Any use of .B tee from the shell can be written in terms of .B multitee. .SH "EXIT VALUE" 0 normally. 1 for usage messages. 3 if .B tee runs out of memory. 4 if .B multitee cannot be executed. .SH DIAGNOSTICS .TP .I fatal: out of memory .B tee has run out of memory. .TP .I warning: cannot open file Self-explanatory. .B tee will continue processing other files in this case. .SH BUGS None known. .SH VERSION tee version 1.0, 7/22/91. .SH AUTHOR Placed into the public domain by Daniel J. Bernstein. .SH "SEE ALSO" multitee(1) multitee-3.0/sigsched.3100644 1752 1752 23523 6472172450 13716 0ustar roverrover.TH sigsched 3 .SH NAME sigsched \- signal-schedule (non-preemptive threads) library .SH SYNTAX .B #include ss_sig *\fBss_asap()\fR; .br ss_sig *\fBss_signal(\fIsigno\fB)\fR; .br ss_sig *\fBss_sigread(\fIfd\fB)\fR; .br ss_sig *\fBss_sigwrite(\fIfd\fB)\fR; .br ss_sig *\fBss_sigexcept(\fIfd\fB)\fR; int \fBss_addsig(\fIsigno\fB)\fR; int \fBss_schedvwait(\fIsig,t,flagi,i,p,wait\fB)\fR; .br int \fBss_schedwait(\fIsig,t,i,wait\fB)\fR; .br int \fBss_sched(\fIsig,t,i\fB)\fR; int \fBss_schedonce(\fIsig,t,i\fB)\fR; int \fBss_unschedv(\fIsig,t,flagi,i,p\fB)\fR; .br int \fBss_unsched(\fIsig,t,i\fB)\fR; void \fBss_externsetsig(\fIsig,x\fB)\fR; int \fBss_exec()\fR; void \fBss_forcewait()\fR; .br void \fBss_unforcewait()\fR; ss_thread \fI*t\fP; .br ss_sig \fI*sig\fP; .br ss_extern \fI*x\fP; .br int \fIflagi\fP; .br int \fIsigno\fP; .br int \fIfd\fP; .br ss_id \fIi\fP; .br ss_idptr \fIp\fP; .br int \fIwait\fP; .SH DESCRIPTION .B sigsched implements the signal-schedule programming model, otherwise known as non-preemptive threads, otherwise known as event-based programming. A thread is scheduled to execute upon receipt of a signal (occurrence of an event). Separate threads do not interrupt each other. All they can do is schedule more threads. .B sigsched supports far more flexible signals than C normally provides under UNIX. ``File descriptor 2 is writable'' is a signal, for example. Furthermore, threads do not have to be written to handle a signal at any moment, so code written to use .B sigsched can be fully optimized. In contrast, preemptive thread models (including UNIX's usual signal handling) prevent optimizations involving global variables. In general, a ``signal'' is any persistent condition. The ``file descriptor 2 is writable'' signal starts when the pipe is created, persists at least until the next I/O, finishes when the pipe is written to capacity, restarts when the pipe is read, and so on. UNIX signals are examples of .I thread-lowered signals. For example, SIGINT starts (is raised) when some process executes kill(pid,SIGINT), and finishes (is lowered) just before process pid calls the appropriate signal handler (thread). Note that if another process calls kill(pid,SIGINT) before the first one is delivered, the signal merely persists. It is not delivered twice, as after the first delivery the signal condition has been turned off and can't be redelivered. Any number of kill()s may be absorbed into one delivery in this way. With .B sigsched, the program can schedule a thread to execute upon receipt of a signal. .B ss_schedvwait() and .B ss_unschedv() schedule and unschedule threads. .B ss_exec() then executes one scheduled thread after another, as described below. It exits when there are no ``important'' threads left to execute. .B ss_schedvwait(\fIsig,t,flagi,i,p,wait\fB) schedules the thread .I t to execute with integer identifier .I i or pointer identifier .I p as soon as condition .I sig exists. This is an ``important'' thread if .I wait is nonzero. .I sig is of type .B ss_sig *; various functions produce signals of this type. .I t is of type .B ss_thread *, defined as a function returning void; it is called as .I t(i) if .I flagi is nonzero, or .I t(p) if .I flagi is zero. .I i is an integer, which must be zero if .I flagi is; .I p is a character pointer, which must be a null pointer if .I flagi is nonzero. .B defines the .B ss_sig and .B ss_thread types; it also abbreviates int as .B ss_id and char * as .B ss_idptr. .B ss_schedvwait normally returns 0, but will return -1 in case of a memory allocation failure. .B ss_unschedv(\fIsig,t,flagi,i,p\fB) unschedules the thread .I t previously scheduled to execute with identifier .I i or .I p as soon as condition .I sig existed. .I flagi, .I i, and .I p must follow the same rules as above. .B ss_unschedv returns 0 if the unschedule was successful, 1 if there was no such thread. The effects are currently undefined if a thread is scheduled more than once for the same signal with the same identifier. .B ss_exec() executes one thread after another, with no interruptions. It calls .I t(id) only if, for some signal .I sig, (1) .I t(id) is scheduled to execute upon .I sig; (2) condition .I sig has existed sometime between the end of the last call of a thread scheduled upon .I sig and the beginning of this call to .I t(id). If a thread has just finished executing and .B ss_exec can call one or more threads under the above restrictions, it will choose one and call that, unless every scheduled thread has a wait value of 0 (i.e., there are no important threads scheduled). In the latter case (including, for example, when there are no threads scheduled at all), .B ss_exec() immediately returns 0. It returns -1 in case of a memory allocation failure; in that case its internal structures may be permanently corrupted, and .B ss_exec may not be called again in the same program. If no threads can execute at a given moment, but if some thread is scheduled with a non-zero wait value, .B ss_exec has to wait for a signal to arrive. As an optimization, it will block the process until some thread can execute, rather than actively polling the set of signals. Note that if several threads are scheduled to execute upon one signal, and the signal suddenly exists, one of the threads will execute. If the signal turns off before the end of that thread, the other threads scheduled upon the signal will not execute. This is always true for thread-lowered signals. This behavior stands in marked contrast to the behavior of interrupts---upon an interrupt, all the scheduled threads would be executed. Each signal provides its own scheduling guarantees. For instance, under this implementation, any (waiting) thread scheduled on the signal .I ss_asap() will in fact execute at some point, provided that no thread blocks or loops forever. There is no way to keep pushing .I ss_asap() farther and farther into the future by scheduling other threads. On the other hand, .I ss_asap() will never flush out the other builtin signals. .B sigsched provides several builtin signals: .B ss_asap() returns a (pointer to a) signal which always exists. .B ss_signal(\fIsigno\fB) returns a thread-lowered signal which is true when UNIX signal .I signo is received. .B ss_sigread(\fIfd\fB) returns a signal which is true when .I fd is open and readable, as defined by .I select(); similarly for .B ss_sigwrite and .B ss_sigexcept. In order for .B sigsched to handle UNIX signal .I signo, you must call .B ss_addsig(\fIsigno\fB) before calling .B ss_exec(). .B ss_addsig will discard the old signal handler; later, .B ss_exec will not restore the handler upon exiting, and may leave the signal blocked or unblocked. .B ss_addsig will return 0 normally, -1 if .I signo is not in the right range for signals. If another library makes use of .B ss_signal with .B sigsched, it should provide a mandatory initialization routine which calls .B ss_addsig. .B ss_schedvwait and .B ss_unschedv can be abbreviated in common cases. .B ss_schedwait(\fIsig,t,i,wait\fB) is the same as .B ss_schedvwait(\fIsig,t,1,i,(ss_idptr)0,wait\fB). .B ss_sched(\fIsig,t,i\fB) is the same with .I wait set to 0; it is commonly used for handling user signals. .B ss_unsched(\fIsig,t,i\fB) is the same as .B ss_unschedv(\fIsig,t,1,i,(ss_idptr)0\fB). .B ss_schedonce(\fIsig,t,i\fB) is similar to .B ss_sched but is in fact implemented on top of .B ss_schedvwait with an independent mechanism. Each call to .B ss_schedonce schedules .I t upon a new signal which starts when .I sig does and exists only until .I t(i) is executed. After the first execution the new signal disappears. The new signal cannot be unscheduled. .B ss_forcewait() tells .B sigsched that something important is going on outside .B sigsched and that .B ss_exec should not exit. .B ss_unforcewait() negates a previous .B ss_forcewait(). .B ss_forcewait() and .B ss_unforcewait() control a counter, not a flag, so independent libraries can use them, but each library should be careful to use as many of one call as of the other. These functions must not be used outside .B ss_exec(). .B ss_externsetsig(sig,x) creates a new signal in the .B ss_sig pointed to by .I sig. .I x points to an .B ss_extern, which is defined as follows in .B : .PP .EX typedef struct { int (*sched)(); int (*unsched)(); union { int n; char *c; } u; } ss_extern; .EE .PP .I sched must be filled in with a scheduling function, which is called as .I (*sched)(x,t,flagi,i,p,wait) whenever .B ss_schedvwait(\fIsig,t,flagi,i,p,wait\fB) is called; similarly for .I unsched. Use of .I u is up to the caller. .I sched and .I unsched must observe the same rules as .B ss_schedvwait and .B ss_unschedv on any other signals: i.e., they must schedule threads upon a persistent condition, make sure that .I ss_exec does not exit if any important threads are scheduled, etc. Note that .B ss_externsetsig records .I x in .I sig, so .I x must point either to static memory or to memory which remains allocated as long as any thread is scheduled or executing upon .I sig. Memory management of the .I sig structure itself is up to the caller. It is recommended that library .I foo define a .B foo_sig structure, which contains .B ss_sig .I sig, .B ss_extern .I x, and any other necessary information for the signals defined by .I foo. Then .B foo_setsig(\fI&fsig,otherinfo\fB), where .I fsig is a .B foo_sig, should set up the .I otherinfo, set .I fsig.x.u.c to .I &fsig, set .I fsig.x.sched and .I fsig.x.unsched appropriately, and finish with .B ss_externsetsig(&fsig.sig,&fsig.x). That way the user can use .I &fsig.sig as the signal argument to .B sigsched functions, and when .I foo's scheduling routines are passed .I &fsig.x as a first argument, they can get to .I otherinfo through .I fsig.x.u.c. .B sigsched uses .B ralloc for all allocation. .SH VERSION sigsched 1.1, August 25, 1991. .SH AUTHOR Placed into the public domain by Daniel J. Bernstein. .SH "SEE ALSO" select(2), sigvec(2), ralloc(3) multitee-3.0/sigdfl.3100644 1752 1752 3252 6472172450 13352 0ustar roverrover.TH sigdfl 3 .SH NAME sigdfl \- invoke the default action for a signal .SH SYNTAX .B #include int \fBsigdfl(\fIsig\fB)\fR; int \fBsigdfl_ttou()\fR; .br int \fBsigdfl_tstp()\fR; .br int \fBsigdfl_ttin()\fR; .br int \fBsigdfl_stop()\fR; int \fBsigdfl_abrt()\fR; int \fIsig\fP; .SH DESCRIPTION .B sigdfl(\fIsig\fB) has exactly the same effect as what happens if the process isn't blocking .I sig, has SIG_DFL as the handler for .I sig, and then receives .I sig. In the case of stop signals (TSTP, TTIN, TTOU, STOP), .B sigdfl will absorb one or more CONT signals before returning. .B sigdfl is safe against multithreading. .B sigdfl has several interesting uses inside signal handlers. For instance, the handler for SEGV can clean up the tty and then call .B sigdfl(\fRSIGSEGV\fB). Unlike a simple .B abort(), this doesn't hide the reason for the core dump from the user. Similar comments apply to the stop signals. Note that processes handling a stop signal should take steps to ensure that they are back in the foreground after .B sigdfl returns. .B sigdfl returns 0 normally, -1 with errno set in case of various impossible errors. .B sigdfl_tstp() is the same as .B sigdfl(\fRSIGTSTP\fB). On systems not supporting SIGTSTP, .B sigdfl_tstp returns -1 with errno ENOTTY. Similar comments apply to .B sigdfl_stop(), .B sigdfl_ttin(), and .B sigdfl_ttou(). .B sigdfl_abrt() is a professional version of .B abort(); it will dump core with the ABRT signal, even if an ABRT handler is in place. SIG_DFL could, in principle, be implemented as .B sigdfl. .SH VERSION sigdfl 1.0, July 18, 1991. .SH AUTHOR Placed into the public domain by Daniel J. Bernstein. .SH "SEE ALSO" signal(3), abort(3)