IMplement help system. Though it need refactoring.
This commit is contained in:
+168
-34
@@ -68,11 +68,12 @@ struct UCCmdNode {
|
|||||||
union {
|
union {
|
||||||
struct UCCmdWord word;
|
struct UCCmdWord word;
|
||||||
struct UCCmdList list;
|
struct UCCmdList list;
|
||||||
struct UCCmdDef *cmd;
|
const struct UCCmdDef *cmd;
|
||||||
} data;
|
} data;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
struct UCCmdNode *next; //next at same level
|
struct UCCmdNode *next; //next at same level
|
||||||
struct UCCmdNode *child;
|
struct UCCmdNode *child;
|
||||||
|
struct UCCmdNode *parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UCCmdRunner {
|
struct UCCmdRunner {
|
||||||
@@ -99,17 +100,21 @@ struct UCCmdTokenizer {
|
|||||||
|
|
||||||
struct UCCmdArgs {
|
struct UCCmdArgs {
|
||||||
struct UCStrv argv;
|
struct UCStrv argv;
|
||||||
struct UCCmdDef *cmd;
|
const struct UCCmdDef *cmd;
|
||||||
};
|
};
|
||||||
|
|
||||||
void print_args(const struct UCStrv *args);
|
void
|
||||||
static int uc_cmd_type_is_variable(enum UCCmdType type)
|
print_args(const struct UCStrv *args);
|
||||||
|
|
||||||
|
static int
|
||||||
|
uc_cmd_type_is_variable(enum UCCmdType type)
|
||||||
{
|
{
|
||||||
return type == UC_CMD_LIST ||
|
return type == UC_CMD_LIST ||
|
||||||
type == UC_CMD_STRING;
|
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;
|
t->num_words = 0;
|
||||||
@@ -144,13 +149,14 @@ uc_cmd_runner_init(struct UCCmdRunner *r)
|
|||||||
{
|
{
|
||||||
memset(r, 0, sizeof *r);
|
memset(r, 0, sizeof *r);
|
||||||
r->root_node = &r->_root;
|
r->root_node = &r->_root;
|
||||||
r->_root.type = UC_CMD_KEYWORD;
|
r->_root.type = UC_CMD_ROOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
uc_cmd_node_dealloc(struct UCCmdNode *n)
|
uc_cmd_node_dealloc(struct UCCmdNode *n)
|
||||||
{
|
{
|
||||||
struct UCCmdNode *next;
|
struct UCCmdNode *next;
|
||||||
|
|
||||||
while (n) {
|
while (n) {
|
||||||
uc_cmd_node_dealloc(n->child);
|
uc_cmd_node_dealloc(n->child);
|
||||||
next = n->next;
|
next = n->next;
|
||||||
@@ -281,6 +287,7 @@ uc_cmd_add(struct UCCmdNode *parent, const char *word)
|
|||||||
if (new_node == NULL) {
|
if (new_node == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
new_node->parent = parent;
|
||||||
|
|
||||||
while (*iter) {
|
while (*iter) {
|
||||||
struct UCCmdNode *n = *iter;
|
struct UCCmdNode *n = *iter;
|
||||||
@@ -304,12 +311,80 @@ uc_cmd_add(struct UCCmdNode *parent, const char *word)
|
|||||||
return new_node;
|
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
|
int
|
||||||
uc_cmd_install_noassert(struct UCCmdRunner *r, struct UCCmdDef *cmd)
|
uc_cmd_install_noassert(struct UCCmdRunner *r, struct UCCmdDef *cmd)
|
||||||
{
|
{
|
||||||
struct UCCmdNode *node = r->root_node;
|
struct UCCmdNode *node = r->root_node;
|
||||||
struct UCCmdNode *new_node;
|
struct UCCmdNode *new_node;
|
||||||
struct UCCmdTokenizer t;
|
struct UCCmdTokenizer t;
|
||||||
|
int rc;
|
||||||
|
|
||||||
uc_cmd_tokenize(&t, cmd->cmd, NULL);
|
uc_cmd_tokenize(&t, cmd->cmd, NULL);
|
||||||
if (t.num_words <= 0) {
|
if (t.num_words <= 0) {
|
||||||
@@ -333,13 +408,15 @@ uc_cmd_install_noassert(struct UCCmdRunner *r, struct UCCmdDef *cmd)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
new_node->data.cmd = cmd;
|
new_node->data.cmd = cmd;
|
||||||
new_node->next = node->child;
|
new_node->next = node->child;
|
||||||
node->child = new_node;
|
node->child = new_node;
|
||||||
|
new_node->parent = node;
|
||||||
|
|
||||||
|
rc = uc_cmd_node_add_help(new_node);
|
||||||
|
|
||||||
uc_cmd_tokenize_destroy(&t);
|
uc_cmd_tokenize_destroy(&t);
|
||||||
|
|
||||||
return 0;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -360,11 +437,11 @@ uc_cmd_find(struct UCCmdNode *first,
|
|||||||
struct UCCmdNode **result)
|
struct UCCmdNode **result)
|
||||||
{
|
{
|
||||||
struct UCCmdNode *node = first;
|
struct UCCmdNode *node = first;
|
||||||
|
struct UCCmdNode *found = NULL;
|
||||||
enum UCCmdRc rc = UC_CMDRC_NOT_FOUND;
|
enum UCCmdRc rc = UC_CMDRC_NOT_FOUND;
|
||||||
|
|
||||||
for (int i = 0; i < t->num_words && node; i++) {
|
for (int i = 0; i < t->num_words && node; i++) {
|
||||||
int n_found = 0;
|
int n_found = 0;
|
||||||
struct UCCmdNode *found = NULL;
|
|
||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
if (uc_cmd_node_match(node, t->cmd_words[i], args)) {
|
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;
|
rc = UC_CMDRC_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,42 +500,57 @@ uc_cmd_parse_and_find(struct UCCmdRunner *r, const char *cmd_line,
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum UCCmdRc
|
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;
|
struct UCCmdNode *found = NULL;
|
||||||
enum UCCmdRc rc;
|
enum UCCmdRc rc;
|
||||||
|
|
||||||
rc = uc_cmd_parse_and_find(r, cmd_line, NULL, &found);
|
rc = uc_cmd_parse_and_find(r, cmd_line, NULL, &found);
|
||||||
if (rc != UC_CMDRC_NOT_FOUND && found != NULL) {
|
if (rc != UC_CMDRC_NOT_FOUND && found != NULL) {
|
||||||
struct UCCmdNode *node = found;
|
struct UCCmdNode *node = found->child;
|
||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
if (node->type == UC_CMD_END) {
|
if (node->type == UC_CMD_END) {
|
||||||
uc_strv_append(&completions, "<CR>");
|
uc_strv_append(completions, "<CR>");
|
||||||
} else if (node->type == UC_CMD_LIST) {
|
} 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 ||
|
} else if (node->type == UC_CMD_STRING ||
|
||||||
node->type == UC_CMD_KEYWORD) {
|
node->type == UC_CMD_KEYWORD) {
|
||||||
uc_strv_append(&completions, node->data.word.word);
|
uc_strv_append(completions, node->data.word.word);
|
||||||
}
|
}
|
||||||
node = node->next;
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum UCCmdRc
|
enum UCCmdRc
|
||||||
uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line)
|
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);
|
rc = uc_cmd_parse_and_find(r, cmd_line, &args.argv, &found);
|
||||||
if (rc == UC_CMDRC_OK) {
|
if (rc == UC_CMDRC_OK) {
|
||||||
|
found = found->child;
|
||||||
// Now check the current level for any UC_CMD_END
|
// Now check the current level for any UC_CMD_END
|
||||||
// which indicates a complete command
|
// which indicates a complete command
|
||||||
while (found) {
|
while (found) {
|
||||||
@@ -482,7 +575,7 @@ uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (found != NULL) {
|
if (found != NULL) {
|
||||||
struct UCCmdDef *cmd = found->data.cmd;
|
const struct UCCmdDef *cmd = found->data.cmd;
|
||||||
args.cmd = cmd;
|
args.cmd = cmd;
|
||||||
rc = cmd->callback(&args);
|
rc = cmd->callback(&args);
|
||||||
} else {
|
} else {
|
||||||
@@ -536,8 +629,39 @@ enum UCCmdRc cmd4_cb(const struct UCCmdArgs *args)
|
|||||||
return UC_CMDRC_OK;
|
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[])
|
int main(int argc, char *argv[])
|
||||||
@@ -546,7 +670,7 @@ int main(int argc, char *argv[])
|
|||||||
struct UCCmdRunner r;
|
struct UCCmdRunner r;
|
||||||
struct UCCmdDef cmd1 = {
|
struct UCCmdDef cmd1 = {
|
||||||
"show all",
|
"show all",
|
||||||
"\n\n",
|
" \nEverything\n",
|
||||||
cmd1_cb,
|
cmd1_cb,
|
||||||
0,
|
0,
|
||||||
NULL
|
NULL
|
||||||
@@ -554,7 +678,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
struct UCCmdDef cmd2 = {
|
struct UCCmdDef cmd2 = {
|
||||||
"show empty",
|
"show empty",
|
||||||
"\n\n",
|
"Show parameter\nAll empty\n",
|
||||||
cmd2_cb,
|
cmd2_cb,
|
||||||
0,
|
0,
|
||||||
NULL
|
NULL
|
||||||
@@ -562,7 +686,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
struct UCCmdDef cmd3 = {
|
struct UCCmdDef cmd3 = {
|
||||||
"show empty bottle",
|
"show empty bottle",
|
||||||
"\n\n\n",
|
NULL,
|
||||||
cmd3_cb,
|
cmd3_cb,
|
||||||
0,
|
0,
|
||||||
NULL
|
NULL
|
||||||
@@ -570,7 +694,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
struct UCCmdDef cmd4 = {
|
struct UCCmdDef cmd4 = {
|
||||||
"delete file NAME",
|
"delete file NAME",
|
||||||
"\n\nName of the file\n",
|
"delete\nfile\nName of the file\n",
|
||||||
cmd4_cb,
|
cmd4_cb,
|
||||||
0,
|
0,
|
||||||
NULL
|
NULL
|
||||||
@@ -587,6 +711,12 @@ int main(int argc, char *argv[])
|
|||||||
uc_cmd_run(&r, "show empty");
|
uc_cmd_run(&r, "show empty");
|
||||||
uc_cmd_run(&r, "sh emp b");
|
uc_cmd_run(&r, "sh emp b");
|
||||||
uc_cmd_run(&r, "");
|
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)) {
|
while (fgets(line,sizeof line, stdin)) {
|
||||||
enum UCCmdRc rc = uc_cmd_run(&r, line);
|
enum UCCmdRc rc = uc_cmd_run(&r, line);
|
||||||
@@ -596,9 +726,13 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
uc_cmd_complete(&r, "sho em");
|
do_complete(&r, "sho em");
|
||||||
uc_cmd_complete(&r, "show ");
|
do_complete(&r, "show ");
|
||||||
uc_cmd_complete(&r, "");
|
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);
|
uc_cmd_runner_destroy(&r);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user