Implement completion

This commit is contained in:
Nils O. Selåsdal
2015-12-10 22:47:41 +01:00
parent db78689015
commit e902b04996
+215 -73
View File
@@ -7,24 +7,24 @@
#include "ucore/string.h" #include "ucore/string.h"
#include "ucore/strvec.h" #include "ucore/strvec.h"
#include "ucore/utils.h" #include "ucore/utils.h"
#include "ucore/backtrace.h"
//max words in a single command //
#define UC_CMD_MAX_WORDS 32
enum UCCmdRc { enum UCCmdRc {
/**Executed ok */
UC_CMDRC_OK, UC_CMDRC_OK,
/**Executed with warning */ /**Executed with warning */
UC_CMDRC_WARNING, UC_CMDRC_WARNING,
/*Not executed, incomplete command */ /*incomplete command */
UC_CMDRC_INCOMPLETE, UC_CMDRC_INCOMPLETE,
/*Command cann't uniquely be distinguished */ /*Command cann't uniquely be distinguished */
UC_CMDRC_AMBIGUOUS, UC_CMDRC_AMBIGUOUS,
/* Command doesn't exist*/
UC_CMDRC_NOT_FOUND,
/*Not executed, generic error */ /*Not executed, generic error */
UC_CMDRC_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 { enum UCCmdType {
UC_CMD_ROOT = 1, UC_CMD_ROOT = 1,
@@ -34,6 +34,26 @@ enum UCCmdType {
UC_CMD_END, 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 { struct UCCmdNode {
enum UCCmdType type; enum UCCmdType type;
uint32_t flags; uint32_t flags;
@@ -62,12 +82,27 @@ struct UCCmdDef {
}; };
//max words in a single command
#define UC_CMD_MAX_WORDS 32
struct UCCmdTokenizer { struct UCCmdTokenizer {
char *cmd_copy; char *cmd_copy;
char *cmd_words[UC_CMD_MAX_WORDS]; char *cmd_words[UC_CMD_MAX_WORDS];
int num_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) 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); delim);
} }
static void uc_cmd_tokenize_destroy(struct UCCmdTokenizer *t) static void
uc_cmd_tokenize_destroy(struct UCCmdTokenizer *t)
{ {
free(t->cmd_copy); free(t->cmd_copy);
t->num_words = 0; t->num_words = 0;
t->cmd_copy = NULL; 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); 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_KEYWORD;
} }
void uc_cmd_node_dealloc(struct UCCmdNode *n) void
uc_cmd_node_dealloc(struct UCCmdNode *n)
{ {
struct UCCmdNode *next; struct UCCmdNode *next;
while (n) { 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); uc_cmd_node_dealloc(r->root_node->child);
r->root_node->child = NULL; r->root_node->child = NULL;
} }
static struct UCCmdNode *
uc_cmd_node_alloc(enum UCCmdType type, void *data, uint32_t flags)
struct UCCmdNode *uc_cmd_node_alloc(enum UCCmdType type, void *data, uint32_t flags)
{ {
struct UCCmdNode *n; struct UCCmdNode *n;
@@ -146,7 +185,8 @@ struct UCCmdNode *uc_cmd_node_alloc(enum UCCmdType type, void *data, uint32_t fl
return n; 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; 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); node = uc_cmd_node_alloc(UC_CMD_KEYWORD, strdup(word), 0);
} }
return node; 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; size_t word_len;
int found = 0;
if (word == NULL || word[0] == 0) { if (word == NULL || word[0] == 0) {
return 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 (n->type == UC_CMD_KEYWORD) {
if (strncmp(word, n->data, word_len) == 0) { 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) { } else if (n->type == UC_CMD_LIST) {
struct UCStrv *v = n->data; 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) { if (args != NULL) {
uc_strv_append(args, v->strings[i]); uc_strv_append(args, v->strings[i]);
} }
return 1; found = 1;
} }
} }
} else if (n->type == UC_CMD_STRING) { } else if (n->type == UC_CMD_STRING) {
if (args != NULL) { if (args != NULL) {
uc_strv_append(args, word); 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 *new_node;
struct UCCmdNode **iter = &parent->child; struct UCCmdNode **iter = &parent->child;
@@ -229,11 +273,10 @@ static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word)
return NULL; return NULL;
} }
while (*iter) { while (*iter) {
struct UCCmdNode *n = *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 //we don't support combinations of keywords and variables
//at the same level. //at the same level.
uc_cmd_node_dealloc(new_node); 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; 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 *node = r->root_node;
struct UCCmdNode *new_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++) { for (int i = 0; i < t.num_words; i++) {
node = uc_cmd_add(node, t.cmd_words[i]); node = uc_cmd_add(node, t.cmd_words[i]);
if (node == NULL) { if (node == NULL) {
uc_cmd_tokenize_destroy(&t);
return -2; 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); new_node = uc_cmd_node_alloc(UC_CMD_END, cmd, 0);
if (new_node == NULL) { if (new_node == NULL) {
uc_cmd_tokenize_destroy(&t);
return -1; return -1;
} }
new_node->next = node->child; new_node->next = node->child;
@@ -286,33 +332,36 @@ int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd)
return 0; return 0;
} }
int
enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line) 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; return rc;
struct UCCmdTokenizer t; }
enum UCCmdRc rc = UC_CMDRC_ERROR;
struct UCStrv args = UC_STRV_INITIALIZER; //arguments to the callback function
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;
for (int i = 0; i < t->num_words && node; i++) {
//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++) {
int n_found = 0; int n_found = 0;
struct UCCmdNode *found = NULL; 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)) {
found = node; if (n_found == 0) {
//keep the 1. matched
found = node;
}
n_found++; n_found++;
} }
node = node->next; node = node->next;
@@ -320,31 +369,118 @@ enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line)
if (n_found == 1) { if (n_found == 1) {
node = found->child; node = found->child;
rc = UC_CMDRC_OK;
} else if (n_found > 1) { } else if (n_found > 1) {
uc_strv_destroy(&args); node = found;
uc_cmd_tokenize_destroy(&t); rc = UC_CMDRC_AMBIGUOUS;
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; break;
} }
node = node->next;
} }
if (node != NULL) { *result = node;
struct UCCmdDef *cmd = node->data;
rc = cmd->callback(&args); 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, "<CR>");
} 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 { } 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_strv_destroy(&args.argv);
uc_cmd_tokenize_destroy(&t);
return rc; 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) void print_args(const struct UCStrv *args)
{ {
for (size_t i = 0; i < args->cnt; i++) { 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__); puts(__func__);
print_args(args); print_args(&args->argv);
return UC_CMDRC_OK; return UC_CMDRC_OK;
} }
enum UCCmdRc cmd2_cb(const struct UCStrv *args) enum UCCmdRc cmd2_cb(const struct UCCmdArgs *args)
{ {
puts(__func__); puts(__func__);
print_args(args); print_args(&args->argv);
return UC_CMDRC_OK; return UC_CMDRC_OK;
} }
enum UCCmdRc cmd3_cb(const struct UCStrv *args) enum UCCmdRc cmd3_cb(const struct UCCmdArgs *args)
{ {
puts(__func__); puts(__func__);
print_args(args); print_args(&args->argv);
return UC_CMDRC_OK; return UC_CMDRC_OK;
} }
enum UCCmdRc cmd4_cb(const struct UCStrv *args) enum UCCmdRc cmd4_cb(const struct UCCmdArgs *args)
{ {
puts(__func__); puts(__func__);
print_args(args); print_args(&args->argv);
return UC_CMDRC_OK; return UC_CMDRC_OK;
} }
@@ -411,7 +549,7 @@ int main(int argc, char *argv[])
}; };
struct UCCmdDef cmd3 = { struct UCCmdDef cmd3 = {
"show empty (bottle|glass)", "show empty bottle",
cmd3_cb, cmd3_cb,
0, 0,
NULL NULL
@@ -433,7 +571,8 @@ int main(int argc, char *argv[])
uc_cmd_run(&r, "delete file /tmp/a"); uc_cmd_run(&r, "delete file /tmp/a");
uc_cmd_run(&r, "show all"); uc_cmd_run(&r, "show all");
uc_cmd_run(&r, "show empty"); 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)) { while (fgets(line,sizeof line, stdin)) {
enum UCCmdRc rc = uc_cmd_run(&r, line); 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); uc_cmd_runner_destroy(&r);