command history in shell
This commit is contained in:
parent
1d69a46f5d
commit
e9a11deef0
3 changed files with 129 additions and 5 deletions
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
Reference in a new issue