#include "rterm.h" #include #include #include "rstring.h" #include "rrex4.h" #include #include "rautocomplete.h" typedef struct ricli_line_t { unsigned int index; char type[20]; size_t length; char *content; } ricli_line_t; ricli_line_t *ricli_line_new() { ricli_line_t *line = (ricli_line_t *)malloc(sizeof(ricli_line_t)); line->index = 0; memset(line->type, 0, sizeof(line->type)); line->length = 0; line->content = NULL; return line; } char *rscli_line_to_json(ricli_line_t *line) { char *json = (char *)malloc(sizeof(line->type) + strlen(line->content) * 2 + 10); json[0] = 0; strcpy(json, "{\"type\":\""); strcat(json, line->type); strcat(json, "\",\"content\":\""); char content_safe[strlen(line->content) * 2]; content_safe[0] = 0; rstraddslashes(line->content, content_safe); strcat(json, content_safe); strcat(json, "\"}"); return json; } typedef struct ricli_t { ricli_line_t **lines; int line_count; bool line_numbers; char input[1024 * 5]; unsigned int history_index; unsigned int x; bool auto_save; rautocomplete_t *autocomplete; char history_file[FILENAME_MAX]; bool reset; void (*before_add_line)(struct ricli_t *r); void (*after_add_line)(struct ricli_t *r); void (*keypress)(struct ricli_t *); void (*before_draw)(struct ricli_t *); rterm_t *term; } ricli_t; void ricli_keypress(rterm_t *rt); void ricli_before_draw(rterm_t *rt); void ricli_save(ricli_t *cli, char *path); void ricli_autocomplete_execute(ricli_t *cli); void ricli_add_autocomplete(ricli_t *cli, char *str) { if (rautocomplete_contains(cli->autocomplete, str)) return; rautocomplete_add(cli->autocomplete, str); } ricli_line_t *ricli_get_last_line(ricli_t *r) { if (!r->line_count) { return NULL; } return r->lines[r->line_count - 1]; } void ricli_after_draw(rterm_t *rt) { ricli_t *r = (ricli_t *)rt->session; ricli_autocomplete_execute(r); } ricli_t *ricli_terminal_new() { ricli_t *terminal = (ricli_t *)malloc(sizeof(ricli_t)); terminal->lines = NULL; terminal->line_count = 0; terminal->line_numbers = false; terminal->reset = true; terminal->history_index = 0; terminal->before_add_line = NULL; terminal->term = NULL; terminal->history_file[0] = 0; terminal->autocomplete = rautocomplete_new(); terminal->auto_save = true; terminal->x = 0; memset(terminal->input, 0, sizeof(terminal->input)); terminal->term = (rterm_t *)malloc(sizeof(rterm_t)); rterm_init(terminal->term); terminal->line_numbers = true; terminal->term->after_key_press = ricli_keypress; terminal->term->before_draw = ricli_before_draw; terminal->term->after_draw = ricli_after_draw; terminal->term->session = (void *)terminal; return terminal; } void ricli_set_input(ricli_t *cli, const char *content); void ricli_autocomplete_execute(ricli_t *r) { char *result = rautocomplete_find(r->autocomplete, r->input); unsigned int original_x = r->term->cursor.x; unsigned int original_y = r->term->cursor.y; if (result && result[0] != 1) { original_x = r->x; cursor_set(r->term, 0, r->term->size.ws_row - 1); printf("(%d)%s", result[0], result); cursor_set(r->term, original_x, original_y); } } void ricli_add_line(ricli_t *r, char *type, char *content) { ricli_line_t *line = ricli_line_new(); strcpy(line->type, type ? type : ""); line->content = (char *)malloc(strlen(content ? content : "") + 1); strcpy(line->content, content ? content : ""); line->length = strlen(line->content); if (line->length && line->content[line->length - 1] == '\n') { line->content[line->length - 1] = 0; line->length--; } if (line->length) ricli_add_autocomplete(r, line->content); strcpy(line->type, type ? type : ""); line->index = r->line_count; r->lines = realloc(r->lines, sizeof(ricli_line_t *) * (r->line_count + 1)); r->lines[r->line_count] = line; r->line_count++; r->history_index = r->line_count; r->x = 0; if (r->history_file[0] && r->auto_save) ricli_save(r, r->history_file); } ricli_t *rt_get_ricli(rterm_t *rt) { return (ricli_t *)rt->session; } void ricli_reset(rterm_t *rt) { ricli_t *cli = rt_get_ricli(rt); cli->reset = false; cursor_set(rt, 0, rt->size.ws_row - 1); } void ricli_before_draw(rterm_t *rt) { ricli_t *cli = rt_get_ricli(rt); int offset = 0; if (cli->line_count > rt->size.ws_row - 1) { offset = cli->line_count - rt->size.ws_row; } for (int i = offset; i < cli->line_count; i++) { printf("%.5d %s\n", i + 1, cli->lines[i]->content); } rt->status_text = cli->input; if (cli->reset) { ricli_reset(rt); } } void ricli_clear_input(ricli_t *cli) { char line[cli->term->size.ws_col + 1]; memset(line, ' ', sizeof(line)); line[sizeof(line) - 1] = 0; cursor_set(cli->term, 0, cli->term->cursor.y); } void ricli_set_input(ricli_t *cli, const char *content) { if (cli->input != content) { memset(cli->input, 0, sizeof(cli->input)); strcpy(cli->input, content); } strcpy(cli->term->status_text, cli->input); ricli_clear_input(cli); rterm_print_status_bar(cli->term, 'c', cli->input); cursor_set(cli->term, cli->x, cli->term->size.ws_row); } void ricli_put_input(ricli_t *cli, char c) { bool was_zero = cli->input[cli->x] == 0; if (was_zero) { cli->input[cli->x] = c; cli->input[cli->x + 1] = 0; } else { char line_first[strlen(cli->input) + 5]; memset(line_first, 0, sizeof(line_first)); line_first[0] = 0; strncpy(line_first, cli->input, cli->x); char line_end[strlen(cli->input) + 2]; memset(line_end, 0, sizeof(line_end)); char *input_ptr = cli->input; strcpy(line_end, input_ptr + cli->x); char new_char[] = {c, 0}; strcat(line_first, new_char); strcat(line_first, line_end); memset(cli->input, 0, sizeof(cli->input)); strcpy(cli->input, line_first); } cli->history_index = cli->line_count; rterm_print_status_bar(cli->term, 'c', cli->input); if (cli->x >= strlen(cli->input)) cli->x = strlen(cli->input) - 1; cli->x++; cursor_set(cli->term, cli->x, cli->term->cursor.y); } void ricli_load(ricli_t *cli, char *path) { strcpy(cli->history_file, path); size_t size = rfile_size(path); if (size == 0) { return; } char *data = malloc(size + 1); memset(data, 0, size + 1); rfile_readb(path, data, size); r4_t *r = r4(data, "\"type\":\"(.*)\",\"content\":\"(.*)\""); while (r->match_count == 2) { char stripped_slashes[strlen(r->matches[1]) + 1]; memset(stripped_slashes, 0, sizeof(stripped_slashes)); rstrstripslashes(r->matches[1], stripped_slashes); ricli_add_line(cli, r->matches[0], stripped_slashes); r4_next(r, NULL); } r4_free(r); free(data); } void ricli_save(ricli_t *cli, char *path) { FILE *f = fopen(path, "w+"); for (int i = 0; i < cli->line_count; i++) { if (!cli->lines[i]->length) continue; char *json_line = rscli_line_to_json(cli->lines[i]); if (i != cli->line_count - 1) { strcat(json_line, ","); } fwrite(json_line, 1, strlen(json_line), f); free(json_line); } fclose(f); } ricli_delete_input(ricli_t *cli, unsigned int index) { if (cli->input[index + 1] == 0) { cli->input[index] = 0; } else { char new_line[strlen(cli->input) + 5]; memset(new_line, 0, sizeof(new_line)); strncpy(new_line, cli->input, index); char *input_ptr = cli->input; strcat(new_line, input_ptr + index + 1); strcpy(cli->input, new_line); } cursor_set(cli->term, cli->x, cli->term->cursor.y); } void ricli_keypress(rterm_t *rt) { ricli_t *cli = rt_get_ricli(rt); if (rt->key.c == 10) { if (cli->input[rt->cursor.x] == 0) { cli->input[rt->cursor.x] = '\n'; cli->input[rt->cursor.x + 1] = 0; } if (cli->before_add_line) { cli->before_add_line(cli); } ricli_add_line(cli, "user", cli->input); cursor_set(rt, 0, rt->cursor.y); memset(cli->input, 0, sizeof(cli->input)); cli->history_index = cli->line_count; if (cli->after_add_line) { cli->after_add_line(cli); } } else if (rt->key.escape && rt->key.c == 'A') { if (cli->history_index != 0) cli->history_index--; strcpy(cli->input, cli->lines[cli->history_index]->content); cli->x = strlen(cli->input); ricli_set_input(cli, cli->lines[cli->history_index]->content); } else if (rt->key.c == 127) { if (cli->x > 0) { cli->x--; cli->input[cli->x] = 0; ricli_delete_input(cli, cli->x); } } else if (rt->key.escape && rt->key.c == 'B') { if (cli->history_index < cli->line_count - 1) { cli->history_index++; strcpy(cli->input, cli->lines[cli->history_index]->content); cli->x = strlen(cli->input); ricli_set_input(cli, cli->lines[cli->history_index]->content); } else { cli->x = 0; ricli_set_input(cli, ""); cli->history_index = cli->line_count; } } else if (rt->key.escape && rt->key.c == 'D') { cli->x = rt->cursor.x; cursor_set(rt, cli->x, rt->cursor.y); } else if (rt->key.escape && rt->key.c == 'C') { cli->x = rt->cursor.x; if (cli->x > strlen(cli->input)) cli->x = strlen(cli->input); cursor_set(rt, cli->x, rt->cursor.y); } else if (!rt->key.escape) { if (rt->cursor.x > strlen(cli->input)) { rt->cursor.x = strlen(cli->input); cli->x = strlen(cli->input); } ricli_put_input(cli, rt->key.c); ricli_autocomplete_execute(cli); } // rterm_print_status_bar(rt, 0, 0); } void ricli_loop(ricli_t *r) { rterm_loop(r->term); } int main() { ricli_t *cli = ricli_terminal_new(); ricli_load(cli, "/tmp/.ricli.json"); ricli_loop(cli); return 0; }