|
#include "rterm.h"
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include "rstring.h"
|
|
#include "rrex4.h"
|
|
#include <limits.h>
|
|
#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;
|
|
} |