9os

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 06abcce2337cc853a09635fa70ff3583b5dc80ef
parent 71c38bc3d65907d2997c23b62fdd13f193b62cee
Author: Ambroise Vincent <ambroise.vincent@arm.com>
Date:   Wed,  3 Apr 2019 15:05:16 +0100

[dev] Add line editing to console driver

The functionalities are:

 - Echo
 - Convert NL to CR NL
 - Backspace
 - ^W erase last word
 - ^U erase full line
 - ^L repaint line

Change-Id: I18d23a44ec7ad1e45f5f2b1e2293c6e9e7f1a8de
Signed-off-by: Ambroise Vincent <ambroise.vincent@arm.com>

Diffstat:
Mdrivers/devcons.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Minclude/rcode/rcode.h | 1+
Msrc/romfw/dlang.c | 1-
Mtarget/native/rom.c | 11-----------
4 files changed, 211 insertions(+), 33 deletions(-)

diff --git a/drivers/devcons.c b/drivers/devcons.c @@ -4,15 +4,18 @@ #include <ctype.h> #include <errno.h> #include <libk.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <rcode/dev.h> +#include "dev.h" #define CONSOUT 2 #define NAMESIZE 15 #define CONSSTATUS 128 +#define CTRL(x) ((x) & 0x1f) + enum Orootqid { Qconsfs, Qraw, @@ -33,6 +36,10 @@ struct cons { static struct cons *cons; +static char buffer[LINELEN]; +static int head; +static int tail; + static int conswalk(Chan *c, const char *name) { @@ -67,8 +74,15 @@ consstatus(void *buf, Chan *c, int n) static int consaddin(void *buf, int n) { - if (cons->in || n >= NAMESIZE) + if (n >= NAMESIZE) { + errno = EINVAL; + return -1; + } + + if (cons->in) { + errno = ENOMEM; return -1; + } cons->in = namec(buf, O_READ); memcpy(cons->inname, buf, n); @@ -80,8 +94,10 @@ consaddout(void *buf, int n) { int i; - if (n >= NAMESIZE) + if (n >= NAMESIZE) { + errno = ENAMETOOLONG; return -1; + } for (i = 0; i < CONSOUT; i++) { if (!cons->out[i]) { @@ -90,14 +106,22 @@ consaddout(void *buf, int n) return 0; } } + errno = ENOMEM; return -1; } static int consdelin(void *buf, int n) { - if (!cons->in || memcmp(buf, cons->inname, n)) + if (n >= NAMESIZE) { + errno = EINVAL; return -1; + } + + if (!cons->in || memcmp(buf, cons->inname, n)) { + errno = ENOENT; + return -1; + } chanclose(cons->in); cons->in = NULL; @@ -110,6 +134,11 @@ consdelout(void *buf, int n) { int i; + if (n >= NAMESIZE) { + errno = EINVAL; + return -1; + } + for (i = 0; i < CONSOUT; i++) { if (cons->out[i] && !memcmp(buf, cons->outname[i], n)) { chanclose(cons->out[i]); @@ -118,16 +147,49 @@ consdelout(void *buf, int n) return 0; } } + errno = ENOENT; return -1; } static int +conswriteraw(char *buf, int n) +{ + int i, idx, r; + char wrtbuf[LINELEN]; + Chan **pp; + + idx = 0; + for (i = 0; i < n; i++) { + if (idx + 2 > sizeof(wrtbuf)) + break; + if (buf[i] == '\n') + wrtbuf[idx++] = '\r'; + wrtbuf[idx++] = buf[i]; + } + + r = 0; + for (pp = cons->out; pp < &cons->out[CONSOUT]; pp++) { + int w; + Chan *p; + + if ((p = *pp) == NULL) + continue; + w = devtab[p->type]->write(p, wrtbuf, idx); + if (w < 0) { + r = -1; + } else if (w != idx) { + errno = EIO; + r = -1; + } + } + return r < 0 ? r : idx; +} + +static int conswrite(Chan *c, void *buf, int n) { char *tokens[2]; - int written, i; - Chan *p; - Chan **pp; + int i; int (*func)(void *, int); struct conscmds { @@ -144,42 +206,169 @@ conswrite(Chan *c, void *buf, int n) switch (c->qid & ~CHDIR) { case Qconsfs: + errno = EISDIR; return -1; case Qraw: - written = -1; - for (pp = cons->out; pp < &cons->out[CONSOUT]; pp++) { - if ((p = *pp) == NULL) - continue; - written = devtab[p->type]->write(p, buf, n); - } - return written; + return conswriteraw(buf, n); case Qctl: - if (tokenize(buf, n, tokens, 2) != 2) + if (tokenize(buf, n, tokens, 2) != 2) { + errno = EINVAL; return -1; + } for (i = 0; i < NELEM(conscmds); i++) { if (!strcmp(tokens[0], conscmds[i].name)) { func = conscmds[i].func; return (*func)(tokens[1], strlen(tokens[1])); } } + errno = EINVAL; return -1; default: panic("conswrite"); } } +static void +conserase(Chan *c, int n) +{ + int i; + char editingchar; + + if (head + 1 < n) + n = head + 1; + + editingchar = '\b'; + for (i = 0; i < n; i++) + conswrite(c, &editingchar, 1); + editingchar = ' '; + for (i = 0; i < n; i++) + conswrite(c, &editingchar, 1); + editingchar = '\b'; + for (i = 0; i < n; i++) + conswrite(c, &editingchar, 1); +} + +static int +consreadediting(Chan *c) +{ + int i; + char editingchar; + + /* + * XXX: it relies on the fact that head is incremented in the + * calling function after exiting this one. For example, head is + * briefly equal to -1 when backspace is hit on an empty line. + */ + switch (buffer[head]) { + case '\0': + return -1; + case '\b': + case 127: /* DEL */ + head--; + conserase(c, 1); + head -= head >= 0; + return 0; + case CTRL('L'): + editingchar = '\r'; + conswrite(c, &editingchar, 1); + conswrite(c, buffer, head); + head--; + return 0; + case CTRL('U'): + editingchar = '\r'; + conswrite(c, &editingchar, 1); + editingchar = ' '; + for (i = 0; i < head; i++) + conswrite(c, &editingchar, 1); + editingchar = '\r'; + conswrite(c, &editingchar, 1); + head = -1; + return 0; + case '\r': + buffer[head] = '\n'; + break; + case CTRL('W'): + i = 0; + head--; + while (head - i >= 0 && buffer[head - i] == ' ') + i++; + while (head - i >= 0 && buffer[head - i] != ' ') + i++; + conserase(c, i); + head -= i; + return 0; + default: + break; + } + + conswrite(c, &buffer[head], 1); + + return 0; +} + +static char +consreadone(void) +{ + char ch; + Chan *in = cons->in; + + return devtab[in->type]->read(in, &ch, 1) < 0 ? EOF : ch; +} + +static int +fillbuffer(Chan *c) +{ + char ch; + + while (head + 1 < sizeof(buffer) && (ch = consreadone()) != EOF) { + buffer[head] = ch; + consreadediting(c); + if (buffer[head++] == '\n') + break; + } + if (ch == EOF && head == 0) { + errno = EIO; + return -1; + } + buffer[head] = '\0'; + + return 0; +} + +static int +readqueue(void *buf, int n) +{ + int size = head - tail; + + if (n < size) { + memcpy(buf, &buffer[tail], n); + tail += n; + return n; + } else { + memcpy(buf, &buffer[tail], size); + head = 0; + tail = 0; + return size; + } +} + static int consread(Chan *c, void *buf, int n) { switch (c->qid & ~CHDIR) { case Qconsfs: return dirread(c, buf, n, dirtab, NELEM(dirtab), devgen); - case Qraw: { - Chan *in = cons->in; - if (cons->in) - return devtab[in->type]->read(in, buf, n); - return -1; - } + case Qraw: + if (!cons->in) { + errno = ENOENT; + return -1; + } + if (head == 0) { + if (fillbuffer(c) < 0) + return -1; + } + + return readqueue(buf, n); case Qctl: return consstatus(buf, c, n); default: diff --git a/include/rcode/rcode.h b/include/rcode/rcode.h @@ -14,6 +14,7 @@ #define NELEM(tab) (sizeof(tab) / sizeof((tab)[0])) +#define LINELEN 80 #define PAGESIZE 4096 #define IENABLE 1 #define IDISABLE 0 diff --git a/src/romfw/dlang.c b/src/romfw/dlang.c @@ -15,7 +15,6 @@ #define PREFIX "> " #define NR_ARGC_MAX 5 -#define LINELEN 80 unsigned in_debug; jmp_buf dbgrecover; diff --git a/target/native/rom.c b/target/native/rom.c @@ -73,16 +73,6 @@ imach(Mach *mp, void *stackp) } static void -itty(void) -{ - static char lnm[] = "\x1b[20h"; - static char srm[] = "\x1b[12l"; - - write(1, lnm, sizeof(lnm)); - write(1, srm, sizeof(srm)); -} - -static void info(Mach *mp) { dbg("romfw: version %s\n" @@ -148,7 +138,6 @@ main(void *stackp) intr(IENABLE); barrier(ISB); namespace(); - itty(); info(&mach); debug(); swtch(framep);