summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2018-07-24 01:24:16 +0200
committerRichard Braun <rbraun@sceen.net>2018-07-24 01:24:16 +0200
commit4ae79cffba89c9f619dc65414291db9ae71208a4 (patch)
treeea5de4cf9ea3dba211ca79ff9559212d1ebe6b24
parent2ba647b61c77c6b84a9bd2bcd611ebb2d61f64f1 (diff)
lib: update from upstreamHEADmaster
-rw-r--r--lib/fmt.c1
-rw-r--r--lib/shell.c1085
-rw-r--r--lib/shell.h113
-rw-r--r--lib/shell_i.h145
-rw-r--r--src/main.c65
-rw-r--r--src/main.h30
-rw-r--r--src/sw.c23
7 files changed, 864 insertions, 598 deletions
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 <assert.h>
#include <errno.h>
+#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
@@ -32,48 +34,17 @@
#include <lib/macros.h>
#include <lib/hash.h>
#include <lib/shell.h>
+#include <lib/shell_i.h>
#include <src/mutex.h>
#include <src/panic.h>
#include <src/thread.h>
#include <src/uart.h>
-#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.
*
* Here is an incomplete description of escape sequences :
@@ -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,33 +106,257 @@ 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;
+ }
+
+ return EINVAL;
+}
+
+static int
+shell_cmd_check(const struct shell_cmd *cmd)
+{
+ size_t len;
+ int error;
+
+ len = strlen(cmd->name);
+
+ if (len == 0) {
+ return EINVAL;
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ error = shell_cmd_check_char(cmd->name[i]);
+
+ if (error) {
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static const char *
+shell_line_str(const struct shell_line *line)
+{
+ return line->str;
+}
+
+static size_t
+shell_line_size(const struct shell_line *line)
+{
+ return line->size;
}
static void
-shell_cmd_acquire(void)
+shell_line_reset(struct shell_line *line)
{
- mutex_lock(&shell_lock);
+ line->str[0] = '\0';
+ line->size = 0;
}
static void
-shell_cmd_release(void)
+shell_line_copy(struct shell_line *dest, const struct shell_line *src)
+{
+ strcpy(dest->str, src->str);
+ dest->size = src->size;
+}
+
+static int
+shell_line_cmp(const struct shell_line *a, const struct shell_line *b)
+{
+ return strcmp(a->str, b->str);
+}
+
+static int
+shell_line_insert(struct shell_line *line, size_t index, char c)
{
- mutex_unlock(&shell_lock);
+ size_t remaining_chars;
+
+ if (index > line->size) {
+ return EINVAL;
+ }
+
+ if ((line->size + 1) == sizeof(line->str)) {
+ return ENOMEM;
+ }
+
+ remaining_chars = line->size - index;
+
+ if (remaining_chars != 0) {
+ memmove(&line->str[index + 1], &line->str[index], remaining_chars);
+ }
+
+ line->str[index] = c;
+ line->size++;
+ line->str[line->size] = '\0';
+ return 0;
+}
+
+static int
+shell_line_erase(struct shell_line *line, size_t index)
+{
+ size_t remaining_chars;
+
+ if (index >= line->size) {
+ return EINVAL;
+ }
+
+ remaining_chars = line->size - index - 1;
+
+ if (remaining_chars != 0) {
+ memmove(&line->str[index], &line->str[index + 1], remaining_chars);
+ }
+
+ 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)];
+}
+
+static void
+shell_history_init(struct shell_history *history)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(history->lines); i++) {
+ shell_line_reset(shell_history_get(history, i));
+ }
+
+ history->newest = 0;
+ history->oldest = 0;
+ history->index = 0;
+}
+
+static struct shell_line *
+shell_history_get_newest(struct shell_history *history)
+{
+ return shell_history_get(history, history->newest);
+}
+
+static struct shell_line *
+shell_history_get_index(struct shell_history *history)
+{
+ return shell_history_get(history, history->index);
+}
+
+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;
+ }
+
+ 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 void
+shell_history_back(struct shell_history *history)
+{
+ if (history->index == history->oldest) {
+ return;
+ }
+
+ history->index--;
+ shell_line_copy(shell_history_get_newest(history),
+ shell_history_get_index(history));
+}
+
+static void
+shell_history_forward(struct shell_history *history)
+{
+ if (history->index == history->newest) {
+ return;
+ }
+
+ history->index++;
+
+ 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));
+ }
+}
+
+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));
+ }
+}
+
+static void
+shell_cmd_set_lock(struct shell_cmd_set *cmd_set)
+{
+ mutex_lock(&cmd_set->lock);
+}
+
+static void
+shell_cmd_set_unlock(struct shell_cmd_set *cmd_set)
+{
+ mutex_unlock(&cmd_set->lock);
+}
+
+static struct shell_bucket *
+shell_cmd_set_get_bucket(struct shell_cmd_set *cmd_set, const char *name)
+{
+ size_t index;
+
+ index = hash_str(name, SHELL_HTABLE_BITS);
+ assert(index < ARRAY_SIZE(cmd_set->htable));
+ return &cmd_set->htable[index];
}
static const struct shell_cmd *
-shell_cmd_lookup(const char *name)
+shell_cmd_set_lookup(struct shell_cmd_set *cmd_set, const char *name)
{
const struct shell_bucket *bucket;
const struct shell_cmd *cmd;
- shell_cmd_acquire();
+ shell_cmd_set_lock(cmd_set);
- bucket = shell_bucket_get(name);
+ 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) {
@@ -231,7 +364,7 @@ shell_cmd_lookup(const char *name)
}
}
- shell_cmd_release();
+ shell_cmd_set_unlock(cmd_set);
return cmd;
}
@@ -244,9 +377,13 @@ shell_cmd_lookup(const char *name)
* 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)
+shell_cmd_set_match(const struct shell_cmd_set *cmd_set, const char *str,
+ size_t size)
{
+ const struct shell_cmd *cmd;
+
+ cmd = cmd_set->cmd_list;
+
while (cmd != NULL) {
if (strncmp(cmd->name, str, size) == 0) {
return cmd;
@@ -273,15 +410,13 @@ shell_cmd_match(const struct shell_cmd *cmd, const char *str,
* 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_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;
- unsigned long size;
+ size_t size;
size = *sizep;
@@ -289,7 +424,7 @@ shell_cmd_complete(const char *str, unsigned long *sizep,
* 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);
+ cmd = shell_cmd_set_match(cmd_set, str, size);
if (cmd == NULL) {
return EINVAL;
@@ -306,8 +441,7 @@ shell_cmd_complete(const char *str, unsigned long *sizep,
*/
next = cmd->ls_next;
- if ((next == NULL)
- || (strncmp(cmd->name, next->name, size) != 0)) {
+ if ((next == NULL) || (strncmp(cmd->name, next->name, size) != 0)) {
*sizep = strlen(cmd->name);
return 0;
}
@@ -340,125 +474,101 @@ shell_cmd_complete(const char *str, unsigned long *sizep,
return EAGAIN;
}
-/*
- * 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)
+struct shell_cmd_set *
+shell_get_cmd_set(struct shell *shell)
{
- const struct shell_cmd *tmp;
- unsigned int i;
-
- printf("\n");
-
- for (tmp = cmd, i = 1; tmp != NULL; tmp = tmp->ls_next, i++) {
- if (strncmp(cmd->name, tmp->name, size) != 0) {
- break;
- }
-
- printf("%" SHELL_COMPLETION_MATCH_FMT, tmp->name);
-
- if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) == 0) {
- printf("\n");
- }
- }
+ return shell->cmd_set;
+}
- if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) != 1) {
- printf("\n");
- }
+static struct shell_history *
+shell_get_history(struct shell *shell)
+{
+ return &shell->history;
}
-static int
-shell_cmd_check_char(char c)
+static void
+shell_cb_help(struct shell *shell, int argc, char *argv[])
{
- if (((c >= 'a') && (c <= 'z'))
- || ((c >= 'A') && (c <= 'Z'))
- || ((c >= '0') && (c <= '9'))
- || (c == '-')
- || (c == '_')) {
- return 0;
+ struct shell_cmd_set *cmd_set;
+ const struct shell_cmd *cmd;
+
+ cmd_set = shell_get_cmd_set(shell);
+
+ if (argc > 2) {
+ argc = 2;
+ argv[1] = "help";
}
- return EINVAL;
-}
+ if (argc == 2) {
+ cmd = shell_cmd_set_lookup(cmd_set, argv[1]);
-static int
-shell_cmd_check(const struct shell_cmd *cmd)
-{
- unsigned long i;
- int error;
+ if (cmd == NULL) {
+ shell_printf(shell, "shell: help: %s: command not found\n",
+ argv[1]);
+ return;
+ }
- for (i = 0; cmd->name[i] != '\0'; i++) {
- error = shell_cmd_check_char(cmd->name[i]);
+ shell_printf(shell, "usage: %s\n%s\n", cmd->usage, cmd->short_desc);
- if (error) {
- return error;
+ if (cmd->long_desc != NULL) {
+ shell_printf(shell, "\n%s\n", cmd->long_desc);
}
+
+ return;
}
- if (i == 0) {
- return EINVAL;
+ shell_cmd_set_lock(cmd_set);
+
+ for (cmd = cmd_set->cmd_list; cmd != NULL; cmd = cmd->ls_next) {
+ shell_printf(shell, "%13s %s\n", cmd->name, cmd->short_desc);
}
- return 0;
+ shell_cmd_set_unlock(cmd_set);
}
-/*
- * The global lock must be acquired before calling this function.
- */
static void
-shell_cmd_add_list(struct shell_cmd *cmd)
+shell_cb_history(struct shell *shell, int argc, char *argv[])
{
- struct shell_cmd *prev, *next;
-
- prev = shell_list;
-
- if ((prev == NULL)
- || (strcmp(cmd->name, prev->name) < 0)) {
- shell_list = cmd;
- cmd->ls_next = prev;
- return;
- }
-
- for (;;) {
- next = prev->ls_next;
+ (void)argc;
+ (void)argv;
- if ((next == NULL)
- || (strcmp(cmd->name, next->name) < 0)) {
- break;
- }
+ shell_history_print(shell_get_history(shell), shell);
+}
- prev = next;
- }
+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"),
+};
- prev->ls_next = cmd;
- cmd->ls_next = next;
+void
+shell_cmd_set_init(struct shell_cmd_set *cmd_set)
+{
+ 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);
}
-/*
- * The global lock must be acquired before calling this function.
- */
static int
-shell_cmd_add(struct shell_cmd *cmd)
+shell_cmd_set_add_htable(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd)
{
struct shell_bucket *bucket;
struct shell_cmd *tmp;
- bucket = shell_bucket_get(cmd->name);
+ bucket = shell_cmd_set_get_bucket(cmd_set, cmd->name);
tmp = bucket->cmd;
if (tmp == NULL) {
bucket->cmd = cmd;
- goto out;
+ return 0;
}
for (;;) {
if (strcmp(cmd->name, tmp->name) == 0) {
- printf("shell: error: %s: shell command name collision", cmd->name);
return EEXIST;
}
@@ -470,290 +580,126 @@ shell_cmd_add(struct shell_cmd *cmd)
}
tmp->ht_next = cmd;
-
-out:
- shell_cmd_add_list(cmd);
return 0;
}
-int
-shell_cmd_register(struct shell_cmd *cmd)
-{
- int error;
-
- error = shell_cmd_check(cmd);
-
- if (error) {
- return error;
- }
-
- shell_cmd_acquire();
- error = shell_cmd_add(cmd);
- shell_cmd_release();
-
- return error;
-}
-
-static inline const char *
-shell_line_str(const struct shell_line *line)
-{
- return line->str;
-}
-
-static inline unsigned long
-shell_line_size(const struct shell_line *line)
-{
- return line->size;
-}
-
-static inline void
-shell_line_reset(struct shell_line *line)
-{
- line->str[0] = '\0';
- line->size = 0;
-}
-
-static inline void
-shell_line_copy(struct shell_line *dest, const struct shell_line *src)
-{
- strcpy(dest->str, src->str);
- dest->size = src->size;
-}
-
-static inline int
-shell_line_cmp(const struct shell_line *a, const struct shell_line *b)
+static void
+shell_cmd_set_add_list(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd)
{
- return strcmp(a->str, b->str);
-}
+ struct shell_cmd *prev, *next;
-static int
-shell_line_insert(struct shell_line *line, unsigned long index, char c)
-{
- unsigned long remaining_chars;
+ prev = cmd_set->cmd_list;
- if (index > line->size) {
- return EINVAL;
+ if ((prev == NULL) || (strcmp(cmd->name, prev->name) < 0)) {
+ cmd_set->cmd_list = cmd;
+ cmd->ls_next = prev;
+ return;
}
- if ((line->size + 1) == sizeof(line->str)) {
- return ENOMEM;
- }
+ for (;;) {
+ next = prev->ls_next;
- remaining_chars = line->size - index;
+ if ((next == NULL) || (strcmp(cmd->name, next->name) < 0)) {
+ break;
+ }
- if (remaining_chars != 0) {
- memmove(&line->str[index + 1], &line->str[index], remaining_chars);
+ prev = next;
}
- line->str[index] = c;
- line->size++;
- line->str[line->size] = '\0';
- return 0;
+ prev->ls_next = cmd;
+ cmd->ls_next = next;
}
static int
-shell_line_erase(struct shell_line *line, unsigned long index)
+shell_cmd_set_add(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd)
{
- unsigned long remaining_chars;
-
- if (index >= line->size) {
- return EINVAL;
- }
+ int error;
- remaining_chars = line->size - index - 1;
+ error = shell_cmd_set_add_htable(cmd_set, cmd);
- if (remaining_chars != 0) {
- memmove(&line->str[index], &line->str[index + 1], remaining_chars);
+ if (error) {
+ return error;
}
- line->size--;
- line->str[line->size] = '\0';
+ shell_cmd_set_add_list(cmd_set, cmd);
return 0;
}
-static struct shell_line *
-shell_history_get(unsigned long index)
-{
- return &shell_history[index % ARRAY_SIZE(shell_history)];
-}
-
-static struct shell_line *
-shell_history_get_newest(void)
-{
- return shell_history_get(shell_history_newest);
-}
-
-static struct shell_line *
-shell_history_get_index(void)
-{
- return shell_history_get(shell_history_index);
-}
-
-static void
-shell_history_reset_index(void)
-{
- shell_history_index = shell_history_newest;
-}
-
-static inline int
-shell_history_same_newest(void)
-{
- return (shell_history_newest != shell_history_oldest)
- && shell_line_cmp(shell_history_get_newest(),
- shell_history_get(shell_history_newest - 1)) == 0;
-}
-
-static void
-shell_history_push(void)
-{
- if ((shell_line_size(shell_history_get_newest()) == 0)
- || shell_history_same_newest()) {
- shell_history_reset_index();
- return;
- }
-
- shell_history_newest++;
- shell_history_reset_index();
-
- /* 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;
- }
-}
-
-static void
-shell_history_back(void)
-{
- if (shell_history_index == shell_history_oldest) {
- return;
- }
-
- shell_history_index--;
- shell_line_copy(shell_history_get_newest(), shell_history_get_index());
-}
-
-static void
-shell_history_forward(void)
-{
- if (shell_history_index == shell_history_newest) {
- return;
- }
-
- 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());
- }
-}
-
-static void
-shell_cmd_help(int argc, char *argv[])
+int
+shell_cmd_set_register(struct shell_cmd_set *cmd_set, struct shell_cmd *cmd)
{
- const struct shell_cmd *cmd;
-
- if (argc > 2) {
- argc = 2;
- argv[1] = "help";
- }
-
- if (argc == 2) {
- cmd = shell_cmd_lookup(argv[1]);
-
- if (cmd == NULL) {
- printf("shell: help: %s: command not found\n", argv[1]);
- return;
- }
-
- printf("usage: %s\n%s\n", cmd->usage, cmd->short_desc);
+ int error;
- if (cmd->long_desc != NULL) {
- printf("\n%s\n", cmd->long_desc);
- }
+ error = shell_cmd_check(cmd);
- return;
+ if (error) {
+ return error;
}
- shell_cmd_acquire();
-
- for (cmd = shell_list; cmd != NULL; cmd = cmd->ls_next) {
- printf("%13s %s\n", cmd->name, cmd->short_desc);
- }
+ shell_cmd_set_lock(cmd_set);
+ error = shell_cmd_set_add(cmd_set, cmd);
+ shell_cmd_set_unlock(cmd_set);
- shell_cmd_release();
+ return error;
}
-static void
-shell_cmd_history(int argc, char *argv[])
+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)
{
- unsigned long i;
-
- (void)argc;
- (void)argv;
-
- /* 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)));
- }
+ 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 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"),
-};
-
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 <errno.h>
+#include <stdarg.h>
#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <lib/macros.h>
-#include <src/panic.h>
-
-#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 <stddef.h>
+
+#include <lib/macros.h>
+
+#include <src/mutex.h>
+
+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 <stdarg.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
#include <lib/macros.h>
@@ -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 <lib/shell.h>
+
+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 <lib/shell.h>
#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());
}