From 8adec5408e6b16839281b9409a8c152324f6f704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 8 Dec 2015 18:52:25 +0100 Subject: [PATCH] Progress on cmd --- src/cmd/cmd.c | 368 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 339 insertions(+), 29 deletions(-) diff --git a/src/cmd/cmd.c b/src/cmd/cmd.c index 9459958..59aa421 100644 --- a/src/cmd/cmd.c +++ b/src/cmd/cmd.c @@ -1,54 +1,364 @@ #include +#include +#include +#include +#include +#include "ucore/string.h" +#include "ucore/utils.h" + +//max words in a single command +#define UC_CMD_MAX_WORDS 32 enum UCCmdRc { /**Executed ok */ - UC_CMD_OK, + UC_CMDRC_OK, /**Executed with warning */ - UC_CMD_WARNING, + UC_CMDRC_WARNING, /*Not executed, incomplete command */ - UC_CMD_INCOMPLETE, + UC_CMDRC_INCOMPLETE, + /*Command cann't uniquely be distinguished */ + UC_CMDRC_AMBIGUOUS, /*Not executed, generic error */ - UC_CMD_ERROR, + UC_CMDRC_ERROR, }; typedef enum UCCmdRc (*uc_cmd_handler)(int narg, char *args[]); -typedef void (*uc_cmd_write_cfg)(void); -#define UC_CMD_FL_HIDDEN (1 << 0) - -struct UCCmdNode; -struct UCCmdDef; - -struct UCCmdSettings { - const char *name; - const char *version; - const char *boot_cfg_file; - - struct UCCmdNode *initial_node; - struct UCCmdNode *node_head; +enum UCCmdType { + UC_CMD_ROOT = 1, + UC_CMD_KEYWORD, + UC_CMD_LIST, + UC_CMD_END, }; struct UCCmdNode { - int id; - uc_cmd_write_cfg write_func; - struct UCCmdDef *cmd_head; //linked list of UCCmdDef - struct UCCmdNode *next; + enum UCCmdType type; + uint32_t flags; + void *data; + struct UCCmdNode *next; //next at same level + struct UCCmdNode *child; +}; + +//for UC_CMD_LIST, +struct UCCmdList { + char *word; + struct UCCmdList *next; +}; + +struct UCCmdRunner { + struct UCCmdNode *root_node; + struct UCCmdNode _root; }; struct UCCmdDef { const char *cmd; - const char **help; - size_t cmd_len; - uc_cmd_handler cb; + uc_cmd_handler callback; uint32_t flags; struct UCCmdDef *next; }; -void uc_cmd_init(const char *app_name, const char *version); -int uc_cmd_read_config(const char *filename); -void uc_cmd_set_configfile(const char *filename); -void uc_cmd_add_cmd(struct UCCmdDef *cmd, int parent_node_id); -void uc_cmd_set_default_node(int node_id); +struct UCCmdTokenizer { + char *cmd_copy; + char *cmd_words[UC_CMD_MAX_WORDS]; + int num_words; +}; +static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd) +{ + t->num_words = 0; + t->cmd_copy = strdup(cmd); + if (t->cmd_copy == NULL) { + assert(t->cmd_copy); + return; + } + + t->num_words = getfields(t->cmd_copy, + t->cmd_words, + ARRAY_SIZE(t->cmd_words), + 1, + "\r\n\t\v "); +} + +static void uc_cmd_tokenize_destroy(struct UCCmdTokenizer *t) +{ + free(t->cmd_copy); + t->num_words = 0; + t->cmd_copy = NULL; +} + +void uc_cmd_runner_init(struct UCCmdRunner *r) +{ + memset(r, 0, sizeof *r); + r->root_node = &r->_root; + r->_root.type = UC_CMD_KEYWORD; +} + +static void uc_cmd_list_free(struct UCCmdList *l) +{ + struct UCCmdList *next; + + while (l) { + next = l->next; + free(l->word); + free(l); + l = next; + } +} + +void uc_cmd_node_dealloc(struct UCCmdNode *n) +{ + struct UCCmdNode *next; + while (n) { + uc_cmd_node_dealloc(n->child); + next = n->next; + if (n->type == UC_CMD_KEYWORD) { + free(n->data); + } else if (n->type == UC_CMD_LIST) { + uc_cmd_list_free(n->data); + } + free(n); + n = next; + } +} + +void uc_cmd_runner_destroy(struct UCCmdRunner *r) +{ + uc_cmd_node_dealloc(r->root_node->child); + r->root_node->child = NULL; +} + + + +struct UCCmdNode *uc_cmd_node_alloc(enum UCCmdType type, void *data, uint32_t flags) +{ + struct UCCmdNode *n; + + n = calloc(1, sizeof *n); + if (n == NULL) { + return NULL; + } + + n->type = type; + n->data = data; + n->flags = flags; + + return n; +} + +static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word) +{ + size_t word_len; + + if (word == NULL || word[0] == 0) { + return 0; + } + + word_len = strlen(word); + + if (n->type == UC_CMD_KEYWORD) { + return strncmp(word, n->data, word_len) == 0; + } + + if (n->type == UC_CMD_LIST) { + struct UCCmdList *l = n->data; + + assert(l); + assert(l->word); + + while (l) { + if (strncmp(word, n->data, word_len) == 0) { + return 1; + } + l = l->next; + } + return 0; + } + + + return 0; + +} + +static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word) +{ + struct UCCmdNode *new_node; + struct UCCmdNode **location = &parent->child; + + assert(parent); + + while (*location) { + struct UCCmdNode *n = *location; + if (uc_cmd_node_match(n, word)) { + return *location; + } + location = &n->next; + } + + new_node = uc_cmd_node_alloc(UC_CMD_KEYWORD, strdup(word), 0); + *location = new_node; + + return new_node; +} + +int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd) +{ + struct UCCmdNode *node = r->root_node; + struct UCCmdNode *new_node; + struct UCCmdTokenizer t; + + uc_cmd_tokenize(&t, cmd->cmd); + if (t.num_words <= 0) { + uc_cmd_tokenize_destroy(&t); + return -1; + } + + for (int i = 0; i < t.num_words; i++) { + node = uc_cmd_add(node, t.cmd_words[i]); + } + + //TODO, check for duplicate command here. + + new_node = uc_cmd_node_alloc(UC_CMD_END, cmd, 0); + if (new_node == NULL) { + return -1; + } + new_node->next = node->child; + node->child = new_node; + + + uc_cmd_tokenize_destroy(&t); + + return 0; +} + + +enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) +{ + + struct UCCmdNode *node = r->root_node->child; + struct UCCmdTokenizer t; + enum UCCmdRc rc = UC_CMDRC_ERROR; + + //make a copy since getfields destroys the original string + uc_cmd_tokenize(&t, cmd_line); + if (t.num_words <= 0) { + uc_cmd_tokenize_destroy(&t); + return rc; + } + + //match the commands towards the command tree + // + for (int i = 0; i < t.num_words && node; i++) { + int n_found = 0; + struct UCCmdNode *found = NULL; + + while (node) { + if (uc_cmd_node_match(node, t.cmd_words[i])) { + found = node; + n_found++; + } + node = node->next; + } + + if (n_found == 1) { + node = found->child; + } else if (n_found > 1) { + return UC_CMDRC_AMBIGUOUS; + } + } + + // Now check the current level for any UC_CMD_END + // which indicates a complete command + while (node) { + if (node->type == UC_CMD_END) { + break; + } + node = node->next; + } + + if (node != NULL) { + struct UCCmdDef *cmd = node->data; + rc = cmd->callback(0, NULL); + } else { + rc = UC_CMDRC_INCOMPLETE; + } + + uc_cmd_tokenize_destroy(&t); + + return rc; +} + + +enum UCCmdRc cmd1_cb(int narg, char *args[]) +{ + puts(__func__); + + return UC_CMDRC_OK; +} + +enum UCCmdRc cmd2_cb(int narg, char *args[]) +{ + puts(__func__); + + return UC_CMDRC_OK; +} + +enum UCCmdRc cmd3_cb(int narg, char *args[]) +{ + puts(__func__); + + return UC_CMDRC_OK; +} + + + + +int main(int argc, char *argv[]) +{ + char line[128]; + struct UCCmdRunner r; + struct UCCmdDef cmd1 = { + "show all", + cmd1_cb, + 0, + NULL + }; + + struct UCCmdDef cmd2 = { + "show empty", + cmd2_cb, + 0, + NULL + }; + + struct UCCmdDef cmd3 = { + "show empty bottles", + cmd3_cb, + 0, + NULL + }; + + uc_cmd_runner_init(&r); + uc_cmd_install(&r, &cmd1); + uc_cmd_install(&r, &cmd2); + uc_cmd_install(&r, &cmd3); + + uc_cmd_run(&r, "show all"); + uc_cmd_run(&r, "show empty"); + uc_cmd_run(&r, "show empty bottles"); + /* + while (fgets(line,sizeof line, stdin)) { + enum UCCmdRc rc = uc_cmd_run(&r, line); + if (rc != UC_CMDRC_OK) { + printf("Command failed with code %d\n", rc); + } + } + */ + + uc_cmd_runner_destroy(&r); + + + return 1; +}