From 4ae79cffba89c9f619dc65414291db9ae71208a4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 24 Jul 2018 01:24:16 +0200 Subject: lib: update from upstream --- lib/fmt.c | 1 - lib/shell.c | 1167 ++++++++++++++++++++++++++++----------------------------- lib/shell.h | 113 ++++-- lib/shell_i.h | 145 +++++++ src/main.c | 65 +++- src/main.h | 30 ++ src/sw.c | 23 +- 7 files changed, 905 insertions(+), 639 deletions(-) create mode 100644 lib/shell_i.h create mode 100644 src/main.h diff --git a/lib/fmt.c b/lib/fmt.c index 90e31f4..26560b1 100644 --- a/lib/fmt.c +++ b/lib/fmt.c @@ -118,7 +118,6 @@ struct fmt_sscanf_state { va_list ap; unsigned int flags; int width; - int precision; unsigned int modifier; unsigned int specifier; unsigned int base; diff --git a/lib/shell.c b/lib/shell.c index 3fada36..8c3e86b 100644 --- a/lib/shell.c +++ b/lib/shell.c @@ -23,7 +23,9 @@ * http://git.sceen.net/rbraun/librbraun.git/ */ +#include #include +#include #include #include #include @@ -32,47 +34,16 @@ #include #include #include +#include #include #include #include #include -#define SHELL_STACK_SIZE 4096 - -/* - * Binary exponent and size of the hash table used to store commands. - */ -#define SHELL_HTABLE_BITS 6 -#define SHELL_HTABLE_SIZE (1 << SHELL_HTABLE_BITS) - -struct shell_bucket { - struct shell_cmd *cmd; -}; - -/* - * Hash table for quick command lookup. - */ -static struct shell_bucket shell_htable[SHELL_HTABLE_SIZE]; - #define SHELL_COMPLETION_MATCH_FMT "-16s" #define SHELL_COMPLETION_NR_MATCHES_PER_LINE 4 -/* - * Sorted command list. - */ -static struct shell_cmd *shell_list; - -/* - * Lock protecting access to the hash table and list of commands. - * - * Note that this lock only protects access to the commands containers, - * not the commands themselves. In particular, it is not necessary to - * hold this lock when a command is used, i.e. when accessing a command - * name, function pointer, or description. - */ -static struct mutex shell_lock; - /* * Escape sequence states. * @@ -84,61 +55,13 @@ static struct mutex shell_lock; #define SHELL_ESC_STATE_START 1 #define SHELL_ESC_STATE_CSI 2 -/* - * This value changes depending on the standard used and was chosen arbitrarily. - */ -#define SHELL_ESC_SEQ_MAX_SIZE 8 - -typedef void (*shell_esc_seq_fn)(void); +typedef void (*shell_esc_seq_fn)(struct shell *shell); struct shell_esc_seq { const char *str; shell_esc_seq_fn fn; }; -#define SHELL_LINE_MAX_SIZE 64 - -/* - * Line containing a shell entry. - * - * The string must be nul-terminated. The size doesn't include this - * additional nul character, the same way strlen() doesn't account for it. - */ -struct shell_line { - char str[SHELL_LINE_MAX_SIZE]; - unsigned long size; -}; - -/* - * Number of entries in the history. - * - * One of these entryes is used as the current line. - */ -#define SHELL_HISTORY_SIZE 21 - -#if SHELL_HISTORY_SIZE == 0 -#error "shell history size must be non-zero" -#endif /* SHELL_HISTORY_SIZE == 0 */ - -/* - * Shell history. - * - * The history is never empty. There is always at least one entry, the - * current line, referenced by the newest (most recent) index. The array - * is used like a circular buffer, i.e. old entries are implicitely - * erased by new ones. The index references the entry used as a template - * for the current line. - */ -static struct shell_line shell_history[SHELL_HISTORY_SIZE]; -static unsigned long shell_history_newest; -static unsigned long shell_history_oldest; -static unsigned long shell_history_index; - -/* - * Cursor within the current line. - */ -static unsigned long shell_cursor; - #define SHELL_SEPARATOR ' ' /* @@ -149,20 +72,6 @@ static unsigned long shell_cursor; #define SHELL_ERASE_BS '\b' #define SHELL_ERASE_DEL '\x7f' -/* - * Buffer used to store the current line during argument processing. - * - * The pointers in the argv array point inside this buffer. The - * separators immediately following the arguments are replaced with - * nul characters. - */ -static char shell_tmp_line[SHELL_LINE_MAX_SIZE]; - -#define SHELL_MAX_ARGS 16 - -static int shell_argc; -static char *shell_argv[SHELL_MAX_ARGS]; - static const char * shell_find_word(const char *str) { @@ -197,563 +106,600 @@ shell_cmd_name(const struct shell_cmd *cmd) return cmd->name; } -static inline struct shell_bucket * -shell_bucket_get(const char *name) +static int +shell_cmd_check_char(char c) { - return &shell_htable[hash_str(name, SHELL_HTABLE_BITS)]; -} + if (((c >= 'a') && (c <= 'z')) + || ((c >= 'A') && (c <= 'Z')) + || ((c >= '0') && (c <= '9')) + || (c == '-') + || (c == '_')) { + return 0; + } -static void -shell_cmd_acquire(void) -{ - mutex_lock(&shell_lock); + return EINVAL; } -static void -shell_cmd_release(void) +static int +shell_cmd_check(const struct shell_cmd *cmd) { - mutex_unlock(&shell_lock); -} + size_t len; + int error; -static const struct shell_cmd * -shell_cmd_lookup(const char *name) -{ - const struct shell_bucket *bucket; - const struct shell_cmd *cmd; + len = strlen(cmd->name); - shell_cmd_acquire(); + if (len == 0) { + return EINVAL; + } - bucket = shell_bucket_get(name); + for (size_t i = 0; i < len; i++) { + error = shell_cmd_check_char(cmd->name[i]); - for (cmd = bucket->cmd; cmd != NULL; cmd = cmd->ht_next) { - if (strcmp(cmd->name, name) == 0) { - break; + if (error) { + return error; } } - shell_cmd_release(); + return 0; +} - return cmd; +static const char * +shell_line_str(const struct shell_line *line) +{ + return line->str; } -/* - * Look up the first command that matches a given string. - * - * The input string is defined by the given string pointer and size. - * - * The global lock must be acquired before calling this function. - */ -static const struct shell_cmd * -shell_cmd_match(const struct shell_cmd *cmd, const char *str, - unsigned long size) +static size_t +shell_line_size(const struct shell_line *line) { - while (cmd != NULL) { - if (strncmp(cmd->name, str, size) == 0) { - return cmd; - } + return line->size; +} - cmd = cmd->ls_next; - } +static void +shell_line_reset(struct shell_line *line) +{ + line->str[0] = '\0'; + line->size = 0; +} - return NULL; +static void +shell_line_copy(struct shell_line *dest, const struct shell_line *src) +{ + strcpy(dest->str, src->str); + dest->size = src->size; } -/* - * Attempt command auto-completion. - * - * The given string is the beginning of a command, or the empty string. - * The sizep parameter initially points to the size of the given string. - * If the string matches any registered command, the cmdp pointer is - * updated to point to the first matching command in the sorted list of - * commands, and sizep is updated to the number of characters in the - * command name that are common in subsequent commands. The command - * pointer and the returned size can be used to print a list of commands - * eligible for completion. - * - * If there is a single match for the given string, return 0. If there - * are more than one match, return EAGAIN. If there is no match, - * return EINVAL. - * - * The global lock must be acquired before calling this function. - */ static int -shell_cmd_complete(const char *str, unsigned long *sizep, - const struct shell_cmd **cmdp) +shell_line_cmp(const struct shell_line *a, const struct shell_line *b) { - const struct shell_cmd *cmd, *next; - unsigned long size; - - size = *sizep; + return strcmp(a->str, b->str); +} - /* - * Start with looking up a command that matches the given argument. - * If there is no match, return an error. - */ - cmd = shell_cmd_match(shell_list, str, size); +static int +shell_line_insert(struct shell_line *line, size_t index, char c) +{ + size_t remaining_chars; - if (cmd == NULL) { + if (index > line->size) { return EINVAL; } - *cmdp = cmd; + if ((line->size + 1) == sizeof(line->str)) { + return ENOMEM; + } - /* - * If at least one command matches, try to complete it. - * There can be two cases : - * 1/ There is one and only one match, which is directly returned. - * 2/ There are several matches, in which case the common length is - * computed. - */ - next = cmd->ls_next; + remaining_chars = line->size - index; - if ((next == NULL) - || (strncmp(cmd->name, next->name, size) != 0)) { - *sizep = strlen(cmd->name); - return 0; + if (remaining_chars != 0) { + memmove(&line->str[index + 1], &line->str[index], remaining_chars); } - /* - * When computing the common length, all the commands that can match - * must be evaluated. Considering the current command is the first - * that can match, the only other variable missing is the last - * command that can match. - */ - while (next->ls_next != NULL) { - if (strncmp(cmd->name, next->ls_next->name, size) != 0) { - break; - } + line->str[index] = c; + line->size++; + line->str[line->size] = '\0'; + return 0; +} - next = next->ls_next; - } +static int +shell_line_erase(struct shell_line *line, size_t index) +{ + size_t remaining_chars; - if (size == 0) { - size = 1; + if (index >= line->size) { + return EINVAL; } - while ((cmd->name[size - 1] != '\0') - && (cmd->name[size - 1] == next->name[size - 1])) { - size++; + remaining_chars = line->size - index - 1; + + if (remaining_chars != 0) { + memmove(&line->str[index], &line->str[index + 1], remaining_chars); } - size--; - *sizep = size; - return EAGAIN; + line->size--; + line->str[line->size] = '\0'; + return 0; +} + +static struct shell_line * +shell_history_get(struct shell_history *history, size_t index) +{ + return &history->lines[index % ARRAY_SIZE(history->lines)]; } -/* - * Print a list of commands eligible for completion, starting at the - * given command. Other eligible commands share the same prefix, as - * defined by the size argument. - * - * The global lock must be acquired before calling this function. - */ static void -shell_cmd_print_matches(const struct shell_cmd *cmd, unsigned long size) +shell_history_init(struct shell_history *history) { - const struct shell_cmd *tmp; - unsigned int i; + for (size_t i = 0; i < ARRAY_SIZE(history->lines); i++) { + shell_line_reset(shell_history_get(history, i)); + } - printf("\n"); + history->newest = 0; + history->oldest = 0; + history->index = 0; +} - for (tmp = cmd, i = 1; tmp != NULL; tmp = tmp->ls_next, i++) { - if (strncmp(cmd->name, tmp->name, size) != 0) { - break; - } +static struct shell_line * +shell_history_get_newest(struct shell_history *history) +{ + return shell_history_get(history, history->newest); +} - printf("%" SHELL_COMPLETION_MATCH_FMT, tmp->name); +static struct shell_line * +shell_history_get_index(struct shell_history *history) +{ + return shell_history_get(history, history->index); +} - if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) == 0) { - printf("\n"); - } +static void +shell_history_reset_index(struct shell_history *history) +{ + history->index = history->newest; +} + +static int +shell_history_same_newest(struct shell_history *history) +{ + return (history->newest != history->oldest) + && (shell_line_cmp(shell_history_get_newest(history), + shell_history_get(history, history->newest - 1)) + == 0); +} + +static void +shell_history_push(struct shell_history *history) +{ + if ((shell_line_size(shell_history_get_newest(history)) == 0) + || shell_history_same_newest(history)) { + shell_history_reset_index(history); + return; } - if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) != 1) { - printf("\n"); + history->newest++; + shell_history_reset_index(history); + + /* Mind integer overflows */ + if ((history->newest - history->oldest) >= ARRAY_SIZE(history->lines)) { + history->oldest = history->newest - ARRAY_SIZE(history->lines) + 1; } } -static int -shell_cmd_check_char(char c) +static void +shell_history_back(struct shell_history *history) { - if (((c >= 'a') && (c <= 'z')) - || ((c >= 'A') && (c <= 'Z')) - || ((c >= '0') && (c <= '9')) - || (c == '-') - || (c == '_')) { - return 0; + if (history->index == history->oldest) { + return; } - return EINVAL; + history->index--; + shell_line_copy(shell_history_get_newest(history), + shell_history_get_index(history)); } -static int -shell_cmd_check(const struct shell_cmd *cmd) +static void +shell_history_forward(struct shell_history *history) { - unsigned long i; - int error; + if (history->index == history->newest) { + return; + } - for (i = 0; cmd->name[i] != '\0'; i++) { - error = shell_cmd_check_char(cmd->name[i]); + history->index++; - if (error) { - return error; - } + if (history->index == history->newest) { + shell_line_reset(shell_history_get_newest(history)); + } else { + shell_line_copy(shell_history_get_newest(history), + shell_history_get_index(history)); } +} - if (i == 0) { - return EINVAL; +static void +shell_history_print(struct shell_history *history, struct shell *shell) +{ + const struct shell_line *line; + + /* Mind integer overflows */ + for (size_t i = history->oldest; i != history->newest; i++) { + line = shell_history_get(history, i); + shell_printf(shell, "%6lu %s\n", + (unsigned long)(i - history->oldest), + shell_line_str(line)); } +} - return 0; +static void +shell_cmd_set_lock(struct shell_cmd_set *cmd_set) +{ + mutex_lock(&cmd_set->lock); } -/* - * The global lock must be acquired before calling this function. - */ static void -shell_cmd_add_list(struct shell_cmd *cmd) +shell_cmd_set_unlock(struct shell_cmd_set *cmd_set) { - struct shell_cmd *prev, *next; + mutex_unlock(&cmd_set->lock); +} - prev = shell_list; +static struct shell_bucket * +shell_cmd_set_get_bucket(struct shell_cmd_set *cmd_set, const char *name) +{ + size_t index; - if ((prev == NULL) - || (strcmp(cmd->name, prev->name) < 0)) { - shell_list = cmd; - cmd->ls_next = prev; - return; - } + index = hash_str(name, SHELL_HTABLE_BITS); + assert(index < ARRAY_SIZE(cmd_set->htable)); + return &cmd_set->htable[index]; +} - for (;;) { - next = prev->ls_next; +static const struct shell_cmd * +shell_cmd_set_lookup(struct shell_cmd_set *cmd_set, const char *name) +{ + const struct shell_bucket *bucket; + const struct shell_cmd *cmd; - if ((next == NULL) - || (strcmp(cmd->name, next->name) < 0)) { + shell_cmd_set_lock(cmd_set); + + bucket = shell_cmd_set_get_bucket(cmd_set, name); + + for (cmd = bucket->cmd; cmd != NULL; cmd = cmd->ht_next) { + if (strcmp(cmd->name, name) == 0) { break; } - - prev = next; } - prev->ls_next = cmd; - cmd->ls_next = next; + shell_cmd_set_unlock(cmd_set); + + return cmd; } /* + * Look up the first command that matches a given string. + * + * The input string is defined by the given string pointer and size. + * * The global lock must be acquired before calling this function. */ -static int -shell_cmd_add(struct shell_cmd *cmd) +static const struct shell_cmd * +shell_cmd_set_match(const struct shell_cmd_set *cmd_set, const char *str, + size_t size) { - struct shell_bucket *bucket; - struct shell_cmd *tmp; + const struct shell_cmd *cmd; - bucket = shell_bucket_get(cmd->name); - tmp = bucket->cmd; + cmd = cmd_set->cmd_list; - if (tmp == NULL) { - bucket->cmd = cmd; - goto out; + while (cmd != NULL) { + if (strncmp(cmd->name, str, size) == 0) { + return cmd; + } + + cmd = cmd->ls_next; } - for (;;) { - if (strcmp(cmd->name, tmp->name) == 0) { - printf("shell: error: %s: shell command name collision", cmd->name); - return EEXIST; - } + return NULL; +} - if (tmp->ht_next == NULL) { - break; - } +/* + * Attempt command auto-completion. + * + * The given string is the beginning of a command, or the empty string. + * The sizep parameter initially points to the size of the given string. + * If the string matches any registered command, the cmdp pointer is + * updated to point to the first matching command in the sorted list of + * commands, and sizep is updated to the number of characters in the + * command name that are common in subsequent commands. The command + * pointer and the returned size can be used to print a list of commands + * eligible for completion. + * + * If there is a single match for the given string, return 0. If there + * are more than one match, return EAGAIN. If there is no match, + * return EINVAL. + */ +static int +shell_cmd_set_complete(struct shell_cmd_set *cmd_set, const char *str, + size_t *sizep, const struct shell_cmd **cmdp) +{ + const struct shell_cmd *cmd, *next; + size_t size; - tmp = tmp->ht_next; - } + size = *sizep; - tmp->ht_next = cmd; + /* + * Start with looking up a command that matches the given argument. + * If there is no match, return an error. + */ + cmd = shell_cmd_set_match(cmd_set, str, size); -out: - shell_cmd_add_list(cmd); - return 0; -} + if (cmd == NULL) { + return EINVAL; + } -int -shell_cmd_register(struct shell_cmd *cmd) -{ - int error; + *cmdp = cmd; - error = shell_cmd_check(cmd); + /* + * If at least one command matches, try to complete it. + * There can be two cases : + * 1/ There is one and only one match, which is directly returned. + * 2/ There are several matches, in which case the common length is + * computed. + */ + next = cmd->ls_next; - if (error) { - return error; + if ((next == NULL) || (strncmp(cmd->name, next->name, size) != 0)) { + *sizep = strlen(cmd->name); + return 0; } - shell_cmd_acquire(); - error = shell_cmd_add(cmd); - shell_cmd_release(); + /* + * When computing the common length, all the commands that can match + * must be evaluated. Considering the current command is the first + * that can match, the only other variable missing is the last + * command that can match. + */ + while (next->ls_next != NULL) { + if (strncmp(cmd->name, next->ls_next->name, size) != 0) { + break; + } - return error; -} + next = next->ls_next; + } -static inline const char * -shell_line_str(const struct shell_line *line) -{ - return line->str; -} + if (size == 0) { + size = 1; + } -static inline unsigned long -shell_line_size(const struct shell_line *line) -{ - return line->size; -} + while ((cmd->name[size - 1] != '\0') + && (cmd->name[size - 1] == next->name[size - 1])) { + size++; + } -static inline void -shell_line_reset(struct shell_line *line) -{ - line->str[0] = '\0'; - line->size = 0; + size--; + *sizep = size; + return EAGAIN; } -static inline void -shell_line_copy(struct shell_line *dest, const struct shell_line *src) +struct shell_cmd_set * +shell_get_cmd_set(struct shell *shell) { - strcpy(dest->str, src->str); - dest->size = src->size; + return shell->cmd_set; } -static inline int -shell_line_cmp(const struct shell_line *a, const struct shell_line *b) +static struct shell_history * +shell_get_history(struct shell *shell) { - return strcmp(a->str, b->str); + return &shell->history; } -static int -shell_line_insert(struct shell_line *line, unsigned long index, char c) +static void +shell_cb_help(struct shell *shell, int argc, char *argv[]) { - unsigned long remaining_chars; + struct shell_cmd_set *cmd_set; + const struct shell_cmd *cmd; - if (index > line->size) { - return EINVAL; - } + cmd_set = shell_get_cmd_set(shell); - if ((line->size + 1) == sizeof(line->str)) { - return ENOMEM; + if (argc > 2) { + argc = 2; + argv[1] = "help"; } - remaining_chars = line->size - index; + if (argc == 2) { + cmd = shell_cmd_set_lookup(cmd_set, argv[1]); - if (remaining_chars != 0) { - memmove(&line->str[index + 1], &line->str[index], remaining_chars); - } + if (cmd == NULL) { + shell_printf(shell, "shell: help: %s: command not found\n", + argv[1]); + return; + } - line->str[index] = c; - line->size++; - line->str[line->size] = '\0'; - return 0; -} + shell_printf(shell, "usage: %s\n%s\n", cmd->usage, cmd->short_desc); -static int -shell_line_erase(struct shell_line *line, unsigned long index) -{ - unsigned long remaining_chars; + if (cmd->long_desc != NULL) { + shell_printf(shell, "\n%s\n", cmd->long_desc); + } - if (index >= line->size) { - return EINVAL; + return; } - remaining_chars = line->size - index - 1; + shell_cmd_set_lock(cmd_set); - if (remaining_chars != 0) { - memmove(&line->str[index], &line->str[index + 1], remaining_chars); + for (cmd = cmd_set->cmd_list; cmd != NULL; cmd = cmd->ls_next) { + shell_printf(shell, "%13s %s\n", cmd->name, cmd->short_desc); } - line->size--; - line->str[line->size] = '\0'; - return 0; -} - -static struct shell_line * -shell_history_get(unsigned long index) -{ - return &shell_history[index % ARRAY_SIZE(shell_history)]; + shell_cmd_set_unlock(cmd_set); } -static struct shell_line * -shell_history_get_newest(void) +static void +shell_cb_history(struct shell *shell, int argc, char *argv[]) { - return shell_history_get(shell_history_newest); -} + (void)argc; + (void)argv; -static struct shell_line * -shell_history_get_index(void) -{ - return shell_history_get(shell_history_index); + shell_history_print(shell_get_history(shell), shell); } -static void -shell_history_reset_index(void) -{ - shell_history_index = shell_history_newest; -} +static struct shell_cmd shell_default_cmds[] = { + SHELL_CMD_INITIALIZER("help", shell_cb_help, + "help [command]", + "obtain help about shell commands"), + SHELL_CMD_INITIALIZER("history", shell_cb_history, + "history", + "display history list"), +}; -static inline int -shell_history_same_newest(void) +void +shell_cmd_set_init(struct shell_cmd_set *cmd_set) { - return (shell_history_newest != shell_history_oldest) - && shell_line_cmp(shell_history_get_newest(), - shell_history_get(shell_history_newest - 1)) == 0; + mutex_init(&cmd_set->lock); + memset(cmd_set->htable, 0, sizeof(cmd_set->htable)); + cmd_set->cmd_list = NULL; + SHELL_REGISTER_CMDS(shell_default_cmds, cmd_set); } -static void -shell_history_push(void) +static int +shell_cmd_set_add_htable(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd) { - if ((shell_line_size(shell_history_get_newest()) == 0) - || shell_history_same_newest()) { - shell_history_reset_index(); - return; - } + struct shell_bucket *bucket; + struct shell_cmd *tmp; - shell_history_newest++; - shell_history_reset_index(); + bucket = shell_cmd_set_get_bucket(cmd_set, cmd->name); + tmp = bucket->cmd; - /* Mind integer overflows */ - if ((shell_history_newest - shell_history_oldest) - >= ARRAY_SIZE(shell_history)) { - shell_history_oldest = shell_history_newest - - ARRAY_SIZE(shell_history) + 1; + if (tmp == NULL) { + bucket->cmd = cmd; + return 0; } -} -static void -shell_history_back(void) -{ - if (shell_history_index == shell_history_oldest) { - return; - } + for (;;) { + if (strcmp(cmd->name, tmp->name) == 0) { + return EEXIST; + } - shell_history_index--; - shell_line_copy(shell_history_get_newest(), shell_history_get_index()); -} + if (tmp->ht_next == NULL) { + break; + } -static void -shell_history_forward(void) -{ - if (shell_history_index == shell_history_newest) { - return; + tmp = tmp->ht_next; } - shell_history_index++; - - if (shell_history_index == shell_history_newest) { - shell_line_reset(shell_history_get_newest()); - } else { - shell_line_copy(shell_history_get_newest(), shell_history_get_index()); - } + tmp->ht_next = cmd; + return 0; } static void -shell_cmd_help(int argc, char *argv[]) +shell_cmd_set_add_list(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd) { - const struct shell_cmd *cmd; + struct shell_cmd *prev, *next; - if (argc > 2) { - argc = 2; - argv[1] = "help"; + prev = cmd_set->cmd_list; + + if ((prev == NULL) || (strcmp(cmd->name, prev->name) < 0)) { + cmd_set->cmd_list = cmd; + cmd->ls_next = prev; + return; } - if (argc == 2) { - cmd = shell_cmd_lookup(argv[1]); + for (;;) { + next = prev->ls_next; - if (cmd == NULL) { - printf("shell: help: %s: command not found\n", argv[1]); - return; + if ((next == NULL) || (strcmp(cmd->name, next->name) < 0)) { + break; } - printf("usage: %s\n%s\n", cmd->usage, cmd->short_desc); + prev = next; + } - if (cmd->long_desc != NULL) { - printf("\n%s\n", cmd->long_desc); - } + prev->ls_next = cmd; + cmd->ls_next = next; +} - return; - } +static int +shell_cmd_set_add(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd) +{ + int error; - shell_cmd_acquire(); + error = shell_cmd_set_add_htable(cmd_set, cmd); - for (cmd = shell_list; cmd != NULL; cmd = cmd->ls_next) { - printf("%13s %s\n", cmd->name, cmd->short_desc); + if (error) { + return error; } - shell_cmd_release(); + shell_cmd_set_add_list(cmd_set, cmd); + return 0; } -static void -shell_cmd_history(int argc, char *argv[]) +int +shell_cmd_set_register(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd) { - unsigned long i; + int error; - (void)argc; - (void)argv; + error = shell_cmd_check(cmd); - /* Mind integer overflows */ - for (i = shell_history_oldest; i != shell_history_newest; i++) { - printf("%6lu %s\n", i - shell_history_oldest, - shell_line_str(shell_history_get(i))); + if (error) { + return error; } + + shell_cmd_set_lock(cmd_set); + error = shell_cmd_set_add(cmd_set, cmd); + shell_cmd_set_unlock(cmd_set); + + return error; } -static struct shell_cmd shell_default_cmds[] = { - SHELL_CMD_INITIALIZER("help", shell_cmd_help, - "help [command]", - "obtain help about shell commands"), - SHELL_CMD_INITIALIZER("history", shell_cmd_history, - "history", - "display history list"), -}; +void +shell_init(struct shell *shell, struct shell_cmd_set *cmd_set, + shell_getc_fn_t getc_fn, shell_vfprintf_fn_t vfprintf_fn, + void *io_object) +{ + shell->cmd_set = cmd_set; + shell->getc_fn = getc_fn; + shell->vfprintf_fn = vfprintf_fn; + shell->io_object = io_object; + shell_history_init(&shell->history); + shell->esc_seq_index = 0; +} static void -shell_prompt(void) +shell_prompt(struct shell *shell) { - printf("shell> "); + shell_printf(shell, "shell> "); } static void -shell_reset(void) +shell_reset(struct shell *shell) { - shell_line_reset(shell_history_get_newest()); - shell_cursor = 0; - shell_prompt(); + shell_line_reset(shell_history_get_newest(&shell->history)); + shell->cursor = 0; + shell_prompt(shell); } static void -shell_erase(void) +shell_erase(struct shell *shell) { struct shell_line *current_line; - unsigned long remaining_chars; + size_t remaining_chars; - current_line = shell_history_get_newest(); + current_line = shell_history_get_newest(&shell->history); remaining_chars = shell_line_size(current_line); - while (shell_cursor != remaining_chars) { - putchar(' '); - shell_cursor++; + while (shell->cursor != remaining_chars) { + shell_printf(shell, " "); + shell->cursor++; } while (remaining_chars != 0) { - printf("\b \b"); + shell_printf(shell, "\b \b"); remaining_chars--; } - shell_cursor = 0; + shell->cursor = 0; } static void -shell_restore(void) +shell_restore(struct shell *shell) { struct shell_line *current_line; - current_line = shell_history_get_newest(); - printf("%s", shell_line_str(current_line)); - shell_cursor = shell_line_size(current_line); + current_line = shell_history_get_newest(&shell->history); + shell_printf(shell, "%s", shell_line_str(current_line)); + shell->cursor = shell_line_size(current_line); } static int @@ -763,87 +709,91 @@ shell_is_ctrl_char(char c) } static void -shell_process_left(void) +shell_process_left(struct shell *shell) { - if (shell_cursor == 0) { + if (shell->cursor == 0) { return; } - shell_cursor--; - printf("\e[1D"); + shell->cursor--; + shell_printf(shell, "\e[1D"); } static int -shell_process_right(void) +shell_process_right(struct shell *shell) { - if (shell_cursor >= shell_line_size(shell_history_get_newest())) { + size_t size; + + size = shell_line_size(shell_history_get_newest(&shell->history)); + + if (shell->cursor >= size) { return EAGAIN; } - shell_cursor++; - printf("\e[1C"); + shell->cursor++; + shell_printf(shell, "\e[1C"); return 0; } static void -shell_process_up(void) +shell_process_up(struct shell *shell) { - shell_erase(); - shell_history_back(); - shell_restore(); + shell_erase(shell); + shell_history_back(&shell->history); + shell_restore(shell); } static void -shell_process_down(void) +shell_process_down(struct shell *shell) { - shell_erase(); - shell_history_forward(); - shell_restore(); + shell_erase(shell); + shell_history_forward(&shell->history); + shell_restore(shell); } static void -shell_process_backspace(void) +shell_process_backspace(struct shell *shell) { struct shell_line *current_line; - unsigned long remaining_chars; + size_t remaining_chars; int error; - current_line = shell_history_get_newest(); - error = shell_line_erase(current_line, shell_cursor - 1); + current_line = shell_history_get_newest(&shell->history); + error = shell_line_erase(current_line, shell->cursor - 1); if (error) { return; } - shell_cursor--; - printf("\b%s ", shell_line_str(current_line) + shell_cursor); - remaining_chars = shell_line_size(current_line) - shell_cursor + 1; + shell->cursor--; + shell_printf(shell, "\b%s ", shell_line_str(current_line) + shell->cursor); + remaining_chars = shell_line_size(current_line) - shell->cursor + 1; while (remaining_chars != 0) { - putchar('\b'); + shell_printf(shell, "\b"); remaining_chars--; } } static int -shell_process_raw_char(char c) +shell_process_raw_char(struct shell *shell, char c) { struct shell_line *current_line; - unsigned long remaining_chars; + size_t remaining_chars; int error; - current_line = shell_history_get_newest(); - error = shell_line_insert(current_line, shell_cursor, c); + current_line = shell_history_get_newest(&shell->history); + error = shell_line_insert(current_line, shell->cursor, c); if (error) { - printf("\nshell: line too long\n"); + shell_printf(shell, "\nshell: line too long\n"); return error; } - shell_cursor++; + shell->cursor++; - if (shell_cursor == shell_line_size(current_line)) { - putchar(c); + if (shell->cursor == shell_line_size(current_line)) { + shell_printf(shell, "%c", c); goto out; } @@ -851,11 +801,11 @@ shell_process_raw_char(char c) * This assumes that the backspace character only moves the cursor * without erasing characters. */ - printf("%s", shell_line_str(current_line) + shell_cursor - 1); - remaining_chars = shell_line_size(current_line) - shell_cursor; + shell_printf(shell, "%s", shell_line_str(current_line) + shell->cursor - 1); + remaining_chars = shell_line_size(current_line) - shell->cursor; while (remaining_chars != 0) { - putchar('\b'); + shell_printf(shell, "\b"); remaining_chars--; } @@ -863,22 +813,58 @@ out: return 0; } +/* + * Print a list of commands eligible for completion, starting at the + * given command. Other eligible commands share the same prefix, as + * defined by the size argument. + * + * The global lock must be acquired before calling this function. + */ +static void +shell_print_cmd_matches(struct shell *shell, const struct shell_cmd *cmd, + unsigned long size) +{ + const struct shell_cmd *tmp; + unsigned int i; + + shell_printf(shell, "\n"); + + for (tmp = cmd, i = 1; tmp != NULL; tmp = tmp->ls_next, i++) { + if (strncmp(cmd->name, tmp->name, size) != 0) { + break; + } + + shell_printf(shell, "%" SHELL_COMPLETION_MATCH_FMT, tmp->name); + + if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) == 0) { + shell_printf(shell, "\n"); + } + } + + if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) != 1) { + shell_printf(shell, "\n"); + } +} + static int -shell_process_tabulation(void) +shell_process_tabulation(struct shell *shell) { + struct shell_cmd_set *cmd_set; const struct shell_cmd *cmd = NULL; /* GCC */ const char *name, *str, *word; - unsigned long i, size, cmd_cursor; + size_t size, cmd_cursor; int error; - shell_cmd_acquire(); + cmd_set = shell->cmd_set; - str = shell_line_str(shell_history_get_newest()); + shell_cmd_set_lock(cmd_set); + + str = shell_line_str(shell_history_get_newest(&shell->history)); word = shell_find_word(str); - size = shell_cursor - (word - str); - cmd_cursor = shell_cursor - size; + size = shell->cursor - (word - str); + cmd_cursor = shell->cursor - size; - error = shell_cmd_complete(word, &size, &cmd); + error = shell_cmd_set_complete(cmd_set, word, &size, &cmd); if (error && (error != EAGAIN)) { error = 0; @@ -886,27 +872,27 @@ shell_process_tabulation(void) } if (error == EAGAIN) { - unsigned long cursor; + size_t cursor; - cursor = shell_cursor; - shell_cmd_print_matches(cmd, size); - shell_prompt(); - shell_restore(); + cursor = shell->cursor; + shell_print_cmd_matches(shell, cmd, size); + shell_prompt(shell); + shell_restore(shell); /* Keep existing arguments as they are */ - while (shell_cursor != cursor) { - shell_process_left(); + while (shell->cursor != cursor) { + shell_process_left(shell); } } name = shell_cmd_name(cmd); - while (shell_cursor != cmd_cursor) { - shell_process_backspace(); + while (shell->cursor != cmd_cursor) { + shell_process_backspace(shell); } - for (i = 0; i < size; i++) { - error = shell_process_raw_char(name[i]); + for (size_t i = 0; i < size; i++) { + error = shell_process_raw_char(shell, name[i]); if (error) { goto out; @@ -916,65 +902,65 @@ shell_process_tabulation(void) error = 0; out: - shell_cmd_release(); + shell_cmd_set_unlock(cmd_set); return error; } static void -shell_esc_seq_up(void) +shell_esc_seq_up(struct shell *shell) { - shell_process_up(); + shell_process_up(shell); } static void -shell_esc_seq_down(void) +shell_esc_seq_down(struct shell *shell) { - shell_process_down(); + shell_process_down(shell); } static void -shell_esc_seq_next(void) +shell_esc_seq_next(struct shell *shell) { - shell_process_right(); + shell_process_right(shell); } static void -shell_esc_seq_prev(void) +shell_esc_seq_prev(struct shell *shell) { - shell_process_left(); + shell_process_left(shell); } static void -shell_esc_seq_home(void) +shell_esc_seq_home(struct shell *shell) { - while (shell_cursor != 0) { - shell_process_left(); + while (shell->cursor != 0) { + shell_process_left(shell); } } static void -shell_esc_seq_del(void) +shell_esc_seq_del(struct shell *shell) { int error; - error = shell_process_right(); + error = shell_process_right(shell); if (error) { return; } - shell_process_backspace(); + shell_process_backspace(shell); } static void -shell_esc_seq_end(void) +shell_esc_seq_end(struct shell *shell) { - unsigned long size; + size_t size; - size = shell_line_size(shell_history_get_newest()); + size = shell_line_size(shell_history_get_newest(&shell->history)); - while (shell_cursor < size) { - shell_process_right(); + while (shell->cursor < size) { + shell_process_right(shell); } } @@ -993,9 +979,7 @@ static const struct shell_esc_seq shell_esc_seqs[] = { static const struct shell_esc_seq * shell_esc_seq_lookup(const char *str) { - unsigned long i; - - for (i = 0; i < ARRAY_SIZE(shell_esc_seqs); i++) { + for (size_t i = 0; i < ARRAY_SIZE(shell_esc_seqs); i++) { if (strcmp(shell_esc_seqs[i].str, str) == 0) { return &shell_esc_seqs[i]; } @@ -1010,29 +994,24 @@ shell_esc_seq_lookup(const char *str) * Return the next escape state or 0 if the sequence is complete. */ static int -shell_process_esc_sequence(char c) +shell_process_esc_sequence(struct shell *shell, char c) { - static char str[SHELL_ESC_SEQ_MAX_SIZE], *ptr = str; - const struct shell_esc_seq *seq; - uintptr_t index; - - index = ptr - str; - if (index >= (ARRAY_SIZE(str) - 1)) { - printf("shell: escape sequence too long\n"); + if (shell->esc_seq_index >= (ARRAY_SIZE(shell->esc_seq) - 1)) { + shell_printf(shell, "shell: escape sequence too long\n"); goto reset; } - *ptr = c; - ptr++; - *ptr = '\0'; + shell->esc_seq[shell->esc_seq_index] = c; + shell->esc_seq_index++; + shell->esc_seq[shell->esc_seq_index] = '\0'; if ((c >= '@') && (c <= '~')) { - seq = shell_esc_seq_lookup(str); + seq = shell_esc_seq_lookup(shell->esc_seq); if (seq != NULL) { - seq->fn(); + seq->fn(shell); } goto reset; @@ -1041,75 +1020,75 @@ shell_process_esc_sequence(char c) return SHELL_ESC_STATE_CSI; reset: - ptr = str; + shell->esc_seq_index = 0; return 0; } static int -shell_process_args(void) +shell_process_args(struct shell *shell) { - unsigned long i; char c, prev; + size_t i; int j; - snprintf(shell_tmp_line, sizeof(shell_tmp_line), "%s", - shell_line_str(shell_history_get_newest())); + snprintf(shell->tmp_line, sizeof(shell->tmp_line), "%s", + shell_line_str(shell_history_get_newest(&shell->history))); for (i = 0, j = 0, prev = SHELL_SEPARATOR; - (c = shell_tmp_line[i]) != '\0'; + (c = shell->tmp_line[i]) != '\0'; i++, prev = c) { if (c == SHELL_SEPARATOR) { if (prev != SHELL_SEPARATOR) { - shell_tmp_line[i] = '\0'; + shell->tmp_line[i] = '\0'; } } else { if (prev == SHELL_SEPARATOR) { - shell_argv[j] = &shell_tmp_line[i]; + shell->argv[j] = &shell->tmp_line[i]; j++; - if (j == ARRAY_SIZE(shell_argv)) { - printf("shell: too many arguments\n"); + if (j == ARRAY_SIZE(shell->argv)) { + shell_printf(shell, "shell: too many arguments\n"); return EINVAL; } - shell_argv[j] = NULL; + shell->argv[j] = NULL; } } } - shell_argc = j; + shell->argc = j; return 0; } static void -shell_process_line(void) +shell_process_line(struct shell *shell) { const struct shell_cmd *cmd; int error; cmd = NULL; - error = shell_process_args(); + error = shell_process_args(shell); if (error) { goto out; } - if (shell_argc == 0) { + if (shell->argc == 0) { goto out; } - cmd = shell_cmd_lookup(shell_argv[0]); + cmd = shell_cmd_set_lookup(shell->cmd_set, shell->argv[0]); if (cmd == NULL) { - printf("shell: %s: command not found\n", shell_argv[0]); + shell_printf(shell, "shell: %s: command not found\n", shell->argv[0]); goto out; } out: - shell_history_push(); + shell_history_push(&shell->history); if (cmd != NULL) { - cmd->fn(shell_argc, shell_argv); + cmd->fn(shell, shell->argc, shell->argv); } } @@ -1119,19 +1098,19 @@ out: * Return an error if the caller should reset the current line state. */ static int -shell_process_ctrl_char(char c) +shell_process_ctrl_char(struct shell *shell, char c) { switch (c) { case SHELL_ERASE_BS: case SHELL_ERASE_DEL: - shell_process_backspace(); + shell_process_backspace(shell); break; case '\t': - return shell_process_tabulation(); + return shell_process_tabulation(shell); case '\n': case '\r': - putchar('\n'); - shell_process_line(); + shell_printf(shell, "\n"); + shell_process_line(shell); return EAGAIN; default: return 0; @@ -1140,19 +1119,17 @@ shell_process_ctrl_char(char c) return 0; } -static void -shell_run(void *arg) +void +shell_run(struct shell *shell) { int c, error, escape; - (void)arg; - for (;;) { - shell_reset(); + shell_reset(shell); escape = 0; for (;;) { - c = getchar(); + c = shell->getc_fn(shell->io_object); if (escape) { switch (escape) { @@ -1166,7 +1143,7 @@ shell_run(void *arg) break; case SHELL_ESC_STATE_CSI: - escape = shell_process_esc_sequence(c); + escape = shell_process_esc_sequence(shell, c); break; default: escape = 0; @@ -1178,14 +1155,14 @@ shell_run(void *arg) escape = SHELL_ESC_STATE_START; error = 0; } else { - error = shell_process_ctrl_char(c); + error = shell_process_ctrl_char(shell, c); if (error) { break; } } } else { - error = shell_process_raw_char(c); + error = shell_process_raw_char(shell, c); } if (error) { @@ -1196,17 +1173,17 @@ shell_run(void *arg) } void -shell_setup(void) +shell_printf(struct shell *shell, const char *format, ...) { - int error; + va_list ap; - mutex_init(&shell_lock); - SHELL_REGISTER_CMDS(shell_default_cmds); - - error = thread_create(NULL, shell_run, NULL, "shell", - SHELL_STACK_SIZE, THREAD_MIN_PRIORITY); + va_start(ap, format); + shell_vprintf(shell, format, ap); + va_end(ap); +} - if (error) { - panic("shell: unable to create shell thread"); - } +void +shell_vprintf(struct shell *shell, const char *format, va_list ap) +{ + shell->vfprintf_fn(shell->io_object, format, ap); } diff --git a/lib/shell.h b/lib/shell.h index 0ba0edc..fccd5ca 100644 --- a/lib/shell.h +++ b/lib/shell.h @@ -29,39 +29,56 @@ #ifndef SHELL_H #define SHELL_H -#include +#include #include +#include +#include #include #include -#include - -#define SHELL_REGISTER_CMDS(cmds) \ -MACRO_BEGIN \ - size_t i___; \ - int error___; \ - \ - for (i___ = 0; i___ < ARRAY_SIZE(cmds); i___++) { \ - error___ = shell_cmd_register(&(cmds)[i___]); \ - \ - if (error___) { \ - panic("%s: %s", __func__, strerror(error___)); \ - } \ - } \ -MACRO_END +/* + * Types for I/O functions. + */ +typedef int (*shell_getc_fn_t)(void *io_object); +typedef void (*shell_vfprintf_fn_t)(void *io_object, + const char *format, va_list ap); -typedef void (*shell_fn_t)(int argc, char *argv[]); +/* + * Shell structure, statically allocatable. + */ +struct shell; -struct shell_cmd { - struct shell_cmd *ht_next; - struct shell_cmd *ls_next; - const char *name; - shell_fn_t fn; - const char *usage; - const char *short_desc; - const char *long_desc; -}; +/* + * Shell command structure. + */ +struct shell_cmd; + +/* + * Command container, shareable across multiple shell instances. + */ +struct shell_cmd_set; + +/* + * Type for command implementation callbacks. + */ +typedef void (*shell_fn_t)(struct shell *shell, int argc, char **argv); + +#include "shell_i.h" + +#define SHELL_REGISTER_CMDS(cmds, cmd_set) \ +MACRO_BEGIN \ + size_t i_; \ + int error_; \ + \ + for (i_ = 0; i_ < ARRAY_SIZE(cmds); i_++) { \ + error_ = shell_cmd_set_register(cmd_set, &(cmds)[i_]); \ + \ + if (error_) { \ + panic("%s: %s\n", __func__, strerror(error_)); \ + } \ + } \ +MACRO_END /* * Static shell command initializers. @@ -79,11 +96,9 @@ void shell_cmd_init(struct shell_cmd *cmd, const char *name, const char *short_desc, const char *long_desc); /* - * Initialize the shell module. - * - * On return, shell commands can be registered. + * Initialize a command set. */ -void shell_setup(void); +void shell_cmd_set_init(struct shell_cmd_set *cmd_set); /* * Register a shell command. @@ -91,9 +106,41 @@ void shell_setup(void); * The command name must be unique. It must not include characters outside * the [a-zA-Z0-9-_] class. * - * The structure passed when calling this function is directly reused by - * the shell module and must persist in memory. + * Commands may safely be registered while the command set is used. + * + * The command structure must persist in memory as long as the command set + * is used. + */ +int shell_cmd_set_register(struct shell_cmd_set *cmd_set, + struct shell_cmd *cmd); + +/* + * Initialize a shell instance. + * + * On return, shell commands can be registered. + */ +void shell_init(struct shell *shell, struct shell_cmd_set *cmd_set, + shell_getc_fn_t getc_fn, shell_vfprintf_fn_t vfprintf_fn, + void *io_object); + +/* + * Run the shell. + * + * This function doesn't return. + */ +void shell_run(struct shell *shell); + +/* + * Obtain the command set associated with a shell. + */ +struct shell_cmd_set * shell_get_cmd_set(struct shell *shell); + +/* + * Printf-like functions specific to the given shell instance. */ -int shell_cmd_register(struct shell_cmd *cmd); +void shell_printf(struct shell *shell, const char *format, ...) + __attribute__((format(printf, 2, 3))); +void shell_vprintf(struct shell *shell, const char *format, va_list ap) + __attribute__((format(printf, 2, 0))); #endif /* SHELL_H */ diff --git a/lib/shell_i.h b/lib/shell_i.h new file mode 100644 index 0000000..71cefbf --- /dev/null +++ b/lib/shell_i.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 Richard Braun. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Upstream site with license notes : + * http://git.sceen.net/rbraun/librbraun.git/ + */ + +#ifndef SHELL_I_H +#define SHELL_I_H + +#include + +#include + +#include + +struct shell_cmd { + struct shell_cmd *ht_next; + struct shell_cmd *ls_next; + const char *name; + shell_fn_t fn; + const char *usage; + const char *short_desc; + const char *long_desc; +}; + +struct shell_bucket { + struct shell_cmd *cmd; +}; + +/* + * Binary exponent and size of the hash table used to store commands. + */ +#define SHELL_HTABLE_BITS 6 +#define SHELL_HTABLE_SIZE (1 << SHELL_HTABLE_BITS) + +/* + * The command list is sorted. + */ +struct shell_cmd_set { + struct mutex lock; + struct shell_bucket htable[SHELL_HTABLE_SIZE]; + struct shell_cmd *cmd_list; +}; + +#define SHELL_LINE_MAX_SIZE 64 + +/* + * Line containing a shell entry. + * + * The string must be nul-terminated. The size doesn't include this + * additional nul character, the same way strlen() doesn't account for it. + */ +struct shell_line { + char str[SHELL_LINE_MAX_SIZE]; + size_t size; +}; + +/* + * Number of entries in the history. + * + * One of these entryes is used as the current line. + */ +#define SHELL_HISTORY_SIZE 21 + +#if SHELL_HISTORY_SIZE == 0 +#error "shell history size must be non-zero" +#endif /* SHELL_HISTORY_SIZE == 0 */ + +/* + * Shell history. + * + * The history is never empty. There is always at least one entry, the + * current line, referenced by the newest (most recent) index. The array + * is used like a circular buffer, i.e. old entries are implicitely + * erased by new ones. The index references the entry used as a template + * for the current line. + */ +struct shell_history { + struct shell_line lines[SHELL_HISTORY_SIZE]; + size_t newest; + size_t oldest; + size_t index; +}; + +/* + * This value changes depending on the standard used and was chosen arbitrarily. + */ +#define SHELL_ESC_SEQ_MAX_SIZE 8 + +#define SHELL_MAX_ARGS 16 + +/* + * Shell structure. + * + * A shell instance can include temporary variables to minimize stack usage. + */ +struct shell { + struct shell_cmd_set *cmd_set; + + shell_getc_fn_t getc_fn; + shell_vfprintf_fn_t vfprintf_fn; + void *io_object; + + struct shell_history history; + + /* Cursor within the current line */ + size_t cursor; + + /* Members used for escape sequence parsing */ + char esc_seq[SHELL_ESC_SEQ_MAX_SIZE]; + size_t esc_seq_index; + + /* + * Buffer used to store the current line during argument processing. + * + * The pointers in the argv array point inside this buffer. The + * separators immediately following the arguments are replaced with + * null characters. + */ + char tmp_line[SHELL_LINE_MAX_SIZE]; + + int argc; + char *argv[SHELL_MAX_ARGS]; +}; + +#endif /* SHELL_I_H */ diff --git a/src/main.c b/src/main.c index 839e273..4af5867 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Richard Braun. + * Copyright (c) 2017-2018 Richard Braun. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -20,7 +20,10 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include +#include +#include #include #include @@ -29,6 +32,7 @@ #include "cpu.h" #include "i8254.h" #include "i8259.h" +#include "main.h" #include "mem.h" #include "panic.h" #include "sw.h" @@ -36,12 +40,63 @@ #include "timer.h" #include "uart.h" +#define MAIN_SHELL_STACK_SIZE 4096 + /* * XXX The Clang compiler apparently doesn't like the lack of prototype for * the main function in free standing mode. */ void main(void); +static struct shell_cmd_set main_shell_cmd_set; +static struct shell main_shell; + +static int +main_getc(void *io_object) +{ + uint8_t byte; + int error; + + (void)io_object; + + error = uart_read(&byte); + + if (error) { + return EOF; + } + + return byte; +} + +static void +main_vfprintf(void *io_object, const char *format, va_list ap) +{ + (void)io_object; + vprintf(format, ap); +} + +static void +main_shell_run(void *arg) +{ + shell_run(arg); +} + +static void +main_setup_shell(void) +{ + int error; + + shell_cmd_set_init(&main_shell_cmd_set); + shell_init(&main_shell, &main_shell_cmd_set, + main_getc, main_vfprintf, NULL); + error = thread_create(NULL, main_shell_run, &main_shell, "shell", + MAIN_SHELL_STACK_SIZE, THREAD_MIN_PRIORITY); + + if (error) { + panic("main: unable to create shell thread"); + } +} + /* * This function is the main entry point for C code. It's called from * assembly code in the boot module, very soon after control is passed @@ -58,7 +113,7 @@ main(void) mem_setup(); thread_setup(); timer_setup(); - shell_setup(); + main_setup_shell(); sw_setup(); printf("X1 " QUOTE(VERSION) "\n\n"); @@ -67,3 +122,9 @@ main(void) /* Never reached */ } + +struct shell_cmd_set * +main_get_shell_cmd_set(void) +{ + return &main_shell_cmd_set; +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..62d342a --- /dev/null +++ b/src/main.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Richard Braun. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef MAIN_H +#define MAIN_H + +#include + +struct shell_cmd_set * main_get_shell_cmd_set(void); + +#endif /* MAIN_H */ diff --git a/src/sw.c b/src/sw.c index 4f74289..0cbb3bf 100644 --- a/src/sw.c +++ b/src/sw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Richard Braun. + * Copyright (c) 2017-2018 Richard Braun. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -33,6 +33,7 @@ #include #include "condvar.h" +#include "main.h" #include "mutex.h" #include "panic.h" #include "sw.h" @@ -198,8 +199,9 @@ out: } static void -sw_shell_start(int argc, char **argv) +sw_shell_start(struct shell *shell, int argc, char **argv) { + (void)shell; (void)argc; (void)argv; @@ -207,8 +209,9 @@ sw_shell_start(int argc, char **argv) } static void -sw_shell_stop(int argc, char **argv) +sw_shell_stop(struct shell *shell, int argc, char **argv) { + (void)shell; (void)argc; (void)argv; @@ -216,8 +219,9 @@ sw_shell_stop(int argc, char **argv) } static void -sw_shell_resume(int argc, char **argv) +sw_shell_resume(struct shell *shell, int argc, char **argv) { + (void)shell; (void)argc; (void)argv; @@ -225,8 +229,9 @@ sw_shell_resume(int argc, char **argv) } static void -sw_shell_read(int argc, char **argv) +sw_shell_read(struct shell *shell, int argc, char **argv) { + (void)shell; (void)argc; (void)argv; @@ -234,11 +239,13 @@ sw_shell_read(int argc, char **argv) } static void -sw_shell_wait(int argc, char **argv) +sw_shell_wait(struct shell *shell, int argc, char **argv) { unsigned long seconds; int ret; + (void)shell; + if (argc != 2) { goto error; } @@ -253,7 +260,7 @@ sw_shell_wait(int argc, char **argv) return; error: - printf("sw_wait: error: invalid arguments\n"); + shell_printf(shell, "sw_wait: error: invalid arguments\n"); } static struct shell_cmd sw_shell_cmds[] = { @@ -283,5 +290,5 @@ sw_setup(void) panic("sw: error: unable to create stopwatch"); } - SHELL_REGISTER_CMDS(sw_shell_cmds); + SHELL_REGISTER_CMDS(sw_shell_cmds, main_get_shell_cmd_set()); } -- cgit v1.2.3