summaryrefslogtreecommitdiff
path: root/kern
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2018-07-25 01:16:24 +0200
committerRichard Braun <rbraun@sceen.net>2018-07-25 01:16:24 +0200
commitd3e43f5bfda0bdad7a829a7ed8c1272a395b196b (patch)
tree897e7851a995f243afdfa5999d786541781f56a9 /kern
parent3a175471f4246c429e89460ab42cd6640427532a (diff)
kern/{fmt,shell}: update from upstream
The shell module has been "objectified" and now supports multiple instances with shareable command sets.
Diffstat (limited to 'kern')
-rw-r--r--kern/fmt.c1
-rw-r--r--kern/kmem.c6
-rw-r--r--kern/log.c6
-rw-r--r--kern/shell.c1128
-rw-r--r--kern/shell.h106
-rw-r--r--kern/shell_i.h139
-rw-r--r--kern/shutdown.c8
-rw-r--r--kern/syscnt.c6
-rw-r--r--kern/task.c6
-rw-r--r--kern/thread.c6
10 files changed, 819 insertions, 593 deletions
diff --git a/kern/fmt.c b/kern/fmt.c
index 61eef79e..414ab1f1 100644
--- a/kern/fmt.c
+++ b/kern/fmt.c
@@ -109,7 +109,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/kern/kmem.c b/kern/kmem.c
index 8856292b..bd2e1d56 100644
--- a/kern/kmem.c
+++ b/kern/kmem.c
@@ -1168,10 +1168,12 @@ out:
}
static void
-kmem_shell_info(int argc, char **argv)
+kmem_shell_info(struct shell *shell, int argc, char **argv)
{
struct kmem_cache *cache;
+ (void)shell;
+
if (argc < 2) {
kmem_info();
} else {
@@ -1195,7 +1197,7 @@ static struct shell_cmd kmem_shell_cmds[] = {
static int __init
kmem_setup_shell(void)
{
- SHELL_REGISTER_CMDS(kmem_shell_cmds);
+ SHELL_REGISTER_CMDS(kmem_shell_cmds, shell_get_main_cmd_set());
return 0;
}
diff --git a/kern/log.c b/kern/log.c
index 55578955..f20f966b 100644
--- a/kern/log.c
+++ b/kern/log.c
@@ -380,11 +380,13 @@ log_dump(unsigned int level)
}
static void
-log_shell_dump(int argc, char **argv)
+log_shell_dump(struct shell *shell, int argc, char **argv)
{
unsigned int level;
int ret;
+ (void)shell;
+
if (argc != 2) {
level = log_print_level;
} else {
@@ -418,7 +420,7 @@ static struct shell_cmd log_shell_cmds[] = {
static int __init
log_setup_shell(void)
{
- SHELL_REGISTER_CMDS(log_shell_cmds);
+ SHELL_REGISTER_CMDS(log_shell_cmds, shell_get_main_cmd_set());
return 0;
}
diff --git a/kern/shell.c b/kern/shell.c
index e99f9119..1fff5d26 100644
--- a/kern/shell.c
+++ b/kern/shell.c
@@ -18,8 +18,12 @@
* 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>
#include <string.h>
#include <kern/hash.h>
@@ -27,43 +31,15 @@
#include <kern/log.h>
#include <kern/macros.h>
#include <kern/mutex.h>
+#include <kern/printf.h>
#include <kern/shell.h>
+#include <kern/shell_i.h>
#include <kern/thread.h>
-/*
- * 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 :
@@ -74,61 +50,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 ' '
/*
@@ -139,20 +67,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)
{
@@ -187,33 +101,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)
{
- mutex_unlock(&shell_lock);
+ 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)
+{
+ 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) {
@@ -221,7 +359,7 @@ shell_cmd_lookup(const char *name)
}
}
- shell_cmd_release();
+ shell_cmd_set_unlock(cmd_set);
return cmd;
}
@@ -234,9 +372,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;
@@ -263,15 +405,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;
@@ -279,7 +419,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;
@@ -296,8 +436,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;
}
@@ -330,125 +469,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, "%19s %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) {
- log_err("shell: %s: shell command name collision", cmd->name);
return EEXIST;
}
@@ -460,290 +575,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("%19s %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
@@ -753,87 +704,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;
}
@@ -841,11 +796,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--;
}
@@ -853,22 +808,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;
+
+ shell_cmd_set_lock(cmd_set);
- str = shell_line_str(shell_history_get_newest());
+ 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;
@@ -876,27 +867,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;
@@ -906,65 +897,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);
}
}
@@ -983,9 +974,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];
}
@@ -1000,29 +989,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;
@@ -1031,75 +1015,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);
}
}
@@ -1109,19 +1093,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;
@@ -1130,19 +1114,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) {
@@ -1156,7 +1138,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;
@@ -1168,14 +1150,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) {
@@ -1185,19 +1167,49 @@ shell_run(void *arg)
}
}
-static int __init
-shell_setup(void)
+void
+shell_printf(struct shell *shell, const char *format, ...)
{
- unsigned long i;
- int error;
+ va_list ap;
- mutex_init(&shell_lock);
+ va_start(ap, format);
+ shell_vprintf(shell, format, ap);
+ va_end(ap);
+}
- for (i = 0; i < ARRAY_SIZE(shell_default_cmds); i++) {
- error = shell_cmd_register(&shell_default_cmds[i]);
- error_check(error, "shell_cmd_register");
- }
+void
+shell_vprintf(struct shell *shell, const char *format, va_list ap)
+{
+ shell->vfprintf_fn(shell->io_object, format, ap);
+}
+
+/*
+ * XXX Temporary glue until the console module is reworked.
+ */
+
+static struct shell_cmd_set shell_main_cmd_set;
+static struct shell shell_main;
+static int
+shell_main_getc(void *io_object)
+{
+ (void)io_object;
+ return getchar();
+}
+
+static void
+shell_main_vfprintf(void *io_object, const char *format, va_list ap)
+{
+ (void)io_object;
+ vprintf(format, ap);
+}
+
+static int __init
+shell_setup(void)
+{
+ shell_cmd_set_init(&shell_main_cmd_set);
+ shell_init(&shell_main, &shell_main_cmd_set,
+ shell_main_getc, shell_main_vfprintf, NULL);
return 0;
}
@@ -1206,6 +1218,18 @@ INIT_OP_DEFINE(shell_setup,
INIT_OP_DEP(mutex_setup, true),
INIT_OP_DEP(printf_setup, true));
+struct shell_cmd_set * __init
+shell_get_main_cmd_set(void)
+{
+ return &shell_main_cmd_set;
+}
+
+static void
+shell_main_run(void *arg)
+{
+ shell_run(arg);
+}
+
void __init
shell_start(void)
{
@@ -1215,6 +1239,6 @@ shell_start(void)
thread_attr_init(&attr, THREAD_KERNEL_PREFIX "shell");
thread_attr_set_detached(&attr);
- error = thread_create(&thread, &attr, shell_run, NULL);
+ error = thread_create(&thread, &attr, shell_main_run, &shell_main);
error_check(error, "thread_create");
}
diff --git a/kern/shell.h b/kern/shell.h
index 0526019e..bde2a341 100644
--- a/kern/shell.h
+++ b/kern/shell.h
@@ -24,34 +24,52 @@
#ifndef KERN_SHELL_H
#define KERN_SHELL_H
+#include <stdarg.h>
#include <stddef.h>
#include <kern/error.h>
#include <kern/init.h>
#include <kern/macros.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_]); \
- error_check(error_, __func__); \
- } \
-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 <kern/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_]); \
+ error_check(error_, __func__); \
+ } \
+MACRO_END
/*
* Static shell command initializers.
@@ -69,9 +87,9 @@ void shell_cmd_init(struct shell_cmd *cmd, const char *name,
const char *short_desc, const char *long_desc);
/*
- * Start the shell thread.
+ * Initialize a command set.
*/
-void shell_start(void);
+void shell_cmd_set_init(struct shell_cmd_set *cmd_set);
/*
* Register a shell command.
@@ -79,16 +97,50 @@ void shell_start(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.
*/
-int shell_cmd_register(struct shell_cmd *cmd);
+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.
+ */
+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)));
/*
* This init operation provides :
- * - commands can be registered
- * - module fully initialized
+ * - main shell command registration
*/
INIT_OP_DECLARE(shell_setup);
+struct shell_cmd_set * shell_get_main_cmd_set(void);
+void shell_start(void);
+
#endif /* KERN_SHELL_H */
diff --git a/kern/shell_i.h b/kern/shell_i.h
new file mode 100644
index 00000000..df1f4fa5
--- /dev/null
+++ b/kern/shell_i.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Upstream site with license notes :
+ * http://git.sceen.net/rbraun/librbraun.git/
+ */
+
+#ifndef KERN_SHELL_I_H
+#define KERN_SHELL_I_H
+
+#include <stddef.h>
+
+#include <kern/macros.h>
+#include <kern/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 /* KERN_SHELL_I_H */
diff --git a/kern/shutdown.c b/kern/shutdown.c
index 510911fe..57eb66e4 100644
--- a/kern/shutdown.c
+++ b/kern/shutdown.c
@@ -29,8 +29,9 @@ static struct plist shutdown_ops_list;
#ifdef CONFIG_SHELL
static void
-shutdown_shell_halt(int argc, char **argv)
+shutdown_shell_halt(struct shell *shell, int argc, char **argv)
{
+ (void)shell;
(void)argc;
(void)argv;
@@ -38,8 +39,9 @@ shutdown_shell_halt(int argc, char **argv)
}
static void
-shutdown_shell_reboot(int argc, char **argv)
+shutdown_shell_reboot(struct shell *shell, int argc, char **argv)
{
+ (void)shell;
(void)argc;
(void)argv;
@@ -58,7 +60,7 @@ static struct shell_cmd shutdown_shell_cmds[] = {
static int __init
shutdown_setup_shell(void)
{
- SHELL_REGISTER_CMDS(shutdown_shell_cmds);
+ SHELL_REGISTER_CMDS(shutdown_shell_cmds, shell_get_main_cmd_set());
return 0;
}
diff --git a/kern/syscnt.c b/kern/syscnt.c
index e0b213d7..1fd3cf06 100644
--- a/kern/syscnt.c
+++ b/kern/syscnt.c
@@ -36,10 +36,12 @@ static struct mutex syscnt_lock;
#ifdef CONFIG_SHELL
static void
-syscnt_shell_info(int argc, char **argv)
+syscnt_shell_info(struct shell *shell, int argc, char **argv)
{
char *prefix;
+ (void)shell;
+
prefix = (argc >= 2) ? argv[1] : NULL;
syscnt_info(prefix);
}
@@ -53,7 +55,7 @@ static struct shell_cmd syscnt_shell_cmds[] = {
static int __init
syscnt_setup_shell(void)
{
- SHELL_REGISTER_CMDS(syscnt_shell_cmds);
+ SHELL_REGISTER_CMDS(syscnt_shell_cmds, shell_get_main_cmd_set());
return 0;
}
diff --git a/kern/task.c b/kern/task.c
index aaa30f44..ebdb021e 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -62,11 +62,13 @@ task_init(struct task *task, const char *name, struct vm_map *map)
#ifdef CONFIG_SHELL
static void
-task_shell_info(int argc, char *argv[])
+task_shell_info(struct shell *shell, int argc, char *argv[])
{
struct task *task;
int error;
+ (void)shell;
+
if (argc == 1) {
task_info(NULL);
return;
@@ -97,7 +99,7 @@ static struct shell_cmd task_shell_cmds[] = {
static int __init
task_setup_shell(void)
{
- SHELL_REGISTER_CMDS(task_shell_cmds);
+ SHELL_REGISTER_CMDS(task_shell_cmds, shell_get_main_cmd_set());
return 0;
}
diff --git a/kern/thread.c b/kern/thread.c
index a8f58b39..6625163a 100644
--- a/kern/thread.c
+++ b/kern/thread.c
@@ -2220,7 +2220,7 @@ thread_setup_runq(struct thread_runq *runq)
* tracing.
*/
static void
-thread_shell_trace(int argc, char *argv[])
+thread_shell_trace(struct shell *shell, int argc, char *argv[])
{
const char *task_name, *thread_name;
struct thread_runq *runq;
@@ -2229,6 +2229,8 @@ thread_shell_trace(int argc, char *argv[])
struct task *task;
int error;
+ (void)shell;
+
if (argc != 3) {
error = EINVAL;
goto error;
@@ -2278,7 +2280,7 @@ static struct shell_cmd thread_shell_cmds[] = {
static int __init
thread_setup_shell(void)
{
- SHELL_REGISTER_CMDS(thread_shell_cmds);
+ SHELL_REGISTER_CMDS(thread_shell_cmds, shell_get_main_cmd_set());
return 0;
}