IMplement help system. Though it need refactoring.
This commit is contained in:
+167
-33
@@ -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, "<CR>");
|
||||
uc_strv_append(completions, "<CR>");
|
||||
} 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);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user