diff --git a/Makefile b/Makefile index a952067..4650c73 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,8 @@ sh_OBJS=$(O)/sh.o \ $(O)/parse.o \ $(O)/builtin.o \ $(O)/job.o \ - $(O)/env.o + $(O)/env.o \ + $(O)/history.o all: mkdirs $(O)/sh diff --git a/include/history.h b/include/history.h new file mode 100644 index 0000000..329e0e8 --- /dev/null +++ b/include/history.h @@ -0,0 +1,10 @@ +#pragma once +#define HISTORY_SIZE 128 + +struct history_entry { + char data[256]; + struct history_entry *prev, *next; +}; + +void history_insert(const char *cmd); +struct history_entry *history_head(void); diff --git a/src/history.c b/src/history.c new file mode 100644 index 0000000..484ee2b --- /dev/null +++ b/src/history.c @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "history.h" + +static struct history_entry *g_history_head, *g_history_tail; +size_t history_size = 0; + +struct history_entry *history_head(void) { + return g_history_head; +} + +void history_insert(const char *command) { + struct history_entry *ent; + if (history_size == HISTORY_SIZE) { + // Just resuse the last entry + assert(g_history_tail && g_history_tail->prev); + ent = g_history_tail; + g_history_tail = g_history_tail->prev; + g_history_tail->next = NULL; + } else { + ent = malloc(sizeof(struct history_entry)); + assert(ent); + } + + strcpy(ent->data, command); + ent->prev = NULL; + ent->next = g_history_head; + if (!g_history_tail) { + assert(!g_history_head); + g_history_tail = ent; + } else { + g_history_head->prev = ent; + } + g_history_head = ent; + ++history_size; +} diff --git a/src/readline.c b/src/readline.c index bb97511..9c3d945 100644 --- a/src/readline.c +++ b/src/readline.c @@ -2,7 +2,9 @@ #include #include #include +#include #include +#include "history.h" #define KEY_UP (256) #define KEY_DOWN (257) @@ -86,6 +88,7 @@ int readline(char *buf, size_t lim) { int cur = 0; int chr; + struct history_entry *ent = NULL; struct termios t0, t1; tcgetattr(STDIN_FILENO, &t0); @@ -118,8 +121,65 @@ int readline(char *buf, size_t lim) { ++cur; } break; + case KEY_UP: + if (!ent) { + ent = history_head(); + } else { + ent = ent->next; + } + + if (ent) { + if (cur) { + fprintf(stdout, "\033[%dD", cur); + } + fputs("\033[K", stdout); + fputs(ent->data, stdout); + fflush(stdout); + len = strlen(ent->data); + cur = len; + } else { + if (cur) { + fprintf(stdout, "\033[%dD", cur); + } + fputs("\033[K", stdout); + fflush(stdout); + cur = 0; + len = 0; + } + break; + case KEY_DOWN: + if (ent) { + ent = ent->prev; + + if (ent) { + if (cur) { + fprintf(stdout, "\033[%dD", cur); + } + fputs("\033[K", stdout); + fputs(ent->data, stdout); + fflush(stdout); + len = strlen(ent->data); + cur = len; + } else { + if (cur) { + fprintf(stdout, "\033[%dD", cur); + } + fputs("\033[K", stdout); + fflush(stdout); + cur = 0; + len = 0; + } + } + break; } + if (chr == KEY_UP || chr == KEY_DOWN) { + continue; + } + + // Any other move resets history position + ent = NULL; + if (chr == 4) { if (len == 0) { fputs("exit\n", stderr); @@ -162,6 +222,13 @@ int readline(char *buf, size_t lim) { } } + const char *e = buf; + while (isspace(*e)) { + ++e; + } + if (*e) { + history_insert(buf); + } tcsetattr(STDIN_FILENO, TCSANOW, &t0); return len;