| #include "stdinc.h" |
| |
| #include "9.h" |
| |
| enum { |
| Nl = 256, /* max. command line length */ |
| Nq = 8*1024, /* amount of I/O buffered */ |
| }; |
| |
| typedef struct Q { |
| QLock lock; |
| Rendez full; |
| Rendez empty; |
| |
| char q[Nq]; |
| int n; |
| int r; |
| int w; |
| } Q; |
| |
| typedef struct Cons { |
| QLock lock; |
| int ref; |
| int closed; |
| int fd; |
| int srvfd; |
| int ctlfd; |
| Q* iq; /* points to console.iq */ |
| Q* oq; /* points to console.oq */ |
| } Cons; |
| |
| char *currfsysname; |
| |
| static struct { |
| Q* iq; /* input */ |
| Q* oq; /* output */ |
| char l[Nl]; /* command line assembly */ |
| int nl; /* current line length */ |
| int nopens; |
| |
| char* prompt; |
| int np; |
| } console; |
| |
| static void |
| consClose(Cons* cons) |
| { |
| qlock(&cons->lock); |
| cons->closed = 1; |
| |
| cons->ref--; |
| if(cons->ref > 0){ |
| qlock(&cons->iq->lock); |
| rwakeup(&cons->iq->full); |
| qunlock(&cons->iq->lock); |
| qlock(&cons->oq->lock); |
| rwakeup(&cons->oq->empty); |
| qunlock(&cons->oq->lock); |
| qunlock(&cons->lock); |
| return; |
| } |
| |
| if(cons->ctlfd != -1){ |
| close(cons->ctlfd); |
| cons->srvfd = -1; |
| } |
| if(cons->srvfd != -1){ |
| close(cons->srvfd); |
| cons->srvfd = -1; |
| } |
| if(cons->fd != -1){ |
| close(cons->fd); |
| cons->fd = -1; |
| } |
| qunlock(&cons->lock); |
| vtfree(cons); |
| console.nopens--; |
| } |
| |
| static void |
| consIProc(void* v) |
| { |
| Q *q; |
| Cons *cons; |
| int n, w; |
| char buf[Nq/4]; |
| |
| threadsetname("consI"); |
| |
| cons = v; |
| q = cons->iq; |
| for(;;){ |
| /* |
| * Can't tell the difference between zero-length read |
| * and eof, so keep calling read until we get an error. |
| */ |
| if(cons->closed || (n = read(cons->fd, buf, Nq/4)) < 0) |
| break; |
| qlock(&q->lock); |
| while(Nq - q->n < n && !cons->closed) |
| rsleep(&q->full); |
| w = Nq - q->w; |
| if(w < n){ |
| memmove(&q->q[q->w], buf, w); |
| memmove(&q->q[0], buf + w, n - w); |
| } |
| else |
| memmove(&q->q[q->w], buf, n); |
| q->w = (q->w + n) % Nq; |
| q->n += n; |
| rwakeup(&q->empty); |
| qunlock(&q->lock); |
| } |
| consClose(cons); |
| } |
| |
| static void |
| consOProc(void* v) |
| { |
| Q *q; |
| Cons *cons; |
| char buf[Nq]; |
| int lastn, n, r; |
| |
| threadsetname("consO"); |
| |
| cons = v; |
| q = cons->oq; |
| qlock(&q->lock); |
| lastn = 0; |
| for(;;){ |
| while(lastn == q->n && !cons->closed) |
| rsleep(&q->empty); |
| if((n = q->n - lastn) > Nq) |
| n = Nq; |
| if(n > q->w){ |
| r = n - q->w; |
| memmove(buf, &q->q[Nq - r], r); |
| memmove(buf+r, &q->q[0], n - r); |
| } |
| else |
| memmove(buf, &q->q[q->w - n], n); |
| lastn = q->n; |
| qunlock(&q->lock); |
| if(cons->closed || write(cons->fd, buf, n) < 0) |
| break; |
| qlock(&q->lock); |
| rwakeup(&q->empty); |
| } |
| consClose(cons); |
| } |
| |
| int |
| consOpen(int fd, int srvfd, int ctlfd) |
| { |
| Cons *cons; |
| |
| cons = vtmallocz(sizeof(Cons)); |
| cons->fd = fd; |
| cons->srvfd = srvfd; |
| cons->ctlfd = ctlfd; |
| cons->iq = console.iq; |
| cons->oq = console.oq; |
| console.nopens++; |
| |
| qlock(&cons->lock); |
| cons->ref = 2; |
| cons->closed = 0; |
| if(proccreate(consOProc, cons, STACK) < 0){ |
| cons->ref--; |
| qunlock(&cons->lock); |
| consClose(cons); |
| return 0; |
| } |
| qunlock(&cons->lock); |
| |
| if(ctlfd >= 0) |
| consIProc(cons); |
| else if(proccreate(consIProc, cons, STACK) < 0){ |
| consClose(cons); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| qWrite(Q* q, char* p, int n) |
| { |
| int w; |
| |
| qlock(&q->lock); |
| if(n > Nq - q->w){ |
| w = Nq - q->w; |
| memmove(&q->q[q->w], p, w); |
| memmove(&q->q[0], p + w, n - w); |
| q->w = n - w; |
| } |
| else{ |
| memmove(&q->q[q->w], p, n); |
| q->w += n; |
| } |
| q->n += n; |
| rwakeup(&q->empty); |
| qunlock(&q->lock); |
| |
| return n; |
| } |
| |
| static Q* |
| qAlloc(void) |
| { |
| Q *q; |
| |
| q = vtmallocz(sizeof(Q)); |
| q->full.l = &q->lock; |
| q->empty.l = &q->lock; |
| q->n = q->r = q->w = 0; |
| |
| return q; |
| } |
| |
| static void |
| consProc(void* v) |
| { |
| USED(v); |
| Q *q; |
| int argc, i, n, r; |
| char *argv[20], buf[Nq], *lp, *wbuf; |
| char procname[64]; |
| |
| snprint(procname, sizeof procname, "cons %s", currfsysname); |
| threadsetname(procname); |
| |
| q = console.iq; |
| qWrite(console.oq, console.prompt, console.np); |
| qlock(&q->lock); |
| for(;;){ |
| while((n = q->n) == 0) |
| rsleep(&q->empty); |
| r = Nq - q->r; |
| if(r < n){ |
| memmove(buf, &q->q[q->r], r); |
| memmove(buf + r, &q->q[0], n - r); |
| } |
| else |
| memmove(buf, &q->q[q->r], n); |
| q->r = (q->r + n) % Nq; |
| q->n -= n; |
| rwakeup(&q->full); |
| qunlock(&q->lock); |
| |
| for(i = 0; i < n; i++){ |
| switch(buf[i]){ |
| case '\004': /* ^D */ |
| if(console.nl == 0){ |
| qWrite(console.oq, "\n", 1); |
| break; |
| } |
| /*FALLTHROUGH*/ |
| default: |
| if(console.nl < Nl-1){ |
| qWrite(console.oq, &buf[i], 1); |
| console.l[console.nl++] = buf[i]; |
| } |
| continue; |
| case '\b': |
| if(console.nl != 0){ |
| qWrite(console.oq, &buf[i], 1); |
| console.nl--; |
| } |
| continue; |
| case '\n': |
| qWrite(console.oq, &buf[i], 1); |
| break; |
| case '\025': /* ^U */ |
| qWrite(console.oq, "^U\n", 3); |
| console.nl = 0; |
| break; |
| case '\027': /* ^W */ |
| console.l[console.nl] = '\0'; |
| wbuf = vtmalloc(console.nl+1); |
| memmove(wbuf, console.l, console.nl+1); |
| argc = tokenize(wbuf, argv, nelem(argv)); |
| if(argc > 0) |
| argc--; |
| console.nl = 0; |
| lp = console.l; |
| for(i = 0; i < argc; i++) |
| lp += sprint(lp, "%q ", argv[i]); |
| console.nl = lp - console.l; |
| vtfree(wbuf); |
| qWrite(console.oq, "^W\n", 3); |
| if(console.nl == 0) |
| break; |
| qWrite(console.oq, console.l, console.nl); |
| continue; |
| case '\177': |
| qWrite(console.oq, "\n", 1); |
| console.nl = 0; |
| break; |
| } |
| |
| console.l[console.nl] = '\0'; |
| if(console.nl != 0) |
| cliExec(console.l); |
| |
| console.nl = 0; |
| qWrite(console.oq, console.prompt, console.np); |
| } |
| |
| qlock(&q->lock); |
| } |
| } |
| |
| int |
| consWrite(char* buf, int len) |
| { |
| if(console.oq == nil) |
| return write(2, buf, len); |
| if(console.nopens == 0) |
| write(2, buf, len); |
| return qWrite(console.oq, buf, len); |
| } |
| |
| int |
| consPrompt(char* prompt) |
| { |
| char buf[ERRMAX]; |
| |
| if(prompt == nil) |
| prompt = "prompt"; |
| |
| vtfree(console.prompt); |
| console.np = snprint(buf, sizeof(buf), "%s: ", prompt); |
| console.prompt = vtstrdup(buf); |
| |
| return console.np; |
| } |
| |
| int |
| consTTY(void) |
| { |
| int ctl, fd; |
| char *name, *p; |
| |
| name = "/dev/cons"; |
| if((fd = open(name, ORDWR)) < 0){ |
| #ifdef PLAN9PORT |
| name = "/dev/tty"; |
| #else |
| name = "#c/cons"; |
| #endif |
| if((fd = open(name, ORDWR)) < 0){ |
| werrstr("consTTY: open %s: %r", name); |
| return 0; |
| } |
| } |
| |
| #ifdef PLAN9PORT |
| USED(p); |
| ctl = 0; |
| #else |
| p = smprint("%sctl", name); |
| if((ctl = open(p, OWRITE)) < 0){ |
| close(fd); |
| werrstr("consTTY: open %s: %r", p); |
| free(p); |
| return 0; |
| } |
| if(write(ctl, "rawon", 5) < 0){ |
| close(ctl); |
| close(fd); |
| werrstr("consTTY: write %s: %r", p); |
| free(p); |
| return 0; |
| } |
| free(p); |
| #endif |
| |
| if(consOpen(fd, fd, ctl) == 0){ |
| close(ctl); |
| close(fd); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| int |
| consInit(void) |
| { |
| console.iq = qAlloc(); |
| console.oq = qAlloc(); |
| console.nl = 0; |
| |
| consPrompt(nil); |
| |
| if(proccreate(consProc, nil, STACK) < 0){ |
| sysfatal("can't start console proc"); |
| return 0; |
| } |
| |
| return 1; |
| } |