Move history into rline and out of shell
This commit is contained in:
parent
79a983c01e
commit
8f5a40cc20
@ -48,64 +48,10 @@ shell_command_t shell_pointers[SHELL_COMMANDS]; /* Command functions */
|
||||
/* This is the number of actual commands installed */
|
||||
uint32_t shell_commands_len = 0;
|
||||
|
||||
/* We also support history through a circular buffer. */
|
||||
#define SHELL_HISTORY_ENTRIES 128
|
||||
char * shell_history[SHELL_HISTORY_ENTRIES];
|
||||
int shell_history_count = 0;
|
||||
int shell_history_offset = 0;
|
||||
int shell_scroll = 0;
|
||||
char shell_temp[1024];
|
||||
|
||||
int shell_interactive = 1;
|
||||
int shell_force_raw = 0;
|
||||
|
||||
int pid; /* Process ID of the shell */
|
||||
|
||||
char * shell_history_prev(int item);
|
||||
|
||||
void shell_history_insert(char * str) {
|
||||
if (str[strlen(str)-1] == '\n') {
|
||||
str[strlen(str)-1] = '\0';
|
||||
}
|
||||
if (shell_history_count) {
|
||||
if (!strcmp(str, shell_history_prev(1))) {
|
||||
free(str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (shell_history_count == SHELL_HISTORY_ENTRIES) {
|
||||
free(shell_history[shell_history_offset]);
|
||||
shell_history[shell_history_offset] = str;
|
||||
shell_history_offset = (shell_history_offset + 1) % SHELL_HISTORY_ENTRIES;
|
||||
} else {
|
||||
shell_history[shell_history_count] = str;
|
||||
shell_history_count++;
|
||||
}
|
||||
}
|
||||
|
||||
void shell_history_append_line(char * str) {
|
||||
if (shell_history_count) {
|
||||
char ** s = &shell_history[(shell_history_count - 1 + shell_history_offset) % SHELL_HISTORY_ENTRIES];
|
||||
char * c = malloc(strlen(*s) + strlen(str) + 2);
|
||||
sprintf(c, "%s\n%s", *s, str);
|
||||
if (c[strlen(c)-1] == '\n') {
|
||||
c[strlen(c)-1] = '\0';
|
||||
}
|
||||
free(*s);
|
||||
*s = c;
|
||||
} else {
|
||||
/* wat */
|
||||
}
|
||||
}
|
||||
|
||||
char * shell_history_get(int item) {
|
||||
return shell_history[(item + shell_history_offset) % SHELL_HISTORY_ENTRIES];
|
||||
}
|
||||
|
||||
char * shell_history_prev(int item) {
|
||||
return shell_history_get(shell_history_count - item);
|
||||
}
|
||||
|
||||
void shell_install_command(char * name, shell_command_t func) {
|
||||
if (shell_commands_len == SHELL_COMMANDS) {
|
||||
fprintf(stderr, "Ran out of space for static shell commands. The maximum number of commands is %d\n", SHELL_COMMANDS);
|
||||
@ -125,19 +71,6 @@ shell_command_t shell_find(char * str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct termios old;
|
||||
|
||||
void set_unbuffered() {
|
||||
tcgetattr(fileno(stdin), &old);
|
||||
struct termios new = old;
|
||||
new.c_lflag &= (~ICANON & ~ECHO);
|
||||
tcsetattr(fileno(stdin), TCSAFLUSH, &new);
|
||||
}
|
||||
|
||||
void set_buffered() {
|
||||
tcsetattr(fileno(stdin), TCSAFLUSH, &old);
|
||||
}
|
||||
|
||||
void install_commands();
|
||||
|
||||
/* Maximum command length */
|
||||
@ -395,122 +328,6 @@ finish_tab:
|
||||
|
||||
}
|
||||
|
||||
void reverse_search(rline_context_t * context) {
|
||||
char input[512] = {0};
|
||||
int collected = 0;
|
||||
int start_at = 0;
|
||||
fprintf(stderr, "\033[G\033[s");
|
||||
fflush(stderr);
|
||||
key_event_state_t kbd_state = {0};
|
||||
while (1) {
|
||||
/* Find matches */
|
||||
char * match = "";
|
||||
int match_index = 0;
|
||||
try_rev_search_again:
|
||||
if (collected) {
|
||||
for (int i = start_at; i < shell_history_count; i++) {
|
||||
char * c = shell_history_prev(i+1);
|
||||
if (strstr(c, input)) {
|
||||
match = c;
|
||||
match_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!strcmp(match,"")) {
|
||||
if (start_at) {
|
||||
start_at = 0;
|
||||
goto try_rev_search_again;
|
||||
}
|
||||
collected--;
|
||||
input[collected] = '\0';
|
||||
if (collected) {
|
||||
goto try_rev_search_again;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\033[u(reverse-i-search)`%s': %s\033[K", input, match);
|
||||
fflush(stderr);
|
||||
|
||||
uint32_t key_sym = kbd_key(&kbd_state, fgetc(stdin));
|
||||
switch (key_sym) {
|
||||
case KEY_BACKSPACE:
|
||||
if (collected > 0) {
|
||||
collected--;
|
||||
input[collected] = '\0';
|
||||
start_at = 0;
|
||||
}
|
||||
break;
|
||||
case KEY_CTRL_C:
|
||||
printf("^C\n");
|
||||
return;
|
||||
case KEY_CTRL_R:
|
||||
start_at = match_index + 1;
|
||||
break;
|
||||
case '\n':
|
||||
memcpy(context->buffer, match, strlen(match) + 1);
|
||||
context->collected = strlen(match);
|
||||
context->offset = context->collected;
|
||||
if (context->callbacks->redraw_prompt) {
|
||||
fprintf(stderr, "\033[G\033[K");
|
||||
context->callbacks->redraw_prompt(context);
|
||||
}
|
||||
fprintf(stderr, "\033[s");
|
||||
rline_redraw_clean(context);
|
||||
fprintf(stderr, "\n");
|
||||
return;
|
||||
default:
|
||||
if (key_sym < KEY_NORMAL_MAX) {
|
||||
input[collected] = (char)key_sym;
|
||||
collected++;
|
||||
input[collected] = '\0';
|
||||
start_at = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void history_previous(rline_context_t * context) {
|
||||
if (shell_scroll == 0) {
|
||||
memcpy(shell_temp, context->buffer, strlen(context->buffer) + 1);
|
||||
}
|
||||
if (shell_scroll < shell_history_count) {
|
||||
shell_scroll++;
|
||||
for (int i = 0; i < strlen(context->buffer); ++i) {
|
||||
printf("\010 \010");
|
||||
}
|
||||
char * h = shell_history_prev(shell_scroll);
|
||||
memcpy(context->buffer, h, strlen(h) + 1);
|
||||
printf("\033[u%s\033[K", h);
|
||||
fflush(stdout);
|
||||
}
|
||||
context->collected = strlen(context->buffer);
|
||||
context->offset = context->collected;
|
||||
}
|
||||
|
||||
void history_next(rline_context_t * context) {
|
||||
if (shell_scroll > 1) {
|
||||
shell_scroll--;
|
||||
for (int i = 0; i < strlen(context->buffer); ++i) {
|
||||
printf("\010 \010");
|
||||
}
|
||||
char * h = shell_history_prev(shell_scroll);
|
||||
memcpy(context->buffer, h, strlen(h) + 1);
|
||||
printf("%s", h);
|
||||
fflush(stdout);
|
||||
} else if (shell_scroll == 1) {
|
||||
for (int i = 0; i < strlen(context->buffer); ++i) {
|
||||
printf("\010 \010");
|
||||
}
|
||||
shell_scroll = 0;
|
||||
memcpy(context->buffer, shell_temp, strlen(shell_temp) + 1);
|
||||
printf("\033[u%s\033[K", context->buffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
context->collected = strlen(context->buffer);
|
||||
context->offset = context->collected;
|
||||
}
|
||||
|
||||
void add_argument(list_t * argv, char * buf) {
|
||||
char * c = malloc(strlen(buf) + 1);
|
||||
memcpy(c, buf, strlen(buf) + 1);
|
||||
@ -521,24 +338,18 @@ void add_argument(list_t * argv, char * buf) {
|
||||
int read_entry(char * buffer) {
|
||||
rline_callbacks_t callbacks = {
|
||||
tab_complete_func, redraw_prompt_func, NULL,
|
||||
history_previous, history_next,
|
||||
NULL, NULL, reverse_search
|
||||
NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
set_unbuffered();
|
||||
int buffer_size = rline((char *)buffer, LINE_LEN, &callbacks);
|
||||
set_buffered();
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
int read_entry_continued(char * buffer) {
|
||||
rline_callbacks_t callbacks = {
|
||||
tab_complete_func, redraw_prompt_func_c, NULL,
|
||||
history_previous, history_next,
|
||||
NULL, NULL, reverse_search
|
||||
NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
set_unbuffered();
|
||||
int buffer_size = rline((char *)buffer, LINE_LEN, &callbacks);
|
||||
set_buffered();
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
@ -572,9 +383,9 @@ int shell_exec(char * buffer, int buffer_size) {
|
||||
|
||||
/* Read previous history entries */
|
||||
if (buffer[0] == '!') {
|
||||
uint32_t x = atoi((char *)((uintptr_t)buffer + 1));
|
||||
if (x <= shell_history_count) {
|
||||
buffer = shell_history_get(x - 1);
|
||||
int x = atoi((char *)((uintptr_t)buffer + 1));
|
||||
if (x > 0 && x <= rline_history_count) {
|
||||
buffer = rline_history_get(x - 1);
|
||||
buffer_size = strlen(buffer);
|
||||
} else {
|
||||
fprintf(stderr, "esh: !%d: event not found\n", x);
|
||||
@ -586,7 +397,7 @@ int shell_exec(char * buffer, int buffer_size) {
|
||||
memcpy(history, buffer, strlen(buffer) + 1);
|
||||
|
||||
if (buffer[0] != ' ' && buffer[0] != '\n') {
|
||||
shell_history_insert(history);
|
||||
rline_history_insert(history);
|
||||
} else {
|
||||
free(history);
|
||||
}
|
||||
@ -723,7 +534,7 @@ _done:
|
||||
if (shell_interactive) {
|
||||
draw_prompt_c();
|
||||
buffer_size = read_entry_continued(buffer);
|
||||
shell_history_append_line(buffer);
|
||||
rline_history_append_line(buffer);
|
||||
continue;
|
||||
} else {
|
||||
fprintf(stderr, "Syntax error: Unterminated quoted string.\n");
|
||||
@ -777,8 +588,6 @@ _done:
|
||||
argv[tokenid-1] = NULL;
|
||||
}
|
||||
|
||||
if (shell_force_raw) set_unbuffered();
|
||||
|
||||
if (cmdi > 0) {
|
||||
int last_output[2];
|
||||
pipe(last_output);
|
||||
@ -942,7 +751,7 @@ int main(int argc, char ** argv) {
|
||||
|
||||
buffer_size = read_entry(buffer);
|
||||
last_ret = shell_exec(buffer, buffer_size);
|
||||
shell_scroll = 0;
|
||||
rline_scroll = 0;
|
||||
|
||||
}
|
||||
|
||||
@ -983,8 +792,8 @@ cd_error:
|
||||
* history
|
||||
*/
|
||||
uint32_t shell_cmd_history(int argc, char * argv[]) {
|
||||
for (int i = 0; i < shell_history_count; ++i) {
|
||||
printf("%d\t%s\n", i + 1, shell_history_get(i));
|
||||
for (int i = 0; i < rline_history_count; ++i) {
|
||||
printf("%d\t%s\n", i + 1, rline_history_get(i));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1053,12 +862,6 @@ uint32_t shell_cmd_set(int argc, char * argv[]) {
|
||||
printf("\033[3000;%s;%sz", argv[2], argv[3]);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
} else if (!strcmp(argv[1], "force-raw")) {
|
||||
shell_force_raw = 1;
|
||||
return 0;
|
||||
} else if (!strcmp(argv[1], "no-force-raw")) {
|
||||
shell_force_raw = 0;
|
||||
return 0;
|
||||
} else if (!strcmp(argv[1], "--help")) {
|
||||
fprintf(stderr, "Available arguments:\n"
|
||||
" alpha - alpha transparency enabled / disabled\n"
|
||||
|
@ -5,11 +5,28 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include "lib/kbd.h"
|
||||
|
||||
#include "rline.h"
|
||||
|
||||
|
||||
static struct termios old;
|
||||
|
||||
static void set_unbuffered() {
|
||||
tcgetattr(fileno(stdin), &old);
|
||||
struct termios new = old;
|
||||
new.c_lflag &= (~ICANON & ~ECHO);
|
||||
tcsetattr(fileno(stdin), TCSAFLUSH, &new);
|
||||
}
|
||||
|
||||
static void set_buffered() {
|
||||
tcsetattr(fileno(stdin), TCSAFLUSH, &old);
|
||||
}
|
||||
|
||||
|
||||
void rline_redraw(rline_context_t * context) {
|
||||
printf("\033[u%s\033[K", context->buffer);
|
||||
for (int i = context->offset; i < context->collected; ++i) {
|
||||
@ -26,6 +43,174 @@ void rline_redraw_clean(rline_context_t * context) {
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
char * rline_history[RLINE_HISTORY_ENTRIES];
|
||||
int rline_history_count = 0;
|
||||
int rline_history_offset = 0;
|
||||
int rline_scroll = 0;
|
||||
char * rline_exit_string = "exit\n";
|
||||
|
||||
static char rline_temp[1024];
|
||||
|
||||
void rline_history_insert(char * str) {
|
||||
if (str[strlen(str)-1] == '\n') {
|
||||
str[strlen(str)-1] = '\0';
|
||||
}
|
||||
if (rline_history_count) {
|
||||
if (!strcmp(str, rline_history_prev(1))) {
|
||||
free(str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (rline_history_count == RLINE_HISTORY_ENTRIES) {
|
||||
free(rline_history[rline_history_offset]);
|
||||
rline_history[rline_history_offset] = str;
|
||||
rline_history_offset = (rline_history_offset + 1) % RLINE_HISTORY_ENTRIES;
|
||||
} else {
|
||||
rline_history[rline_history_count] = str;
|
||||
rline_history_count++;
|
||||
}
|
||||
}
|
||||
|
||||
void rline_history_append_line(char * str) {
|
||||
if (rline_history_count) {
|
||||
char ** s = &rline_history[(rline_history_count - 1 + rline_history_offset) % RLINE_HISTORY_ENTRIES];
|
||||
char * c = malloc(strlen(*s) + strlen(str) + 2);
|
||||
sprintf(c, "%s\n%s", *s, str);
|
||||
if (c[strlen(c)-1] == '\n') {
|
||||
c[strlen(c)-1] = '\0';
|
||||
}
|
||||
free(*s);
|
||||
*s = c;
|
||||
} else {
|
||||
/* wat */
|
||||
}
|
||||
}
|
||||
|
||||
char * rline_history_get(int item) {
|
||||
return rline_history[(item + rline_history_offset) % RLINE_HISTORY_ENTRIES];
|
||||
}
|
||||
|
||||
char * rline_history_prev(int item) {
|
||||
return rline_history_get(rline_history_count - item);
|
||||
}
|
||||
|
||||
static void rline_reverse_search(rline_context_t * context) {
|
||||
char input[512] = {0};
|
||||
int collected = 0;
|
||||
int start_at = 0;
|
||||
fprintf(stderr, "\033[G\033[s");
|
||||
fflush(stderr);
|
||||
key_event_state_t kbd_state = {0};
|
||||
while (1) {
|
||||
/* Find matches */
|
||||
char * match = "";
|
||||
int match_index = 0;
|
||||
try_rev_search_again:
|
||||
if (collected) {
|
||||
for (int i = start_at; i < rline_history_count; i++) {
|
||||
char * c = rline_history_prev(i+1);
|
||||
if (strstr(c, input)) {
|
||||
match = c;
|
||||
match_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!strcmp(match,"")) {
|
||||
if (start_at) {
|
||||
start_at = 0;
|
||||
goto try_rev_search_again;
|
||||
}
|
||||
collected--;
|
||||
input[collected] = '\0';
|
||||
if (collected) {
|
||||
goto try_rev_search_again;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\033[u(reverse-i-search)`%s': %s\033[K", input, match);
|
||||
fflush(stderr);
|
||||
|
||||
uint32_t key_sym = kbd_key(&kbd_state, fgetc(stdin));
|
||||
switch (key_sym) {
|
||||
case KEY_BACKSPACE:
|
||||
if (collected > 0) {
|
||||
collected--;
|
||||
input[collected] = '\0';
|
||||
start_at = 0;
|
||||
}
|
||||
break;
|
||||
case KEY_CTRL_C:
|
||||
printf("^C\n");
|
||||
return;
|
||||
case KEY_CTRL_R:
|
||||
start_at = match_index + 1;
|
||||
break;
|
||||
case '\n':
|
||||
memcpy(context->buffer, match, strlen(match) + 1);
|
||||
context->collected = strlen(match);
|
||||
context->offset = context->collected;
|
||||
if (context->callbacks->redraw_prompt) {
|
||||
fprintf(stderr, "\033[G\033[K");
|
||||
context->callbacks->redraw_prompt(context);
|
||||
}
|
||||
fprintf(stderr, "\033[s");
|
||||
rline_redraw_clean(context);
|
||||
fprintf(stderr, "\n");
|
||||
return;
|
||||
default:
|
||||
if (key_sym < KEY_NORMAL_MAX) {
|
||||
input[collected] = (char)key_sym;
|
||||
collected++;
|
||||
input[collected] = '\0';
|
||||
start_at = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void history_previous(rline_context_t * context) {
|
||||
if (rline_scroll == 0) {
|
||||
memcpy(rline_temp, context->buffer, strlen(context->buffer) + 1);
|
||||
}
|
||||
if (rline_scroll < rline_history_count) {
|
||||
rline_scroll++;
|
||||
for (int i = 0; i < strlen(context->buffer); ++i) {
|
||||
printf("\010 \010");
|
||||
}
|
||||
char * h = rline_history_prev(rline_scroll);
|
||||
memcpy(context->buffer, h, strlen(h) + 1);
|
||||
printf("\033[u%s\033[K", h);
|
||||
fflush(stdout);
|
||||
}
|
||||
context->collected = strlen(context->buffer);
|
||||
context->offset = context->collected;
|
||||
}
|
||||
|
||||
static void history_next(rline_context_t * context) {
|
||||
if (rline_scroll > 1) {
|
||||
rline_scroll--;
|
||||
for (int i = 0; i < strlen(context->buffer); ++i) {
|
||||
printf("\010 \010");
|
||||
}
|
||||
char * h = rline_history_prev(rline_scroll);
|
||||
memcpy(context->buffer, h, strlen(h) + 1);
|
||||
printf("%s", h);
|
||||
fflush(stdout);
|
||||
} else if (rline_scroll == 1) {
|
||||
for (int i = 0; i < strlen(context->buffer); ++i) {
|
||||
printf("\010 \010");
|
||||
}
|
||||
rline_scroll = 0;
|
||||
memcpy(context->buffer, rline_temp, strlen(rline_temp) + 1);
|
||||
printf("\033[u%s\033[K", context->buffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
context->collected = strlen(context->buffer);
|
||||
context->offset = context->collected;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert characters at the current cursor offset.
|
||||
*/
|
||||
@ -56,6 +241,8 @@ int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks) {
|
||||
0,
|
||||
};
|
||||
|
||||
set_unbuffered();
|
||||
|
||||
printf("\033[s");
|
||||
fflush(stdout);
|
||||
|
||||
@ -70,23 +257,30 @@ int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks) {
|
||||
case KEY_CTRL_C:
|
||||
printf("^C\n");
|
||||
context.buffer[0] = '\0';
|
||||
set_buffered();
|
||||
return 0;
|
||||
case KEY_CTRL_R:
|
||||
if (callbacks->rev_search) {
|
||||
callbacks->rev_search(&context);
|
||||
return context.collected;
|
||||
} else {
|
||||
rline_reverse_search(&context);
|
||||
}
|
||||
continue;
|
||||
set_buffered();
|
||||
return context.collected;
|
||||
case KEY_ARROW_UP:
|
||||
case KEY_CTRL_P:
|
||||
if (callbacks->key_up) {
|
||||
callbacks->key_up(&context);
|
||||
} else {
|
||||
history_previous(&context);
|
||||
}
|
||||
continue;
|
||||
case KEY_ARROW_DOWN:
|
||||
case KEY_CTRL_N:
|
||||
if (callbacks->key_down) {
|
||||
callbacks->key_down(&context);
|
||||
} else {
|
||||
history_next(&context);
|
||||
}
|
||||
continue;
|
||||
case KEY_CTRL_ARROW_RIGHT:
|
||||
@ -156,8 +350,9 @@ int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks) {
|
||||
continue;
|
||||
case KEY_CTRL_D:
|
||||
if (context.collected == 0) {
|
||||
printf("exit\n");
|
||||
printf(rline_exit_string);
|
||||
sprintf(context.buffer, "exit\n");
|
||||
set_buffered();
|
||||
return strlen(context.buffer);
|
||||
}
|
||||
/* Intentional fallthrough */
|
||||
@ -293,5 +488,6 @@ int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks) {
|
||||
|
||||
/* Cap that with a null */
|
||||
context.buffer[context.collected] = '\0';
|
||||
set_buffered();
|
||||
return context.collected;
|
||||
}
|
||||
|
@ -32,5 +32,17 @@ void rline_redraw_clean(rline_context_t * context);
|
||||
void rline_insert(rline_context_t * context, const char * what);
|
||||
int rline(char * buffer, int buf_size, rline_callbacks_t * callbacks);
|
||||
|
||||
void rline_history_insert(char * str);
|
||||
void rline_history_append_line(char * str);
|
||||
char * rline_history_get(int item);
|
||||
char * rline_history_prev(int item);
|
||||
|
||||
#define RLINE_HISTORY_ENTRIES 128
|
||||
extern char * rline_history[RLINE_HISTORY_ENTRIES];
|
||||
extern int rline_history_count;
|
||||
extern int rline_history_offset;
|
||||
extern int rline_scroll;
|
||||
extern char * rline_exit_string;
|
||||
|
||||
#endif /* _RLINE_H */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user