406 lines
6.6 KiB
C
406 lines
6.6 KiB
C
#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;
|
|
}
|