summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/user/highway/main.c4
-rw-r--r--src/user/include/libterm/readline.h8
-rw-r--r--src/user/libterm/readline.c122
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 <stdint.h>
+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 <libterm/terminal.h>
#include <knob/block.h>
+#include <knob/heap.h>
#include <knob/key.h>
#include <stdint.h>
-//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);