From e9a11deef01346dc75728f4debefcc694254e5f0 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Tue, 2 Mar 2021 20:40:10 -0500 Subject: command history in shell --- src/user/highway/main.c | 4 +- src/user/include/libterm/readline.h | 8 ++- src/user/libterm/readline.c | 122 +++++++++++++++++++++++++++++++++++- 3 files changed, 129 insertions(+), 5 deletions(-) diff --git a/src/user/highway/main.c b/src/user/highway/main.c index 47f3985..bcc3e01 100644 --- a/src/user/highway/main.c +++ b/src/user/highway/main.c @@ -14,9 +14,11 @@ void main(const char *arg) { term_add_sz("Portland Highway\nType \"help\" for help.\n"); term_paint(); + struct history *h = new_history(200); + char cmd_buf[128]; while (1) { - read_line(cmd_buf, 127, "> "); + read_line(cmd_buf, 127, "> ", h); run_line(cmd_buf); } } \ No newline at end of file diff --git a/src/user/include/libterm/readline.h b/src/user/include/libterm/readline.h index b1d5b0a..fdfb408 100644 --- a/src/user/include/libterm/readline.h +++ b/src/user/include/libterm/readline.h @@ -3,8 +3,14 @@ #include +struct history; + +//returns zero if memory allocation failed or if max_entries was zero or one +struct history *new_history(uint32_t max_entries); + //returns length of string without null terminator //max_length doesn't include null terminator -uint32_t read_line(char *sz, uint32_t max_length, const char *prompt); +//pass null pointer in hist for no history +uint32_t read_line(char *sz, uint32_t max_length, const char *prompt, struct history *hist); #endif \ No newline at end of file diff --git a/src/user/libterm/readline.c b/src/user/libterm/readline.c index fdb61a1..c59a60b 100644 --- a/src/user/libterm/readline.c +++ b/src/user/libterm/readline.c @@ -1,13 +1,82 @@ #include #include +#include #include #include -//returns length of string without null terminator -//max_length doesn't include null terminator -uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { +struct he { + struct he *prev; + struct he *next; + const char *data; +}; + +struct history { + uint32_t entries_left; + struct he *newest_entry; + struct he *oldest_entry; +}; + +static void move_entry_to_front(struct history *h, struct he *e) { + if (!e || !h || (e == h->newest_entry)) + return; + if (e->prev) + e->prev->next = e->next; + if (e->next) + e->next->prev = e->prev; + if (e == h->oldest_entry) + h->oldest_entry = e->next; + e->prev = h->newest_entry; + e->next = 0; + if (h->newest_entry) + h->newest_entry->next = e; + h->newest_entry = e; +} + +static void add_to_history(struct history *h, const char *from) { + const uint32_t from_size = strlen(from) + 1; + + char *const copy = get_block(from_size); + blockcpy(copy, from, from_size); + + struct he *entry; + + if (h->entries_left) { + --h->entries_left; + entry = get_block(sizeof(struct he)); + } + + else { + entry = h->oldest_entry; + h->oldest_entry = entry->next; + entry->next->prev = 0; + free_block(entry->data); + } + + entry->data = copy; + entry->prev = h->newest_entry; + entry->next = 0; + if (h->newest_entry) + h->newest_entry->next = entry; + else + h->oldest_entry = entry; + h->newest_entry = entry; +} + +struct history *new_history(uint32_t max_entries) { + if (max_entries < 2) + return 0; + struct history *h = get_block(sizeof(struct history)); + if (!h) + return 0; + h->newest_entry = 0; + h->oldest_entry = 0; + h->entries_left = max_entries; + return h; +} + +uint32_t read_line(char *sz, uint32_t max_length, const char *prompt, struct history *hist) { term_add_sz(prompt); term_add_char(' '); term_paint(); @@ -15,6 +84,9 @@ uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { uint32_t l = 0; uint32_t c = 0; + struct he *h_on = 0; + bool he_modified = false; + while (1) { struct key_packet kp = term_get_key_blocking(); switch (kp.key_id) { @@ -54,6 +126,7 @@ uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { case KEY_BSPACE: if (!c) continue; + he_modified = true; --c; --l; for (uint32_t i = c; i < l; ++i) @@ -71,16 +144,59 @@ uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { term_cursor_right(); } sz[l] = '\0'; + if (hist) { + if (he_modified) + add_to_history(hist, sz); + else if (h_on) + move_entry_to_front(hist, h_on); + } term_add_char('\n'); term_paint(); _yield_task(); return l; + case KEY_UP_ARROW: + if (!hist || !hist->newest_entry || (h_on == hist->oldest_entry)) + continue; + he_modified = false; + h_on = h_on ? h_on->prev : hist->newest_entry; + for (uint32_t i = 0; i < c; ++i) + term_cursor_left(); + for (uint32_t i = 0; i < l; ++i) + term_add_char(' '); + for (uint32_t i = 0; i < l; ++i) + term_cursor_left(); + l = strcpy(sz, h_on->data); + term_add_sn_no_ww(sz, l); + c = l; + term_paint(); + continue; + case KEY_DOWN_ARROW: + if (!h_on) + continue; + he_modified = false; + h_on = h_on->next; + for (uint32_t i = 0; i < c; ++i) + term_cursor_left(); + for (uint32_t i = 0; i < l; ++i) + term_add_char(' '); + for (uint32_t i = 0; i < l; ++i) + term_cursor_left(); + if (h_on) { + l = strcpy(sz, h_on->data); + term_add_sn_no_ww(sz, l); + } + else + l = 0; + c = l; + term_paint(); + continue; default: if (l == max_length) continue; char ch = key_to_char(kp); if (!ch) continue; + he_modified = true; if (c == l) { ++l; term_add_char(sz[c++] = ch); -- cgit v1.2.3