norio -- do most of what rio does on a console. unfortunately this introduces readline-like functionality. the readline stuff should be removed. it's gross. Reference: /n/atom/patch/applied2013/norio Date: Tue Dec 24 04:43:05 CET 2013 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/aux/norio.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/norio.c Tue Dec 24 04:42:16 2013 @@ -0,0 +1,1232 @@ +#include +#include +#include +#include +#include <9p.h> + +#define Ctl(c) ((c) - '@') + +enum { + Hbufsz = 8192, + Stack = 16*1024, + Textsz = 64*1024, + Ibufsz = 256, + + Qcons = 1, + Qconsctl, + Qsnarf, + Qtext, + Qlast, +}; + +typedef struct Aux Aux; +typedef struct Cons Cons; +typedef struct Cp Cp; +typedef struct Hist Hist; +typedef struct Ibuf Ibuf; + +struct Ibuf { + int esc; + + char cut[Ibufsz]; + uint ncut; + + char ibuf[Ibufsz]; + char *ibufe; + char *tick; + char *e; +}; + +struct Hist { + char rbuf[Hbufsz+2]; + char *buf; + char *bufp; + char *bufe; + + char dir; + char reset; + int histfd; + uvlong off; + uvlong end; +}; + +struct Cons { + Ref; + int pid; + + Channel *ci; + Channel *co; + int ctlfd; + int raw; + + uvlong off0; + char *texte; + char *outpt; + char text[Textsz]; + + Ibuf; + Hist; +}; + +struct Aux { + QLock; + uint opens; + uint type; + Cons *c; +}; + +struct Cp { + char *name; + int fd; + char buf[8192+1]; + Channel *c; + Cons *cons; +}; + +static char cons[] = "/dev/cons"; +static char consctl[] = "/dev/consctl"; + +static char Einuse[] = "device or object already in use"; +static char Ebadarg[] = "bad arg in system call"; +static char flag['z']; + +int +nf0(void*, char *s) +{ + fprint(2, "note: %s\n", s); + syslog(0, "norio", "note: %s", s); + if(strcmp(s, "interrupted") == 0){ + fprint(2, "Interrupted!\n"); + return 1; + } + return 0; +} + +void +nf(void *u, char *s) +{ + if(nf0(u, s) != 0) + noted(NCONT); + else + noted(NDFLT); +} + +int +rc(void) +{ + int pid; + static char *rcv[] = {"rc", "-i", nil}; + + switch(pid = rfork(RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFFDG)){ + case -1: + sysfatal("fork: %r"); + case 0: + close(0); + open(cons, OREAD); + close(1); + open(cons, OWRITE); + close(2); + open(cons, OWRITE); + exec("/bin/rc", rcv); + sysfatal("exec: %r"); + default: + break; + } + return pid; +} + +void +interruptproc(void *v) +{ + Cons *c; + + c = v; + if(postnote(PNGROUP, c->pid, "interrupt") == -1) + fprint(2, "postnote: %r\n"); +} + +void +histreset(Hist *h) +{ + h->reset = 1; +} + +void +histinit(Hist *h) +{ + char *s; + + h->histfd = -1; + if(s = getenv("history")){ + h->histfd = open(s, OREAD); + free(s); + } + h->rbuf[0] = 0; + h->rbuf[sizeof h->rbuf - 1] = 0; + histreset(h); +} + +void +dohistreset(Hist *h) +{ + if(h->reset == 0) + return; + h->reset = 0; + h->dir = -1; + h->buf = h->rbuf+1; /* room for leading \n */ + h->bufp = h->bufe = h->buf; + h->off = seek(h->histfd, 0, 2); + h->end = h->off; +} + +void +histtoken(char *p, int n) +{ + char *e; + + e = p + n; + for(; p = memchr(p, '\n', n); ){ + *p++ = 0; + n = e - p; + } +} + +static char* +memrchr(char *buf, int c, int l) +{ + char *p; + + for(p = buf+l; --p >= buf; ) + if(*p == c) + return p; + return nil; +} + +static int +preadn(int fd, void *av, long n, vlong o) +{ + char *a; + long m, t; + + a = av; + t = 0; + while(t < n){ + m = pread(fd, a+t, n-t, o+t); + if(m <= 0){ + if(t == 0) + return m; + break; + } + t += m; + } + return t; +} + +int +histfil(Hist *h, int dir) +{ + char *p; + int n, blen; + vlong off, m; + + h->buf = h->rbuf+1; + p = h->buf; + blen = h->bufp - h->buf; + m = Hbufsz; + off = h->off; + if(dir == -1){ + if(off == 0) + return -1; + off += blen+1; + if(Hbufsz > off){ + // m -= Hbufsz - off - 1; + m = Hbufsz - off - 1; + off = 0; + }else + off -= Hbufsz; + }else{ + if(h->off == h->end) + return -1; + if(blen == 0) + blen = Hbufsz; + off += blen; + } + n = preadn(h->histfd, p, m, off); + if(n < 1){ + dohistreset(h); + return -1; + } + histtoken(p, n); + h->off = off; + h->bufe = p + n; + /* fix up file ends */ + if(off == 0){ + h->buf--; + *h->buf = 0; + } + if(off == h->end){ + // h->bufe++; + // *h->bufe = 0; + h->bufe[1] = 0; + } + h->bufp = dir == -1? h->bufe : h->buf; + return 0; +} + +int +histck(char *s) +{ + char *p; + int c; + Rune r; + + for(p = s; c = *p; p++){ + if(p - s >= Ibufsz) + return -1; + if(c < 0x20 || c == 0x7f) + return -1; + c = chartorune(&r, p); + if(c == 1 && r == Runeerror) + return -1; + } + return 0; +} + +char* +hprev(Hist *h) +{ + char *e, *p; + + if(h->histfd == -1) + return nil; + if(h->reset == 1){ + dohistreset(h); + h->reset = 0; + } + for(;;){ + for(;;){ + e = h->bufp - 1; + assert(e - h->rbuf >= 0); + p = memrchr(h->buf, 0, e - h->buf); + if(p == nil) + break; + h->bufp = p; + if(e - p >= 1 && p+1 < h->bufe){ + if(histck(p+1) == 0) + if(h->dir != 1){ + h->dir = -1; + return p+1; + } + h->dir = -1; + } + } + if(histfil(h, -1) == -1){ + h->dir = 0; + return nil; + } + } +} + +char* +hnext(Hist *h) +{ + char *e, *p; + + if(h->histfd == -1) + return nil; + if(h->reset == 1){ + dohistreset(h); + h->reset = 0; + } + for(;;){ + for(p = h->bufp + 1; ; p = e + 1){ + assert(h->bufe - p >= 0); + e = memchr(p, 0, h->bufe - p); + if(e == nil) + break; + h->bufp = e; + if(e - p >= 1 && p+1 < h->bufe){ + if(histck(p) == 0) + if(h->dir != -1){ + h->dir = 1; + return p; + } + h->dir = 1; + } + } + if(histfil(h, 1) == -1){ + h->dir = 0; + return nil; + } + } +} + +void +cleanibuf(Ibuf *i) +{ + i->ibufe = i->ibuf + sizeof i->ibuf; + i->tick = i->ibuf; + i->e = i->ibuf; +} + +void +dlog(char *fmt, ...) +{ + static int fd = -1; + va_list args; + + if(fd == -1) + fd = open("x", OWRITE); + if(fd == -1) + return; + + seek(fd, 0, 2); + + va_start(args, fmt); + vfprint(fd, fmt, args); + va_end(args); +} + +int +countrunes(char *s, char *e) +{ + int i, n; + Rune r; + + for(i = 0;; i++){ + n = chartorune(&r, s); + if((s += n) > e) + break; + } + return i; +} + +/* move by runes; return bytes */ +int +mtick(char *s, char *t, int m, int n) +{ + int i; + + i = t - s; + while(n++ < 0) + while(i > 0 && (s[--i]&0xc0) == 0x80) + ; + while(--n > 0) + while(i < m && (s[++i]&0xc0) == 0x80) + ; + return i - (t - s); +} + +int +move(Ibuf *i, int n) +{ + n = mtick(i->ibuf, i->tick, i->e - i->ibuf, n); + if(n == 0) + return -1; + i->tick += n; + return n < 0? 1: 3; +} + +int +bs(Ibuf *i) +{ + int n; + + n = mtick(i->ibuf, i->tick, 0, -1); + if(n == 0) + return -1; + memmove(i->tick + n, i->tick, i->e - i->tick); + i->tick += n; + i->e += n; + return 3; +} + +int +addstr(Ibuf *i, char *s) +{ + int n, r; + + n = strlen(s); + if(i->tick + n > i->ibufe) + n = i->ibufe - i->tick; + if(n == 0) + return -1; + r = i->e - i->tick; + memmove(i->tick + n, i->tick, r); + memmove(i->tick, s, n); + i->tick += n; + i->e += n; + return 3; +} + +void +cut(Ibuf *i, char *s, int n) +{ + if(n == 0) + return; + if(n > sizeof i->cut-1) + n = sizeof i->cut-1; + memmove(i->cut, s, n); + i->ncut = n; +} + +int +paste(Ibuf *i) +{ + if(i->ncut == 0) + return 0; + i->cut[i->ncut] = 0; + addstr(i, i->cut); + return 3; +} + +int +emitit(Channel *c, char *s) +{ + if(s) + s = strdup(s); + if(sendp(c, s) == -1){ + free(s); + return -1; + } + return 0; +} + +static int +scroll(Cons *c) +{ + char *p; + uint n, r; + + p = memchr(c->text, '\n', c->outpt - c->text); + if(p == nil) + return -1; + r = p - c->outpt; + n = c->outpt - p; + memmove(c->text, p, n); + c->outpt = c->text + n; + c->off0 += r; + return r; +} + +static void +addtext(Cons *c, char *s) +{ + int n; + + if(s == nil) + return; + n = strlen(s); + while(c->outpt + n > c->texte) + if(scroll(c) == -1){ + s = s + n - 100; + n = 100; + break; + } + memmove(c->outpt, s, n); + c->outpt += n; +} + +int +ichar(Cp *p, int c) +{ + char *t, *tl, *rp, *s, *o, buf[256*4], tbuf[512]; + int r, i, n, rv, esc, emit; + Cons *cn; + + cn = p->cons; + emit = 3; + r = cn->raw; + buf[0] = c; + buf[1] = 0; + s = o = buf; + tl = nil; + rv = 1; + + switch(c){ + case '\r': + if(flag['S']) + goto emit; + c = '\n'; + s = o = "\n"; + break; + default: + break; + } + + if(r == 0){ + esc = 0; + if(s == nil) + r = 1; + else switch(cn->esc << 8 | c){ + case 1<<8 | '[': + case 1<<8 | 'O': + esc = 2; + emit = 0; + break; + case 2<<8 | '3': + esc = 3; + emit = 0; + break; + case 3<<8 | '~': + if(cn->tick == cn->e){ + emit = 0; + break; + } + n = cn->e-cn->tick-1; + memmove(cn->tick, cn->tick+1, n); + cn->e--; + o = ""; + tl = " \b"; + break; + case 2<<8 | 'A': + case Ctl('P'): + s = hprev(cn); + if(s == nil) + return 1; + h: + n = cn->e - cn->tick; + memset(buf+0*n, ' ', n); + memset(buf+1*n, '\b', n + cn->tick - cn->ibuf); + n = 2*n + cn->tick - cn->ibuf; + memset(buf+n, ' ', cn->tick - cn->ibuf); + n += cn->tick - cn->ibuf; + memset(buf+n, '\b', cn->tick - cn->ibuf); + n += cn->tick - cn->ibuf; + memcpy(buf+n, s, strlen(s)); + n += strlen(s); + buf[n] = 0; + o = buf; + cleanibuf(cn); + addstr(cn, s); + break; + case 2<<8 | 'B': + case Ctl('N'): + s = hnext(cn); + if(s == nil) + s = ""; + goto h; + case 2<<8 | 'C': + case Ctl('F'): + buf[0] = cn->tick[0]; + buf[1] = 0; + emit = move(cn, 1); + o = buf; + break; + case 2<<8 | 'D': + case Ctl('B'): + emit = move(cn, -1); + o = "\b"; + break; + case Ctl('A'): + n = cn->tick-cn->ibuf; + for(i = 0; i < n;) + buf[i++] = Ctl('H'); + buf[i] = 0; + s = buf; + cn->tick = cn->ibuf; + emit = 1; + break; + case 0x7f: + case Ctl('C'): + // proccreate(interruptproc, cn, 4096); + // o = ""; + // s = nil; + // break; + interruptproc(cn); + if(sendp(p->c, nil) == -1) + sysfatal("sendp: %r"); + cleanibuf(cn); + return 0; + case Ctl('D'): + s = nil; + break; + case Ctl('E'): + cn->e[0] = 0; + o = cn->tick; + cn->tick = cn->e; + emit = 1; + break; + case Ctl('H'): + emit = bs(cn); + o = "\b \b"; + tl = " \b"; + break; + case Ctl('K'): + n = cn->e - cn->tick; + cut(cn, cn->tick, n); + memset(buf, ' ', n); + memset(buf+n, '\b', n); + buf[2*n] = 0; + s = buf; + cn->e = cn->tick; + break; + case Ctl('W'): + t = cn->tick; + for(; t > cn->ibuf; t--) + if(strchr(" \t\r\n", t[-1]) == nil) + break; + for(; t > cn->ibuf; t--) + if(strchr(" \t\r\n", t[-1]) != nil) + break; + n = cn->tick - t; + cut(cn, t, n); + memmove(t, cn->tick, n + cn->e-cn->tick); + cn->e -= n; + cn->tick -= n; + /* n = countrunes(cn->t, cn->tick); */ + if(n > 0){ + memset(buf+0*n, '\b', n); + buf[n] = 0; + o = buf; + memset(tbuf+0*n, ' ', n); + memset(tbuf+1*n, '\b', n); + tbuf[2*n] = 0; + tl = tbuf; + }else + o = nil; + break; + case Ctl('U'): + cut(cn, cn->ibuf, cn->e - cn->ibuf); + n = countrunes(cn->tick, cn->e); + move(cn, n); + for(i = 0; n-->0 && i < sizeof buf - 1;) + buf[i++] = ' '; + for(; i < sizeof buf - 1;){ + if(bs(cn) == -1) + break; + buf[i++] = Ctl('H'); + buf[i++] = ' '; + buf[i++] = Ctl('H'); + } + buf[i] = 0; + s = buf; + rv = 0; + break; + case Ctl('Y'): + emit = paste(cn); + o = cn->cut; + break; + case Ctl('['): + esc = 1; + emit = 0; + break; + case '\n': + cn->tick = cn->e; + default: + emit = addstr(cn, s); + break; + } + cn->esc = esc; + if(s == nil || s[0] == '\n'){ + cn->e[0] = 0; + if(emitit(p->c, cn->ibuf) == -1) + return -1; + r = 1; + histreset(cn); + } + }else + if(emitit(p->c, s) == -1) + return -1; +emit: + if(emit > 0) + if(cn->raw == 0){ + if(o && *o) + emitit(cn->co, o); + rp = nil; + cn->e[0] = 0; + if(r != 1 && emit&2 && cn->tick != cn->e){ + emitit(cn->co, cn->tick); + n = cn->e - cn->tick; + for(i = 0; i < n;) + buf[i++] = Ctl('H'); + buf[i] = 0; + rp = buf; + } + if(tl != nil) + emitit(cn->co, tl); + if(rp != nil) + emitit(cn->co, buf); + } + if(r) + cleanibuf(cn); + return rv; +} + +void +consiproc(void *v) +{ + char buf[32]; + int n, i; + Cp *c; + + c = v; + snprint(buf, sizeof buf, "consiproc %s", c->name); + threadsetname(buf); + for(;;){ + n = read(c->fd, c->buf, sizeof c->buf-1); + if(n == -1) + break; + for(i = 0; i < n; i++) + switch(ichar(c, c->buf[i])){ + case -1: + goto dead; + case 0: + goto dump; + } + dump:; + } +dead: +syslog(0, "norio", "%s exiting %s", buf, "dead"); +fprint(2, "%s: exiting\n", buf); + chanclose(c->c); + threadexitsall(""); +} + +void +consoproc(void *v) +{ + char *s, buf[32]; + int n; + Cp *c; + + c = v; + snprint(buf, sizeof buf, "consoproc %s", c->name); + threadsetname(buf); + for(;;){ + s = recvp(c->c); + if(s == nil) + break; + n = write(c->fd, s, strlen(s)); + free(s); + if(n == -1) + break; + } +syslog(0, "norio", "%s exiting %s", buf, s); +fprint(2, "%s: exiting\n", buf); + chanclose(c->c); + threadexits(""); +} + +Cons* +newcons(void) +{ + Cons *c; + + c = emalloc9p(sizeof *c); + memset(c, 0, sizeof *c); + + c->ci = chancreate(sizeof(char*), 1); + c->co = chancreate(sizeof(char*), 1); + + c->texte = c->text + sizeof c->text; + c->outpt = c->text; + + histinit(c); + cleanibuf(c); + return c; +} + +void +consread(Req *r, char *s) +{ + int n; + + n = 0; + if(s != nil) + n = strlen(s); + if(n > r->ifcall.count) + n = r->ifcall.count; + r->ofcall.count = n; + memmove(r->ofcall.data, s, n); +} + +void +fsread(Req *r) +{ + char *s; + Aux *a; + Cons *c; + + a = r->fid->file->aux; + c = a->c; + + switch(a->type){ + default: + abort(); + case Qcons: + s = recvp(c->ci); + addtext(c, s); + consread(r, s); + respond(r, nil); + free(s); + break; + case Qconsctl: + respond(r, "permission denied"); + break; + case Qsnarf: + c->cut[c->ncut] = 0; + readstr(r, c->cut); + if(histck(c->cut) == -1){ + c->ncut = 0; + respond(r, Ebadarg); + }else + respond(r, nil); + break; + case Qtext: + c->outpt[0] = 0; + readstr(r, c->text); + respond(r, nil); + break; + } +} + +char* +rqdup(Req *r) +{ + char *s; + + s = emalloc9p(r->ifcall.count + 1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = 0; + return s; +} + +long +addtext1(Cons *c, char *u, uint n, vlong off) +{ + char *p; + uint r; + + if(off == -1) + off = c->off0 + c->outpt - c->text; + if(off != 0) + off -= c->off0; + if(off < 0) + return -1; + if(n >= sizeof c->text || off > sizeof c->text){ + off = 0; + c->outpt = c->text; + } + + for(;;){ + p = c->outpt + off + n; + if(p < c->texte) + break; + if((r = scroll(c)) == -1){ + c->outpt = c->text; + break; + } + if(r > off){ + r -= off; + off = 0; + }else if(off > 0) + off -= r; + if(r > 0) + break; + } + for(;;){ + p = c->outpt + off + n; + if(p < c->texte) + break; + p = memchr(u, '\n', n); + if(p == nil){ + p = u + n - 100; + break; + } + n -= p - u; + c->off0 += p - u; + } + + memmove(c->text + off, p, n); + c->outpt = c->text + off + n; + return n; +} + +void +fswrite(Req *r) +{ + char *s, *err, e[ERRMAX]; + Aux *a; + Cons *c; + Fcall *f; + + a = r->fid->file->aux; + c = a->c; + + switch(a->type){ + default: + abort(); + case Qcons: + s = rqdup(r); + addtext(c, s); + if(sendp(c->co, s) == -1){ + responderror(r); + free(s); + }else{ + r->ofcall.count = r->ifcall.count; + respond(r, nil); + } + break; + case Qconsctl: + s = rqdup(r); + err = nil; + r->ofcall.count = r->ifcall.count; + if(strncmp(s, "rawon", 5) == 0) + c->raw = 1; + else if(strncmp(s, "rawoff", 6) == 0) + c->raw = 0; + else if(flag['l'] == 0 && write(c->ctlfd, s, strlen(s)) != strlen(s)){ + rerrstr(e, sizeof e); + err = e; + r->ofcall.count = -1; + } + free(s); + respond(r, err); + break; + case Qsnarf: + c->ncut = r->ifcall.count; + if(c->ncut > sizeof c->cut) + c->ncut = sizeof c->cut; + memcpy(c->cut, r->ifcall.data, c->ncut); + r->ofcall.count = c->ncut; + respond(r, nil); + break; + case Qtext: + f = &r->ifcall; + r->ofcall.count = addtext1(c, f->data, f->count, f->offset); + respond(r, nil); + break; + } +} + +void +fsopen(Req *r) +{ + Aux *a; + + a = r->fid->file->aux; + if(a == nil){ + respond(r, nil); + return; + } + + switch(a->type){ + default: + abort(); + case Qcons: + qlock(a); + a->opens++; + qunlock(a); + respond(r, nil); + break; + case Qconsctl: + qlock(a); + if(a->opens != 0){ + qunlock(a); + respond(r, Einuse); + return; + } + a->opens = 1; + qunlock(a); + respond(r, nil); + break; + case Qsnarf: + respond(r, nil); + break; + case Qtext: + if(r->ifcall.mode&OTRUNC) + a->c->texte = a->c->outpt = a->c->text; + respond(r, nil); + break; + } +//fprint(2, "r->fid %#p\n", r->fid); +// r->fid->aux = a; +} + +void +fsstat(Req *r) +{ + Aux *a; + + a = r->fid->file->aux; + if(a == nil){ + respond(r, nil); + return; + } + + switch(a->type){ + default: + abort(); + case Qcons: + case Qconsctl: + respond(r, nil); + break; + case Qsnarf: + r->d.length = a->c->ncut; + respond(r, nil); + break; + case Qtext: + r->d.length = a->c->outpt - a->c->text; + respond(r, nil); + break; + } + r->fid->aux = a; +} + +void +fsdestroyfid(Fid *f) +{ + Aux *a; + Cons *c; + + a = f->aux; + if(a == nil) + return; + c = a->c; + + switch(a->type){ + case Qcons: + qlock(a); + a->opens--; + qunlock(a); + break; + case Qconsctl: + qlock(a); + a->opens = 0; + c->raw = 0; /* consreset(c); */ + qunlock(a); + break; + case Qsnarf: + case Qtext: + break; + } +} + +void +fsend(Srv*) +{ + fprint(2, "END\n"); +} + +Srv fs = { +.open = fsopen, +.read = fsread, +.write = fswrite, +.stat = fsstat, +.destroyfid = fsdestroyfid, +.end = fsend, +}; + +void +reap(File *f) +{ + fprint(2, "reap %p f->aux %p\n", f ,f->aux); +} + +Cp* +newcp(Cons *cons, Channel *c, int fd, char *name) +{ + Cp *p; + + p = emalloc9p(sizeof *p); + memset(p, 0, sizeof *p); + p->c =c; + p->cons = cons; + p->fd = fd; + if(p->fd == -1) + sysfatal("open: %r"); + p->name = name; + return p; +} + +static Aux atab[Qlast]; + +void +mk(int type, Cons *c, char *name, int perm) +{ + Aux *a; + + a = atab + type; + a->type = type; + a->c = c; + closefile(createfile(fs.tree->root, name, "gremlin", perm, a)); +} + +void +usage(void) +{ + fprint(2, "usage: norio [Ssml]\n"); + exits(""); +} + +void +threadmain(int argc, char **argv) +{ + char *mtpt, *srv; + Cons *c; + Cp *p; + Waitmsg *w; + + mtpt = "/dev"; + srv = nil; + ARGBEGIN{ + case 'S': + case 'l': + flag[ARGC()] = 1; + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 's': + srv = EARGF(usage()); + break; + default: + usage(); + }ARGEND + if(argc != 0) + usage(); + + c = newcons(); + c->ctlfd = -1; + if(!flag['l']){ + c->ctlfd = open(consctl, OWRITE); + if(c->ctlfd == -1) + sysfatal("open: %r"); + write(c->ctlfd, "rawon", 5); + } + + close(0); + p = newcp(c, c->ci, open(cons, OREAD), cons); + proccreate(consiproc, p, Stack); + + close(1); + p = newcp(c, c->co, open(cons, OWRITE), cons); + close(2); + dup(1, 2); + proccreate(consoproc, p, Stack); + + fs.tree = alloctree("sys", "sys", DMDIR|0775, reap); + + mk(Qcons, c, "cons", 0666); + mk(Qconsctl, c, "consctl", 0222); + mk(Qsnarf, c, "snarf", 0666); + mk(Qtext, c, "text", 0666); + + threadpostmountsrv(&fs, srv, mtpt, MBEFORE); + c->pid = rc(); + threadnotify(nf0, 1); +// while(waitpid() != c->pid) +// ; + for(;;){ + w = wait(); + if(w == nil) + break; + if(w->msg && w->msg[0]) + print("rc exits: %s\n", w->msg); + free(w); + break; + } + threadexitsall(""); + exits(""); +} --- /sys/src/cmd/aux/mkfile Tue Dec 24 04:42:17 2013 +++ /sys/src/cmd/aux/mkfile Tue Dec 24 04:42:17 2013 @@ -25,6 +25,7 @@ msexceltables\ mswordstrings\ nfsmount\ + norio\ number\ olefs\ pcmcia\