blob: fed24d5581260b0feb8598458c2acdf8df0f59dc [file] [log] [blame]
#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;
}