From e902b0499618b9b95d73362cf8883fcc2c69f6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 10 Dec 2015 22:47:41 +0100 Subject: [PATCH] Implement completion --- src/cmd/cmd.c | 288 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 215 insertions(+), 73 deletions(-) diff --git a/src/cmd/cmd.c b/src/cmd/cmd.c index bfcf4e2..dfd75b8 100644 --- a/src/cmd/cmd.c +++ b/src/cmd/cmd.c @@ -7,24 +7,24 @@ #include "ucore/string.h" #include "ucore/strvec.h" #include "ucore/utils.h" - -//max words in a single command -#define UC_CMD_MAX_WORDS 32 - +#include "ucore/backtrace.h" +// enum UCCmdRc { - /**Executed ok */ UC_CMDRC_OK, /**Executed with warning */ UC_CMDRC_WARNING, - /*Not executed, incomplete command */ + /*incomplete command */ UC_CMDRC_INCOMPLETE, /*Command cann't uniquely be distinguished */ UC_CMDRC_AMBIGUOUS, + /* Command doesn't exist*/ + UC_CMDRC_NOT_FOUND, /*Not executed, generic error */ UC_CMDRC_ERROR, }; -typedef enum UCCmdRc (*uc_cmd_handler)(const struct UCStrv *args); +struct UCCmdArgs; +typedef enum UCCmdRc (*uc_cmd_handler)(const struct UCCmdArgs *args); enum UCCmdType { UC_CMD_ROOT = 1, @@ -34,6 +34,26 @@ enum UCCmdType { UC_CMD_END, }; + +/** + * Command are broken into words and inserted into a tree. + * the 3 commands: + * show all + * date + * show all bottles + * Results in the tree: + * + * +--------+ child +--------+ child +-------+ child +-----+ + * | root |------->| show |------->| all |------>| END | + * +--------+ +--------+ +-------+ +-----+ + * | next | next + * V V + * +--------+ child +-----+ +--------+ child +-----+ + * | date |-----> | END | | bottles|------>| END | + * +--------+ +-----+ +--------+ +-----+ + * + * END nodes are complete commands. Other child/next pointers will be nULL + */ struct UCCmdNode { enum UCCmdType type; uint32_t flags; @@ -62,12 +82,27 @@ struct UCCmdDef { }; +//max words in a single command +#define UC_CMD_MAX_WORDS 32 + struct UCCmdTokenizer { char *cmd_copy; char *cmd_words[UC_CMD_MAX_WORDS]; int num_words; }; +struct UCCmdArgs { + struct UCStrv argv; + struct UCCmdDef *cmd; +}; + +void print_args(const struct UCStrv *args); +static int uc_cmd_type_is_variable(enum UCCmdType type) +{ + return type == UC_CMD_LIST || + type == UC_CMD_STRING; +} + static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd, const char *delim) { @@ -89,21 +124,25 @@ static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd, const cha delim); } -static void uc_cmd_tokenize_destroy(struct UCCmdTokenizer *t) +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) +void +uc_cmd_runner_init(struct UCCmdRunner *r) { memset(r, 0, sizeof *r); r->root_node = &r->_root; r->_root.type = UC_CMD_KEYWORD; } -void uc_cmd_node_dealloc(struct UCCmdNode *n) +void +uc_cmd_node_dealloc(struct UCCmdNode *n) { struct UCCmdNode *next; while (n) { @@ -122,15 +161,15 @@ void uc_cmd_node_dealloc(struct UCCmdNode *n) } } -void uc_cmd_runner_destroy(struct UCCmdRunner *r) +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) +static struct UCCmdNode * +uc_cmd_node_alloc(enum UCCmdType type, void *data, uint32_t flags) { struct UCCmdNode *n; @@ -146,7 +185,8 @@ struct UCCmdNode *uc_cmd_node_alloc(enum UCCmdType type, void *data, uint32_t fl return n; } -struct UCCmdNode *uc_cmd_node_create(const char *word) +static struct UCCmdNode * +uc_cmd_node_create(const char *word) { struct UCCmdNode *node = NULL; @@ -172,14 +212,14 @@ struct UCCmdNode *uc_cmd_node_create(const char *word) node = uc_cmd_node_alloc(UC_CMD_KEYWORD, strdup(word), 0); } - return node; - } -static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word, struct UCStrv *args) +static int +uc_cmd_node_match(const struct UCCmdNode *n, const char *word, struct UCStrv *args) { size_t word_len; + int found = 0; if (word == NULL || word[0] == 0) { return 0; @@ -190,7 +230,10 @@ static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word, struct if (n->type == UC_CMD_KEYWORD) { if (strncmp(word, n->data, word_len) == 0) { - return 1; + if (args != NULL) { + uc_strv_append(args, n->data); + } + found = 1; } } else if (n->type == UC_CMD_LIST) { struct UCStrv *v = n->data; @@ -203,21 +246,22 @@ static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word, struct if (args != NULL) { uc_strv_append(args, v->strings[i]); } - return 1; + found = 1; } } } else if (n->type == UC_CMD_STRING) { if (args != NULL) { uc_strv_append(args, word); } - return 1; + found = 1; } - return 0; + return found; } -static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word) +static struct UCCmdNode * +uc_cmd_add(struct UCCmdNode *parent, const char *word) { struct UCCmdNode *new_node; struct UCCmdNode **iter = &parent->child; @@ -229,11 +273,10 @@ static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word) return NULL; } - while (*iter) { struct UCCmdNode *n = *iter; - if (new_node->type != UC_CMD_KEYWORD && n->type != UC_CMD_END) { + if (uc_cmd_type_is_variable(new_node->type) && n->type != UC_CMD_END) { //we don't support combinations of keywords and variables //at the same level. uc_cmd_node_dealloc(new_node); @@ -252,7 +295,8 @@ static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word) return new_node; } -int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd) +int +uc_cmd_install_noassert(struct UCCmdRunner *r, struct UCCmdDef *cmd) { struct UCCmdNode *node = r->root_node; struct UCCmdNode *new_node; @@ -267,6 +311,7 @@ int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd) for (int i = 0; i < t.num_words; i++) { node = uc_cmd_add(node, t.cmd_words[i]); if (node == NULL) { + uc_cmd_tokenize_destroy(&t); return -2; } } @@ -275,6 +320,7 @@ int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd) new_node = uc_cmd_node_alloc(UC_CMD_END, cmd, 0); if (new_node == NULL) { + uc_cmd_tokenize_destroy(&t); return -1; } new_node->next = node->child; @@ -286,33 +332,36 @@ int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd) return 0; } - -enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) +int +uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd) { + int rc; + + rc = uc_cmd_install_noassert(r, cmd); + UC_ASSERT(rc == 0); - struct UCCmdNode *node = r->root_node->child; - struct UCCmdTokenizer t; - enum UCCmdRc rc = UC_CMDRC_ERROR; - struct UCStrv args = UC_STRV_INITIALIZER; //arguments to the callback function + return rc; +} +enum UCCmdRc +uc_cmd_find(struct UCCmdNode *first, + const struct UCCmdTokenizer *t, + struct UCStrv *args, + struct UCCmdNode **result) +{ + struct UCCmdNode *node = first; + enum UCCmdRc rc = UC_CMDRC_NOT_FOUND; - - //make a copy since getfields destroys the original string - uc_cmd_tokenize(&t, cmd_line, NULL); - 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++) { + 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], &args)) { - found = node; + if (uc_cmd_node_match(node, t->cmd_words[i], args)) { + if (n_found == 0) { + //keep the 1. matched + found = node; + } n_found++; } node = node->next; @@ -320,31 +369,118 @@ enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) if (n_found == 1) { node = found->child; + rc = UC_CMDRC_OK; } else if (n_found > 1) { - uc_strv_destroy(&args); - uc_cmd_tokenize_destroy(&t); - 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) { + node = found; + rc = UC_CMDRC_AMBIGUOUS; break; } - node = node->next; } - if (node != NULL) { - struct UCCmdDef *cmd = node->data; - rc = cmd->callback(&args); + *result = node; + + if (node == NULL) { + rc = UC_CMDRC_NOT_FOUND; + } + + return rc; +} + + +enum UCCmdRc +uc_cmd_parse_and_find(struct UCCmdRunner *r, const char *cmd_line, + struct UCStrv *args, + struct UCCmdNode **result) +{ + + 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, NULL); + + if (t.num_words > 0) { + + //match the commands towards the command tree + // + rc = uc_cmd_find(node, &t, args, result); + } + + uc_cmd_tokenize_destroy(&t); + + return rc; +} + +enum UCCmdRc +uc_cmd_complete(struct UCCmdRunner *r, const char *cmd_line) +{ + + struct UCStrv completions = UC_STRV_INITIALIZER; + + struct UCCmdNode *found = NULL; + enum UCCmdRc rc; + + rc = uc_cmd_parse_and_find(r, cmd_line, NULL, &found); + if (rc != UC_CMDRC_NOT_FOUND && found != NULL) { + struct UCCmdNode *node = found; + while (node) { + if (node->type == UC_CMD_END) { + uc_strv_append(&completions, ""); + } else if (node->type == UC_CMD_LIST) { + uc_strv_append_all(&completions, node->data, 1); + } else if (node->type == UC_CMD_STRING || + node->type == UC_CMD_KEYWORD) { + uc_strv_append(&completions, node->data); + } + node = node->next; + } + + printf("Possible completions for '%s'\n", cmd_line); + print_args(&completions); + } else { - rc = UC_CMDRC_INCOMPLETE; + printf("No possible completions\n"); + } + + uc_strv_destroy(&completions); + + return rc; +} + + +enum UCCmdRc +uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) +{ + struct UCCmdArgs args = { + .argv = UC_STRV_INITIALIZER, + .cmd = NULL, + }; + + struct UCCmdNode *found = NULL; + enum UCCmdRc rc; + + rc = uc_cmd_parse_and_find(r, cmd_line, &args.argv, &found); + if (rc == UC_CMDRC_OK) { + // Now check the current level for any UC_CMD_END + // which indicates a complete command + while (found) { + if (found->type == UC_CMD_END) { + break; + } + found = found->next; + } + + if (found != NULL) { + struct UCCmdDef *cmd = found->data; + args.cmd = cmd; + rc = cmd->callback(&args); + } else { + rc = UC_CMDRC_INCOMPLETE; + } } - uc_strv_destroy(&args); - uc_cmd_tokenize_destroy(&t); + uc_strv_destroy(&args.argv); return rc; } @@ -353,37 +489,39 @@ enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) void print_args(const struct UCStrv *args) { for (size_t i = 0; i < args->cnt; i++) { - printf("arg %zu:%s\n", i, args->strings[i]); + printf("arg %zu: '%s' ", i, args->strings[i]); } + puts(""); } -enum UCCmdRc cmd1_cb(const struct UCStrv *args) + +enum UCCmdRc cmd1_cb(const struct UCCmdArgs *args) { puts(__func__); - print_args(args); + print_args(&args->argv); return UC_CMDRC_OK; } -enum UCCmdRc cmd2_cb(const struct UCStrv *args) +enum UCCmdRc cmd2_cb(const struct UCCmdArgs *args) { puts(__func__); - print_args(args); + print_args(&args->argv); return UC_CMDRC_OK; } -enum UCCmdRc cmd3_cb(const struct UCStrv *args) +enum UCCmdRc cmd3_cb(const struct UCCmdArgs *args) { puts(__func__); - print_args(args); + print_args(&args->argv); return UC_CMDRC_OK; } -enum UCCmdRc cmd4_cb(const struct UCStrv *args) +enum UCCmdRc cmd4_cb(const struct UCCmdArgs *args) { puts(__func__); - print_args(args); + print_args(&args->argv); return UC_CMDRC_OK; } @@ -411,7 +549,7 @@ int main(int argc, char *argv[]) }; struct UCCmdDef cmd3 = { - "show empty (bottle|glass)", + "show empty bottle", cmd3_cb, 0, NULL @@ -433,7 +571,8 @@ int main(int argc, char *argv[]) uc_cmd_run(&r, "delete file /tmp/a"); uc_cmd_run(&r, "show all"); uc_cmd_run(&r, "show empty"); - uc_cmd_run(&r, "sh emp g"); + uc_cmd_run(&r, "sh emp b"); + uc_cmd_run(&r, ""); /* while (fgets(line,sizeof line, stdin)) { enum UCCmdRc rc = uc_cmd_run(&r, line); @@ -443,6 +582,9 @@ int main(int argc, char *argv[]) } */ + uc_cmd_complete(&r, "sho em"); + uc_cmd_complete(&r, "show "); + uc_cmd_complete(&r, ""); uc_cmd_runner_destroy(&r);