232 lines
5.8 KiB
C
232 lines
5.8 KiB
C
#include <m-string.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "cli.h"
|
|
#include "delay.h"
|
|
#include "helpers.h"
|
|
#include "cli-commands.h"
|
|
#include <esp_log.h>
|
|
|
|
typedef enum {
|
|
CliSymbolAsciiSOH = 0x01,
|
|
CliSymbolAsciiETX = 0x03,
|
|
CliSymbolAsciiEOT = 0x04,
|
|
CliSymbolAsciiBell = 0x07,
|
|
CliSymbolAsciiBackspace = 0x08,
|
|
CliSymbolAsciiTab = 0x09,
|
|
CliSymbolAsciiCR = 0x0D,
|
|
CliSymbolAsciiEsc = 0x1B,
|
|
CliSymbolAsciiUS = 0x1F,
|
|
CliSymbolAsciiSpace = 0x20,
|
|
CliSymbolAsciiDel = 0x7F,
|
|
} CliSymbols;
|
|
|
|
struct Cli {
|
|
mstring_t* line;
|
|
mstring_t* prev_line;
|
|
size_t cursor_position;
|
|
bool esc_mode;
|
|
|
|
void* context;
|
|
CliWrite write_cb;
|
|
CliFlush flush_cb;
|
|
};
|
|
|
|
Cli* cli_init(void) {
|
|
Cli* cli = malloc(sizeof(Cli));
|
|
cli->line = mstring_alloc();
|
|
cli->prev_line = mstring_alloc();
|
|
cli->context = NULL;
|
|
cli->write_cb = NULL;
|
|
cli->flush_cb = NULL;
|
|
cli->esc_mode = false;
|
|
|
|
return cli;
|
|
}
|
|
|
|
void cli_reset(Cli* cli) {
|
|
mstring_reset(cli->line);
|
|
cli->cursor_position = 0;
|
|
}
|
|
|
|
void cli_set_context(Cli* cli, void* context) {
|
|
cli->context = context;
|
|
}
|
|
|
|
void cli_set_write_cb(Cli* cli, CliWrite write_cb) {
|
|
cli->write_cb = write_cb;
|
|
}
|
|
|
|
void cli_set_flush_cb(Cli* cli, CliFlush flush_cb) {
|
|
cli->flush_cb = flush_cb;
|
|
}
|
|
|
|
void cli_write(Cli* cli, const uint8_t* data, size_t data_size) {
|
|
if(cli->write_cb != NULL) {
|
|
cli->write_cb(data, data_size, cli->context);
|
|
}
|
|
}
|
|
|
|
void cli_flush(Cli* cli) {
|
|
if(cli->flush_cb != NULL) {
|
|
cli->flush_cb(cli->context);
|
|
}
|
|
}
|
|
|
|
void cli_write_str(Cli* cli, const char* str) {
|
|
cli_write(cli, (const uint8_t*)str, strlen(str));
|
|
}
|
|
|
|
void cli_write_char(Cli* cli, uint8_t c) {
|
|
cli_write(cli, &c, 1);
|
|
}
|
|
|
|
void cli_write_eol(Cli* cli) {
|
|
cli_write_str(cli, "\r\n");
|
|
}
|
|
|
|
void cli_printf(Cli* cli, char* format, ...) {
|
|
mstring_t* str = mstring_alloc();
|
|
va_list args;
|
|
va_start(args, format);
|
|
mstring_vprintf(str, format, args);
|
|
va_end(args);
|
|
cli_write_str(cli, mstring_get_cstr(str));
|
|
mstring_free(str);
|
|
}
|
|
|
|
static void cli_write_motd(Cli* cli) {
|
|
cli_write_str(cli, "Hello!\r\nThis is MOTD\r\n");
|
|
}
|
|
|
|
static void cli_write_prompt(Cli* cli) {
|
|
cli_write_str(cli, ">: ");
|
|
}
|
|
|
|
static const CliItem* cli_search_item(Cli* cli, const mstring_t* command) {
|
|
const CliItem* item = NULL;
|
|
|
|
for(size_t i = 0; i < cli_items_count; i++) {
|
|
if(mstring_cmp_cstr(command, cli_items[i].name) == 0) {
|
|
item = &cli_items[i];
|
|
}
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
static bool cli_handle_enter(Cli* cli) {
|
|
mstring_t* command = mstring_alloc();
|
|
mstring_t* args = mstring_alloc();
|
|
bool result = false;
|
|
const CliItem* item = NULL;
|
|
|
|
mstring_set(cli->prev_line, mstring_get_cstr(cli->line));
|
|
mstring_strim(cli->line, " \n\r\t");
|
|
|
|
size_t ws = mstring_search_char(cli->line, ' ', 0);
|
|
if(ws == STRING_FAILURE) {
|
|
mstring_set(command, mstring_get_cstr(cli->line));
|
|
} else {
|
|
mstring_set_n(command, cli->line, 0, ws);
|
|
mstring_set_n(args, cli->line, ws, mstring_size(cli->line));
|
|
mstring_strim(args, " \n\r\t");
|
|
}
|
|
|
|
item = cli_search_item(cli, command);
|
|
if(item != NULL) {
|
|
item->callback(cli, args);
|
|
} else {
|
|
cli_write_str(cli, "Not found");
|
|
}
|
|
|
|
mstring_free(command);
|
|
mstring_free(args);
|
|
return result;
|
|
}
|
|
|
|
static void cli_handle_backspace(Cli* cli) {
|
|
if(mstring_size(cli->line) > 0) {
|
|
// Other side
|
|
cli_write_str(cli, "\e[D\e[1P");
|
|
// Our side
|
|
mstring_set_strn(cli->line, mstring_get_cstr(cli->line), mstring_size(cli->line) - 1);
|
|
cli->cursor_position--;
|
|
} else {
|
|
cli_write_char(cli, CliSymbolAsciiBell);
|
|
}
|
|
}
|
|
|
|
void cli_handle_char(Cli* cli, uint8_t c) {
|
|
if(cli->esc_mode) {
|
|
switch(c) {
|
|
case '[':
|
|
cli->esc_mode = true;
|
|
break;
|
|
case 'A':
|
|
if(mstring_size(cli->line) == 0 && mstring_cmp(cli->line, cli->prev_line) != 0) {
|
|
// Set line buffer and cursor position
|
|
mstring_set(cli->line, mstring_get_cstr(cli->prev_line));
|
|
cli->cursor_position = mstring_size(cli->line);
|
|
// Show new line to user
|
|
cli_write_str(cli, mstring_get_cstr(cli->line));
|
|
}
|
|
cli->esc_mode = false;
|
|
break;
|
|
default:
|
|
cli->esc_mode = false;
|
|
break;
|
|
}
|
|
} else {
|
|
switch(c) {
|
|
case CliSymbolAsciiEsc:
|
|
cli->esc_mode = true;
|
|
break;
|
|
case CliSymbolAsciiCR:
|
|
if(mstring_size(cli->line) == 0) {
|
|
cli_write_eol(cli);
|
|
} else {
|
|
cli_write_eol(cli);
|
|
cli_handle_enter(cli);
|
|
cli_reset(cli);
|
|
cli_write_eol(cli);
|
|
}
|
|
cli_write_prompt(cli);
|
|
break;
|
|
case CliSymbolAsciiDel:
|
|
case CliSymbolAsciiBackspace:
|
|
cli_handle_backspace(cli);
|
|
break;
|
|
case CliSymbolAsciiSOH:
|
|
delay(33);
|
|
cli_write_motd(cli);
|
|
cli_write_eol(cli);
|
|
cli_write_prompt(cli);
|
|
break;
|
|
case CliSymbolAsciiETX:
|
|
cli_reset(cli);
|
|
cli_write_eol(cli);
|
|
cli_write_prompt(cli);
|
|
break;
|
|
case CliSymbolAsciiEOT:
|
|
cli_reset(cli);
|
|
break;
|
|
case ' ' ... '~':
|
|
if(cli->cursor_position == mstring_size(cli->line)) {
|
|
mstring_push_back(cli->line, c);
|
|
cli_write_char(cli, c);
|
|
} else {
|
|
mstring_push_back(cli->line, c);
|
|
cli_write_str(cli, "\e[4h");
|
|
cli_write_char(cli, c);
|
|
cli_write_str(cli, "\e[4l");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
cli_flush(cli);
|
|
}
|