Display SSH completion candidates for ambiguous prefixes
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#define ADMIN_UART_CONSOLE_TASK_PRIORITY 3U
|
||||
#define ADMIN_SSH_CONSOLE_MAX_ARGUMENTS 10U
|
||||
#define ADMIN_SSH_CONSOLE_HISTORY_DEPTH 4U
|
||||
#define ADMIN_SSH_CONSOLE_COMPLETION_OUTPUT_CAPACITY 2048U
|
||||
#define ADMIN_SSH_CONTROL_QUEUE_LENGTH 2U
|
||||
#define ADMIN_SSH_CONTROL_TASK_STACK_SIZE 4096U
|
||||
#define ADMIN_SSH_CONTROL_TASK_PRIORITY 3U
|
||||
@@ -92,6 +93,8 @@ typedef struct {
|
||||
|
||||
static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static admin_session_t s_sessions[ADMIN_SSH_CONSOLE_MAX_SESSIONS];
|
||||
/* admin_ssh_console_feed_input() is called only by the sole SSH owner task. */
|
||||
static char s_completion_output[ADMIN_SSH_CONSOLE_COMPLETION_OUTPUT_CAPACITY];
|
||||
|
||||
static StaticQueue_t s_request_queue_storage;
|
||||
static uint8_t s_request_queue_bytes[ADMIN_SSH_CONSOLE_REQUEST_QUEUE_LENGTH *
|
||||
@@ -967,8 +970,13 @@ bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
||||
++*consumed;
|
||||
taskEXIT_CRITICAL(&s_lock);
|
||||
char completed[ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY + 1U] = {0};
|
||||
size_t candidates_length = 0U;
|
||||
bool expanded = console_completion_expand(current, completed,
|
||||
sizeof(completed));
|
||||
bool candidates_formatted = !expanded &&
|
||||
console_completion_format_matches(current, s_completion_output,
|
||||
sizeof(s_completion_output),
|
||||
&candidates_length);
|
||||
taskENTER_CRITICAL(&s_lock);
|
||||
session = &s_sessions[token->slot_index];
|
||||
if (token_matches(session, token) && !session->command_pending &&
|
||||
@@ -979,6 +987,14 @@ bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
||||
session->input_cursor = session->input_length;
|
||||
session->history_position = -1;
|
||||
(void)redraw_line_locked(session);
|
||||
} else if (candidates_formatted && candidates_length > 0U &&
|
||||
candidates_length + 2U + sizeof("admin@serial-tool> ") - 1U +
|
||||
session->input_length <=
|
||||
ADMIN_SSH_CONSOLE_OUTPUT_CAPACITY - session->output_length) {
|
||||
(void)append_output_locked(session, (const uint8_t *)"\r\n", 2U);
|
||||
(void)append_output_locked(session, (const uint8_t *)s_completion_output,
|
||||
candidates_length);
|
||||
(void)redraw_line_locked(session);
|
||||
} else {
|
||||
(void)append_output_locked(session, (const uint8_t *)"\a", 1U);
|
||||
}
|
||||
|
||||
@@ -270,6 +270,47 @@ bool console_completion_expand(const char *line, char *completed, size_t capacit
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *output;
|
||||
size_t capacity;
|
||||
size_t length;
|
||||
bool complete;
|
||||
} completion_format_context_t;
|
||||
|
||||
static bool format_completion_candidate(const char *candidate, void *context)
|
||||
{
|
||||
completion_format_context_t *result = context;
|
||||
size_t candidate_length = strlen(candidate);
|
||||
if (candidate_length + 2U > result->capacity - result->length) {
|
||||
result->complete = false;
|
||||
return false;
|
||||
}
|
||||
memcpy(result->output + result->length, candidate, candidate_length);
|
||||
result->length += candidate_length;
|
||||
result->output[result->length++] = '\r';
|
||||
result->output[result->length++] = '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
bool console_completion_format_matches(const char *line, char *output, size_t capacity,
|
||||
size_t *output_length)
|
||||
{
|
||||
if (line == NULL || output == NULL || output_length == NULL || capacity == 0U) {
|
||||
return false;
|
||||
}
|
||||
completion_format_context_t result = {
|
||||
.output = output,
|
||||
.capacity = capacity,
|
||||
.complete = true,
|
||||
};
|
||||
console_completion_visit(line, format_completion_candidate, &result);
|
||||
if (!result.complete) {
|
||||
return false;
|
||||
}
|
||||
*output_length = result.length;
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t console_read_with_late_terminal_upgrade(int file_descriptor,
|
||||
void *buffer,
|
||||
size_t size)
|
||||
|
||||
@@ -22,6 +22,15 @@ void console_completion_visit(const char *line,
|
||||
/* Bounded longest-prefix completion shared by the UART and admin SSH frontends. */
|
||||
bool console_completion_expand(const char *line, char *completed, size_t capacity);
|
||||
|
||||
/*
|
||||
* Format the matching candidates as CRLF-terminated lines for a frontend that
|
||||
* cannot use linenoise's native completion display. A successful empty result
|
||||
* means no candidate matched; false means the supplied output buffer was too
|
||||
* small or an argument was invalid.
|
||||
*/
|
||||
bool console_completion_format_matches(const char *line, char *output, size_t capacity,
|
||||
size_t *output_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user