Files
rosetta/toolchain/mxdbg/client/line-ed/line-ed.c
T

349 lines
6.2 KiB
C
Raw Normal View History

#include "line-ed.h"
#include "history.h"
#include "hook.h"
#include "input.h"
#include "prompt.h"
#include <ctype.h>
#include <fx/stream.h>
#include <fx/term/tty.h>
#include <fx/wstr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
struct line_ed *line_ed_create(void)
{
struct line_ed *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->l_buf = malloc(LINE_MAX);
if (!out->l_buf) {
free(out);
return NULL;
}
out->l_history = fx_array_create();
if (!out->l_history) {
free(out->l_buf);
free(out);
return NULL;
}
pthread_mutex_init(&out->l_lock, NULL);
out->l_tty = fx_stdtty;
out->l_buf_end = out->l_buf + LINE_MAX;
out->l_buf_ptr = out->l_buf;
out->l_line_end = out->l_buf;
out->l_prompt[PROMPT_MAIN] = ">>> ";
out->l_prompt[PROMPT_CONT] = "> ";
return out;
}
void line_ed_destroy(struct line_ed *ed)
{
fx_array_unref(ed->l_history);
free(ed->l_buf);
free(ed);
}
void line_ed_set_flags(struct line_ed *ed, enum line_ed_flags flags)
{
ed->l_flags |= flags;
}
void line_ed_set_scope_type(struct line_ed *ed, const char *scope_type)
{
ed->l_scope_type = scope_type;
}
static void clear_buffer(struct line_ed *ed)
{
memset(ed->l_buf, 0x0, ed->l_buf_end - ed->l_buf);
ed->l_buf_ptr = ed->l_buf;
ed->l_line_end = ed->l_buf;
ed->l_cursor_x = 0;
ed->l_cursor_y = 0;
ed->l_continuations = 0;
}
static void convert_continuation_feeds(fx_wchar *s, size_t max)
{
fx_wchar *end = s + max;
size_t len = fx_wstrlen(s);
while (1) {
size_t r = fx_wstrcspn(s, L"\\");
if (s + r > end) {
break;
}
s += r;
len -= r;
if (*s == '\0') {
break;
}
if (*(s + 1) != '\n') {
s++;
continue;
}
memmove(s, s + 1, len);
s += 1;
}
}
static void remove_continuation_feeds(fx_wchar *s, size_t max)
{
fx_wchar *end = s + max;
size_t len = fx_wstrlen(s);
while (1) {
size_t r = fx_wstrcspn(s, L"\\");
if (s + r > end) {
break;
}
s += r;
len -= r;
if (*s == '\0') {
break;
}
if (*(s + 1) != '\n') {
s++;
continue;
}
memmove(s, s + 2, len);
s += 2;
}
}
static bool input_is_empty(struct line_ed *ed)
{
const fx_wchar *p = ed->l_buf;
while (p < ed->l_line_end) {
if (!fx_wchar_is_space(*p)) {
return false;
}
}
return true;
}
static void check_suspended(struct line_ed *ed)
{
pthread_mutex_lock(&ed->l_lock);
while (1) {
if (!ed->l_suspended) {
break;
}
pthread_cond_wait(&ed->l_suspended_cond, &ed->l_lock);
}
pthread_mutex_unlock(&ed->l_lock);
}
size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out)
{
clear_buffer(ed);
bool append_history = false;
size_t append_to_index = (size_t)-1;
if (!ed->l_scope_type) {
alloc_empty_history_entry(ed);
} else {
append_history = true;
append_to_index = ed->l_history_pos;
}
fx_tty *tty = ed->l_tty;
fx_tty_set_mode(tty, FX_TTY_RAW);
show_prompt(ed);
for (int i = 0; ed->l_buf[i]; i++) {
if (ed->l_buf[i] == '\n') {
fputc('\r', stdout);
}
fx_stream_write_char(fx_stdout, ed->l_buf[i]);
}
fflush(stdout);
bool end = false;
bool eof = false;
while (!end) {
check_suspended(ed);
fx_keycode key = fx_tty_read_key(tty);
hook_keypress(ed, key);
if (ed->l_suspended) {
continue;
}
if (key == FX_TTY_CTRL_KEY('d')) {
if (!input_is_empty(ed)) {
continue;
}
eof = true;
break;
}
if (key & FX_MOD_CTRL) {
continue;
}
switch (key) {
case FX_KEY_RETURN:
fx_tty_reset_vmode(tty);
if (ed->l_line_end > ed->l_buf
&& *(ed->l_line_end - 1) != '\\') {
end = true;
break;
}
if (input_is_empty(ed)) {
fputc('\r', stdout);
fputc('\n', stdout);
clear_buffer(ed);
show_prompt(ed);
break;
}
*ed->l_line_end = '\n';
ed->l_line_end++;
ed->l_buf_ptr = ed->l_line_end;
ed->l_cursor_x = 0;
ed->l_cursor_y++;
ed->l_continuations++;
fputc('\r', stdout);
fputc('\n', stdout);
// fputs("\033[G\n", stdout);
show_prompt(ed);
break;
case FX_KEY_BACKSPACE:
backspace(ed);
break;
case FX_KEY_ARROW_LEFT:
cursor_left(ed);
break;
case FX_KEY_ARROW_RIGHT:
cursor_right(ed);
break;
case FX_KEY_ARROW_UP:
arrow_up(ed);
break;
case FX_KEY_ARROW_DOWN:
arrow_down(ed);
break;
default:
if (fx_wchar_is_graph(key) || key == ' ') {
put_char(ed, key);
}
break;
}
}
fx_tty_set_mode(tty, FX_TTY_CANONICAL);
fputc('\n', stdout);
if (*ed->l_buf == '\0') {
return eof ? -1 : 0;
}
if (append_history) {
ed->l_history_pos = append_to_index;
append_buf_to_history(ed);
} else {
ed->l_history_pos = fx_array_get_size(ed->l_history) - 1;
const char *last = last_history_line(ed);
if (!last || fx_wastrcmp(last, ed->l_buf) != 0) {
save_buf_to_history(ed);
}
}
size_t sz = ed->l_line_end - ed->l_buf + 1;
if ((ed->l_flags & LINE_ED_REMOVE_CONTINUATIONS)
== LINE_ED_REMOVE_CONTINUATIONS) {
remove_continuation_feeds(ed->l_buf, sz);
} else if (
(ed->l_flags & LINE_ED_CONVERT_CONTINUATIONS)
== LINE_ED_CONVERT_CONTINUATIONS) {
convert_continuation_feeds(ed->l_buf, sz);
}
fx_stream_write_wstr(out, ed->l_buf, NULL);
if (!(ed->l_flags & LINE_ED_NO_TRAILING_LINEFEED)) {
fx_stream_write_char(out, '\n');
}
return sz;
}
int line_ed_suspend(struct line_ed *ed)
{
pthread_mutex_lock(&ed->l_lock);
ed->l_suspended = true;
pthread_cond_signal(&ed->l_suspended_cond);
fx_tty_clear(ed->l_tty, FX_TTY_CLEAR_LINE | FX_TTY_CLEAR_ALL);
fx_tty_set_mode(ed->l_tty, FX_TTY_CANONICAL);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, 0);
pthread_mutex_unlock(&ed->l_lock);
return 0;
}
int line_ed_resume(struct line_ed *ed)
{
pthread_mutex_lock(&ed->l_lock);
fx_tty_set_mode(ed->l_tty, FX_TTY_RAW);
fx_tty_clear(ed->l_tty, FX_TTY_CLEAR_LINE | FX_TTY_CLEAR_ALL);
show_prompt(ed);
size_t prompt_len = 0;
if (ed->l_cursor_y == 0) {
prompt_len = prompt_length(ed, PROMPT_MAIN);
}
size_t x = 0;
for (int i = 0; ed->l_buf[i]; i++) {
if (ed->l_buf[i] == '\n') {
fputc('\r', stdout);
}
fx_stream_write_char(fx_stdout, ed->l_buf[i]);
x++;
}
fflush(stdout);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, prompt_len + x);
ed->l_suspended = false;
pthread_cond_signal(&ed->l_suspended_cond);
pthread_mutex_unlock(&ed->l_lock);
return 0;
}