Share completion and add SSH line editing
This commit is contained in:
+114
-14
@@ -49,6 +49,7 @@ typedef struct {
|
||||
admin_ssh_console_token_t token;
|
||||
user_principal_t principal;
|
||||
size_t input_length;
|
||||
size_t input_cursor;
|
||||
uint8_t input[ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY + 1U];
|
||||
uint8_t history[ADMIN_SSH_CONSOLE_HISTORY_DEPTH]
|
||||
[ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY + 1U];
|
||||
@@ -57,6 +58,8 @@ typedef struct {
|
||||
uint8_t draft[ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY + 1U];
|
||||
size_t draft_length;
|
||||
uint8_t escape_state;
|
||||
uint8_t escape_parameters[4];
|
||||
size_t escape_parameter_length;
|
||||
bool discard_next_lf;
|
||||
admin_prompt_state_t prompt_state;
|
||||
bool prompt_hidden;
|
||||
@@ -185,12 +188,25 @@ static void print_prompt(const admin_ssh_console_token_t *token)
|
||||
static bool redraw_line_locked(admin_session_t *session)
|
||||
{
|
||||
static const char prefix[] = "\r\x1b[2Kadmin@serial-tool> ";
|
||||
size_t required = sizeof(prefix) - 1U + session->input_length;
|
||||
char cursor_back[16] = {0};
|
||||
size_t tail_length = session->input_length - session->input_cursor;
|
||||
size_t cursor_back_length = 0U;
|
||||
if (tail_length > 0U) {
|
||||
int written = snprintf(cursor_back, sizeof(cursor_back), "\x1b[%uD",
|
||||
(unsigned int)tail_length);
|
||||
if (written < 0 || (size_t)written >= sizeof(cursor_back)) {
|
||||
return false;
|
||||
}
|
||||
cursor_back_length = (size_t)written;
|
||||
}
|
||||
size_t required = sizeof(prefix) - 1U + session->input_length + cursor_back_length;
|
||||
if (required > ADMIN_SSH_CONSOLE_OUTPUT_CAPACITY - session->output_length) {
|
||||
return append_output_locked(session, (const uint8_t *)"\a", 1U);
|
||||
}
|
||||
(void)append_output_locked(session, (const uint8_t *)prefix, sizeof(prefix) - 1U);
|
||||
return append_output_locked(session, session->input, session->input_length);
|
||||
(void)append_output_locked(session, session->input, session->input_length);
|
||||
return cursor_back_length == 0U ||
|
||||
append_output_locked(session, (const uint8_t *)cursor_back, cursor_back_length);
|
||||
}
|
||||
|
||||
static void history_commit_locked(admin_session_t *session)
|
||||
@@ -210,6 +226,35 @@ static void history_commit_locked(admin_session_t *session)
|
||||
}
|
||||
}
|
||||
|
||||
static void history_move_locked(admin_session_t *session, bool older);
|
||||
|
||||
static void editor_key_locked(admin_session_t *session, uint8_t key)
|
||||
{
|
||||
if (key == 'A' || key == 'B') {
|
||||
history_move_locked(session, key == 'A');
|
||||
return;
|
||||
}
|
||||
if (key == 'C' && session->input_cursor < session->input_length) {
|
||||
++session->input_cursor;
|
||||
} else if (key == 'D' && session->input_cursor > 0U) {
|
||||
--session->input_cursor;
|
||||
} else if (key == 'H') {
|
||||
session->input_cursor = 0U;
|
||||
} else if (key == 'F') {
|
||||
session->input_cursor = session->input_length;
|
||||
} else if (key == 'X' && session->input_cursor < session->input_length) {
|
||||
memmove(session->input + session->input_cursor,
|
||||
session->input + session->input_cursor + 1U,
|
||||
session->input_length - session->input_cursor);
|
||||
--session->input_length;
|
||||
session->history_position = -1;
|
||||
} else {
|
||||
(void)append_output_locked(session, (const uint8_t *)"\a", 1U);
|
||||
return;
|
||||
}
|
||||
(void)redraw_line_locked(session);
|
||||
}
|
||||
|
||||
static void history_move_locked(admin_session_t *session, bool older)
|
||||
{
|
||||
if (older) {
|
||||
@@ -226,6 +271,7 @@ static void history_move_locked(admin_session_t *session, bool older)
|
||||
memcpy(session->input, session->history[session->history_position],
|
||||
sizeof(session->input));
|
||||
session->input_length = strlen((const char *)session->input);
|
||||
session->input_cursor = session->input_length;
|
||||
} else {
|
||||
if (session->history_position < 0) {
|
||||
(void)append_output_locked(session, (const uint8_t *)"\a", 1U);
|
||||
@@ -235,10 +281,12 @@ static void history_move_locked(admin_session_t *session, bool older)
|
||||
if (session->history_position < 0) {
|
||||
memcpy(session->input, session->draft, sizeof(session->input));
|
||||
session->input_length = session->draft_length;
|
||||
session->input_cursor = session->input_length;
|
||||
} else {
|
||||
memcpy(session->input, session->history[session->history_position],
|
||||
sizeof(session->input));
|
||||
session->input_length = strlen((const char *)session->input);
|
||||
session->input_cursor = session->input_length;
|
||||
}
|
||||
}
|
||||
(void)redraw_line_locked(session);
|
||||
@@ -855,13 +903,45 @@ bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
||||
}
|
||||
session->discard_next_lf = false;
|
||||
if (session->escape_state != 0U) {
|
||||
if (session->escape_state == 1U && (value == '[' || value == 'O')) {
|
||||
session->escape_state = 2U;
|
||||
} else if (session->escape_state == 2U) {
|
||||
if (value == 'A' || value == 'B') {
|
||||
history_move_locked(session, value == 'A');
|
||||
if (session->escape_state == 1U) {
|
||||
if (value == '[') {
|
||||
session->escape_state = 2U;
|
||||
session->escape_parameter_length = 0U;
|
||||
} else if (value == 'O') {
|
||||
session->escape_state = 3U;
|
||||
} else {
|
||||
session->escape_state = 0U;
|
||||
}
|
||||
} else if (session->escape_state == 3U) {
|
||||
editor_key_locked(session, value);
|
||||
session->escape_state = 0U;
|
||||
} else if (value >= 'A' && value <= 'Z') {
|
||||
editor_key_locked(session, value);
|
||||
session->escape_state = 0U;
|
||||
} else if (value == '~') {
|
||||
uint8_t key = 0U;
|
||||
if (session->escape_parameter_length > 0U) {
|
||||
switch (session->escape_parameters[0]) {
|
||||
case '1':
|
||||
case '7':
|
||||
key = 'H';
|
||||
break;
|
||||
case '3':
|
||||
key = 'X';
|
||||
break;
|
||||
case '4':
|
||||
case '8':
|
||||
key = 'F';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
editor_key_locked(session, key);
|
||||
session->escape_state = 0U;
|
||||
} else if ((value == ';' || (value >= '0' && value <= '9')) &&
|
||||
session->escape_parameter_length < sizeof(session->escape_parameters)) {
|
||||
session->escape_parameters[session->escape_parameter_length++] = value;
|
||||
} else {
|
||||
session->escape_state = 0U;
|
||||
}
|
||||
@@ -876,6 +956,12 @@ bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
||||
continue;
|
||||
}
|
||||
if (value == '\t') {
|
||||
if (session->input_cursor != session->input_length) {
|
||||
(void)append_output_locked(session, (const uint8_t *)"\a", 1U);
|
||||
++*consumed;
|
||||
taskEXIT_CRITICAL(&s_lock);
|
||||
continue;
|
||||
}
|
||||
char current[ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY + 1U];
|
||||
memcpy(current, session->input, sizeof(current));
|
||||
++*consumed;
|
||||
@@ -890,6 +976,7 @@ bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
||||
if (expanded) {
|
||||
strlcpy((char *)session->input, completed, sizeof(session->input));
|
||||
session->input_length = strlen((const char *)session->input);
|
||||
session->input_cursor = session->input_length;
|
||||
session->history_position = -1;
|
||||
(void)redraw_line_locked(session);
|
||||
} else {
|
||||
@@ -910,6 +997,7 @@ bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
||||
request.principal = session->principal;
|
||||
secure_wipe(session->input, sizeof(session->input));
|
||||
session->input_length = 0U;
|
||||
session->input_cursor = 0U;
|
||||
session->history_position = -1;
|
||||
session->command_pending = true;
|
||||
(void)append_output_locked(session, (const uint8_t *)"\r\n",
|
||||
@@ -918,29 +1006,41 @@ bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
||||
} else if (value == 0x03U) {
|
||||
secure_wipe(session->input, sizeof(session->input));
|
||||
session->input_length = 0U;
|
||||
session->input_cursor = 0U;
|
||||
session->history_position = -1;
|
||||
(void)append_output_locked(session, (const uint8_t *)"^C\r\n",
|
||||
sizeof("^C\r\n") - 1U);
|
||||
(void)append_output_locked(session, (const uint8_t *)"admin@serial-tool> ",
|
||||
sizeof("admin@serial-tool> ") - 1U);
|
||||
} else if (value == 0x08U || value == 0x7fU) {
|
||||
if (session->input_length > 0U) {
|
||||
session->input[--session->input_length] = 0U;
|
||||
if (session->input_cursor > 0U) {
|
||||
memmove(session->input + session->input_cursor - 1U,
|
||||
session->input + session->input_cursor,
|
||||
session->input_length - session->input_cursor + 1U);
|
||||
--session->input_cursor;
|
||||
--session->input_length;
|
||||
session->history_position = -1;
|
||||
(void)append_output_locked(session, (const uint8_t *)"\b \b",
|
||||
sizeof("\b \b") - 1U);
|
||||
(void)redraw_line_locked(session);
|
||||
}
|
||||
} else if (value >= 0x20U && value <= 0x7eU) {
|
||||
if (session->input_length >= ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY) {
|
||||
session->input_length = 0U;
|
||||
session->input_cursor = 0U;
|
||||
(void)append_output_locked(session, (const uint8_t *)
|
||||
"\r\nCommand too long; discarded.\r\nadmin@serial-tool> ",
|
||||
sizeof("\r\nCommand too long; discarded.\r\nadmin@serial-tool> ") - 1U);
|
||||
} else {
|
||||
memmove(session->input + session->input_cursor + 1U,
|
||||
session->input + session->input_cursor,
|
||||
session->input_length - session->input_cursor + 1U);
|
||||
session->input[session->input_cursor++] = value;
|
||||
++session->input_length;
|
||||
session->history_position = -1;
|
||||
session->input[session->input_length++] = value;
|
||||
session->input[session->input_length] = 0U;
|
||||
(void)append_output_locked(session, &value, 1U);
|
||||
if (session->input_cursor == session->input_length) {
|
||||
(void)append_output_locked(session, &value, 1U);
|
||||
} else {
|
||||
(void)redraw_line_locked(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
++*consumed;
|
||||
|
||||
Reference in New Issue
Block a user