From 43688fe7190d0350349d47727c3663421d5618dc Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Mon, 8 Nov 2021 08:46:56 -0800 Subject: feat(rc): added back functionality of prompt, now unicode aware --- sys/cmd/rc/input.c | 155 +++++++++++++++++++++++++++++++++++++++++++++------- sys/cmd/rc/main.c | 1 + sys/cmd/rc/prompt.c | 7 ++- sys/cmd/rc/rc.h | 7 ++- 4 files changed, 148 insertions(+), 22 deletions(-) diff --git a/sys/cmd/rc/input.c b/sys/cmd/rc/input.c index 7ec8100..cc2383d 100644 --- a/sys/cmd/rc/input.c +++ b/sys/cmd/rc/input.c @@ -32,7 +32,8 @@ static int ascii[256] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -struct Mode { +struct Mode +{ ushort raw : 1; ushort multiline : 1; ushort mask : 1; @@ -43,9 +44,6 @@ struct Mode { } vi ; }; -static struct Mode mode; -static struct termios originalterm; - /* * the structure represents the state during line editing. * we pass this state to functions implementing specific editing functionalities @@ -82,8 +80,22 @@ struct TerminalState } yank; /* yank buffer */ intptr maxrows; /* maximum num of rows used so far (multiline mode) */ + intptr history; /* index of history we are currently editing */ }; +/* + * line history (circular buffer) + */ +struct History +{ + char **bot, **top, *entry[1024]; +}; + +/* globals */ +static struct Mode mode; +static struct History history; +static struct termios originalterm; + enum { KeyNil = 0, /* nil */ @@ -293,14 +305,120 @@ beep(void) fflush(stderr); } -/* =========================== Line editing ================================= */ +// ----------------------------------------------------------------------- +// command history + +void +inithistory(void) +{ + history.bot = history.top = history.entry; +} + +int +addhistory(char *line) +{ + char *copy; + + copy = strdup(line); + if(!copy) + return 0; + + *history.top++ = copy; + if(history.top == arrend(history.entry)) + history.top = history.entry; + + if(history.top == history.bot){ + efree(history.bot); + history.bot++; + } + + return 1; +} + +static +void +pophistory(void) +{ + if(--history.top < history.entry) + history.top = arrend(history.entry)-1; + efree(*history.top); +} + +static void refreshline(struct TerminalState *); -/* We define a very simple "append buffer" structure, that is an heap - * allocated string where we can append to. This is useful in order to +static +char ** +currenthistory(struct TerminalState *term, intptr *size) +{ + char **entry; + intptr len, head; + + if(history.top > history.bot){ + len = history.top - history.bot; + entry = history.top - term->history - 1; + }else if(history.top < history.bot){ + len = (arrend(history.entry) - history.bot) + (history.top - history.entry); + if((head=history.top - history.entry) < term->history) + entry = arrend(history.entry) - head; + else + entry = history.top - term->history - 1; + }else + return nil; + + *size = len; + return entry; +} + +static +void +usehistory(struct TerminalState *term, int d) +{ + rune r; + intptr w, len; + char *b, *e, **entry; + + if(!(entry = currenthistory(term, &len))) + return; + + efree(*entry); + *entry = strdup(term->edit.buf); + + term->history += d; + if(term->history < 0){ + term->history = 0; + return; + }else if(term->history >= len){ + term->history = len - 1; + return; + } + entry = currenthistory(term, &len); + + strncpy(term->edit.buf, *entry, term->edit.cap); + term->edit.buf[term->edit.cap-1] = 0; + + /* update cursor/buffer positions */ + term->edit.len = term->edit.pos = strlen(term->edit.buf); + for(w=0, b=term->edit.buf, e=term->edit.buf+term->edit.len; b < e; ){ + b += utf8·decode(b, &r); + w += utf8·runewidth(r); + } + term->cursor.len = term->cursor.pos = w; + + refreshline(term); +} + +// ----------------------------------------------------------------------- +// line editing + +/* + * we define a very simple "append buffer" structure, that is an heap + * allocated string where we can append to. this is useful in order to * write all the escape sequences in a buffer and flush them to the standard - * output in a single call, to avoid flickering effects. */ + * output in a single call, to avoid flickering effects. + */ -struct Buffer { +struct Buffer +{ int len; char *b; }; @@ -667,14 +785,8 @@ refresh: term->cursor.len -= diff; refreshline(term); } - /* movements */ -void -movehistory(struct TerminalState *term, int dir) -{ -} - #define CURRENT(term) (Position){ .buffer=(term)->edit.pos, .cursor=(term)->cursor.pos }; // move cursor to the left n boxes @@ -1223,6 +1335,7 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) term.cursor.cap = columns(ifd, ofd); term.maxrows = 0; + term.history = 0; term.yank.buf = nil; term.yank.cap = term.yank.len = 0; @@ -1231,6 +1344,9 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) term.edit.buf[0] = '\0'; term.edit.cap--; /* make sure there is always space for the nulterm */ + /* push current (empty) command onto history stack */ + addhistory(""); + if(write(term.ofd,prompt,term.prompt.len) == -1) return -1; @@ -1259,6 +1375,7 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) switch(r){ case KeyEnter: + pophistory(); if(mode.multiline) move(&term, END(term)); goto finish; @@ -1300,11 +1417,11 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) break; case KeyCtrlP: /* ctrl-p */ - /* TODO next history */ + usehistory(&term, +1); break; case KeyCtrlN: /* ctrl-n */ - /* TODO prev history */ + usehistory(&term, -1); break; case KeyEsc: /* escape sequence */ @@ -1358,10 +1475,10 @@ interact(int ifd, int ofd, char *buf, intptr len, char *prompt) }else{ switch(esc[1]) { case 'A': /* up */ - movehistory(&term, 1); + usehistory(&term, +1); break; case 'B': /* down */ - movehistory(&term, 0); + usehistory(&term, -1); break; case 'C': /* right */ move(&term, right(&term, 1)); diff --git a/sys/cmd/rc/main.c b/sys/cmd/rc/main.c index 865bcde..2c0aa42 100644 --- a/sys/cmd/rc/main.c +++ b/sys/cmd/rc/main.c @@ -58,6 +58,7 @@ main(int argc, char *argv[]) initpath(); initkeywords(); initshell(); + inithistory(); enablevi(); xboot(argc, argv); diff --git a/sys/cmd/rc/prompt.c b/sys/cmd/rc/prompt.c index ebda9fb..1122d54 100644 --- a/sys/cmd/rc/prompt.c +++ b/sys/cmd/rc/prompt.c @@ -19,8 +19,13 @@ prompt(ushort *flag) runner->flag.eof = 1; return 0; } - write(mapfd(0), "\n\r", 2); + if(runner->cmd.io->e[-1] == '\n'){ + runner->cmd.io->e[-1] = 0; + addhistory(runner->cmd.io->b); + runner->cmd.io->e[-1] = '\n'; + } + write(mapfd(0), "\n\r", 2); promptstr = " "; runner->line++; diff --git a/sys/cmd/rc/rc.h b/sys/cmd/rc/rc.h index 32fb639..83c39e9 100644 --- a/sys/cmd/rc/rc.h +++ b/sys/cmd/rc/rc.h @@ -172,10 +172,13 @@ void *emalloc(uintptr); void *erealloc(void*, uintptr); void efree(void*); -/* read.c */ +/* input.c */ int readline(char *); void enablevi(void); +void inithistory(void); +int addhistory(char *); + /* prompt.c */ void resetprompt(void); int prompt(ushort *); @@ -237,7 +240,7 @@ void background(Thread *, int); /* exec.c */ // XXX: odd place for this -int count(Word *); +int count(Word *); Word *makeword(char *str, Word *link); void freeword(Word *w); -- cgit v1.2.1