diff --git a/src/cmd/cmd.c b/src/cmd/cmd.c index 2bed370..a556e4f 100644 --- a/src/cmd/cmd.c +++ b/src/cmd/cmd.c @@ -68,11 +68,12 @@ struct UCCmdNode { union { struct UCCmdWord word; struct UCCmdList list; - struct UCCmdDef *cmd; + const struct UCCmdDef *cmd; } data; uint32_t flags; struct UCCmdNode *next; //next at same level struct UCCmdNode *child; + struct UCCmdNode *parent; }; struct UCCmdRunner { @@ -99,17 +100,21 @@ struct UCCmdTokenizer { struct UCCmdArgs { struct UCStrv argv; - struct UCCmdDef *cmd; + const struct UCCmdDef *cmd; }; -void print_args(const struct UCStrv *args); -static int uc_cmd_type_is_variable(enum UCCmdType type) +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) +static void +uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd, const char *delim) { t->num_words = 0; @@ -144,13 +149,14 @@ uc_cmd_runner_init(struct UCCmdRunner *r) { memset(r, 0, sizeof *r); r->root_node = &r->_root; - r->_root.type = UC_CMD_KEYWORD; + r->_root.type = UC_CMD_ROOT; } void uc_cmd_node_dealloc(struct UCCmdNode *n) { struct UCCmdNode *next; + while (n) { uc_cmd_node_dealloc(n->child); next = n->next; @@ -281,6 +287,7 @@ uc_cmd_add(struct UCCmdNode *parent, const char *word) if (new_node == NULL) { return NULL; } + new_node->parent = parent; while (*iter) { struct UCCmdNode *n = *iter; @@ -304,12 +311,80 @@ uc_cmd_add(struct UCCmdNode *parent, const char *word) return new_node; } +static int uc_cmd_node_add_help(struct UCCmdNode *node) +{ + struct UCCmdTokenizer t; + int i; + + if (node->data.cmd->help == NULL) { + //allow helpless commands + return 0; + } + + if (node->type != UC_CMD_END) { + return -10; + } + + uc_cmd_tokenize(&t, node->data.cmd->help, "\n"); + if (t.num_words <= 0) { + uc_cmd_tokenize_destroy(&t); + return -11; + } + + i = t.num_words - 1; + //we have the last node in the tree + //Walk it up towards the root(backwards) and fill + //in help texts where it's missing. + while (node->parent) { + + node = node->parent; + if (node->type == UC_CMD_ROOT) { + break; + } + if (i < 0) { + break; + } + + if (node->type == UC_CMD_KEYWORD || + node->type == UC_CMD_STRING) { + if (node->data.word.descr == NULL) { + node->data.word.descr = strdup(t.cmd_words[i]); + } + i--; + } else if (node->type == UC_CMD_LIST) { + if (node->data.list.descrs.cnt == 0) { + for (int k = (int)node->data.list.words.cnt -1; k >= 0; k++) { + if (i < 0) { + break; + } + uc_strv_append(&node->data.list.descrs, t.cmd_words[i]); + i--; + + } + } else { + i -= (int)node->data.list.words.cnt; + } + } + } + + uc_cmd_tokenize_destroy(&t); + + if (node->type != UC_CMD_ROOT || i != -1) { + //we didn't consume all the help tokens, so the + //command definition is screwd up + return -20; + } + + return 0; +} + int uc_cmd_install_noassert(struct UCCmdRunner *r, struct UCCmdDef *cmd) { struct UCCmdNode *node = r->root_node; struct UCCmdNode *new_node; struct UCCmdTokenizer t; + int rc; uc_cmd_tokenize(&t, cmd->cmd, NULL); if (t.num_words <= 0) { @@ -333,13 +408,15 @@ uc_cmd_install_noassert(struct UCCmdRunner *r, struct UCCmdDef *cmd) return -1; } new_node->data.cmd = cmd; - new_node->next = node->child; - node->child = new_node; + new_node->next = node->child; + node->child = new_node; + new_node->parent = node; + rc = uc_cmd_node_add_help(new_node); uc_cmd_tokenize_destroy(&t); - return 0; + return rc; } int @@ -360,11 +437,11 @@ uc_cmd_find(struct UCCmdNode *first, struct UCCmdNode **result) { struct UCCmdNode *node = first; + struct UCCmdNode *found = NULL; enum UCCmdRc rc = UC_CMDRC_NOT_FOUND; 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)) { @@ -387,9 +464,9 @@ uc_cmd_find(struct UCCmdNode *first, } } - *result = node; + *result = found; - if (node == NULL) { + if (found == NULL) { rc = UC_CMDRC_NOT_FOUND; } @@ -423,42 +500,57 @@ uc_cmd_parse_and_find(struct UCCmdRunner *r, const char *cmd_line, } enum UCCmdRc -uc_cmd_complete(struct UCCmdRunner *r, const char *cmd_line) +uc_cmd_help(struct UCCmdRunner *r, const char *cmd_line, struct UCStrv *completions) { + struct UCCmdNode *found = NULL; + enum UCCmdRc rc; - struct UCStrv completions = UC_STRV_INITIALIZER; + rc = uc_cmd_parse_and_find(r, cmd_line, NULL, &found); + if (rc == UC_CMDRC_OK) { + struct UCCmdNode *node = found; + if (node->type == UC_CMD_LIST) { + uc_strv_append_all(completions, &node->data.list.descrs, 1); + } else if (node->type == UC_CMD_STRING || + node->type == UC_CMD_KEYWORD) { + if (node->data.word.descr != NULL) { + uc_strv_append(completions, node->data.word.descr); + } + } + } + + return rc; +} + +enum UCCmdRc +uc_cmd_complete(struct UCCmdRunner *r, const char *cmd_line, struct UCStrv *completions) +{ 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; + struct UCCmdNode *node = found->child; + while (node) { if (node->type == UC_CMD_END) { - uc_strv_append(&completions, ""); + uc_strv_append(completions, ""); } else if (node->type == UC_CMD_LIST) { - uc_strv_append_all(&completions, &node->data.list.words, 1); + uc_strv_append_all(completions, &node->data.list.words, 1); } else if (node->type == UC_CMD_STRING || node->type == UC_CMD_KEYWORD) { - uc_strv_append(&completions, node->data.word.word); + uc_strv_append(completions, node->data.word.word); } node = node->next; } - printf("Possible completions for '%s'\n", cmd_line); - print_args(&completions); + } - } else { - printf("No possible completions\n"); - } - - uc_strv_destroy(&completions); - return rc; } + enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) { @@ -472,6 +564,7 @@ uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) rc = uc_cmd_parse_and_find(r, cmd_line, &args.argv, &found); if (rc == UC_CMDRC_OK) { + found = found->child; // Now check the current level for any UC_CMD_END // which indicates a complete command while (found) { @@ -482,7 +575,7 @@ uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) } if (found != NULL) { - struct UCCmdDef *cmd = found->data.cmd; + const struct UCCmdDef *cmd = found->data.cmd; args.cmd = cmd; rc = cmd->callback(&args); } else { @@ -536,8 +629,39 @@ enum UCCmdRc cmd4_cb(const struct UCCmdArgs *args) return UC_CMDRC_OK; } +void do_complete(struct UCCmdRunner *r, const char *command) +{ + struct UCStrv completions = UC_STRV_INITIALIZER; + uc_cmd_complete(r, command, &completions); + if (completions.cnt > 0) { + printf("Possible completions for '%s' :\n", command); + for (size_t i = 0; i < completions.cnt; i++) { + printf("\t%s\n", completions.strings[i]); + } + } else { + printf("No completions for '%s' :\n", command); + } + uc_strv_destroy(&completions); +} + +void do_help(struct UCCmdRunner *r, const char *command) +{ + struct UCStrv completions = UC_STRV_INITIALIZER; + + uc_cmd_help(r, command, &completions); + if (completions.cnt > 0) { + printf("help for '%s' :\n", command); + for (size_t i = 0; i < completions.cnt; i++) { + printf("\t%s\n", completions.strings[i]); + } + } else { + printf("No help for '%s' :\n", command); + } + + uc_strv_destroy(&completions); +} int main(int argc, char *argv[]) @@ -546,7 +670,7 @@ int main(int argc, char *argv[]) struct UCCmdRunner r; struct UCCmdDef cmd1 = { "show all", - "\n\n", + " \nEverything\n", cmd1_cb, 0, NULL @@ -554,7 +678,7 @@ int main(int argc, char *argv[]) struct UCCmdDef cmd2 = { "show empty", - "\n\n", + "Show parameter\nAll empty\n", cmd2_cb, 0, NULL @@ -562,7 +686,7 @@ int main(int argc, char *argv[]) struct UCCmdDef cmd3 = { "show empty bottle", - "\n\n\n", + NULL, cmd3_cb, 0, NULL @@ -570,7 +694,7 @@ int main(int argc, char *argv[]) struct UCCmdDef cmd4 = { "delete file NAME", - "\n\nName of the file\n", + "delete\nfile\nName of the file\n", cmd4_cb, 0, NULL @@ -587,6 +711,12 @@ int main(int argc, char *argv[]) uc_cmd_run(&r, "show empty"); uc_cmd_run(&r, "sh emp b"); uc_cmd_run(&r, ""); + + do_help(&r, "delete file /tmp/a"); + do_help(&r, "show empty"); + do_help(&r, "show all"); + do_help(&r, "sh"); + do_help(&r, ""); /* while (fgets(line,sizeof line, stdin)) { enum UCCmdRc rc = uc_cmd_run(&r, line); @@ -596,9 +726,13 @@ int main(int argc, char *argv[]) } */ - uc_cmd_complete(&r, "sho em"); - uc_cmd_complete(&r, "show "); - uc_cmd_complete(&r, ""); + do_complete(&r, "sho em"); + do_complete(&r, "show "); + do_complete(&r, ""); + do_complete(&r, "delet "); + do_complete(&r, "delete "); + do_complete(&r, "delete file"); + do_complete(&r, "delete fil"); uc_cmd_runner_destroy(&r);