Add support for lists

This commit is contained in:
Nils O. Selåsdal
2015-12-08 23:59:39 +01:00
parent 8adec5408e
commit d4619418f0
+61 -11
View File
@@ -65,8 +65,9 @@ struct UCCmdTokenizer {
int num_words;
};
static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd)
static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd, const char *delim)
{
t->num_words = 0;
t->cmd_copy = strdup(cmd);
if (t->cmd_copy == NULL) {
@@ -74,11 +75,15 @@ static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd)
return;
}
if (delim == NULL) {
delim = "\r\n\t\v ";
}
t->num_words = getfields(t->cmd_copy,
t->cmd_words,
ARRAY_SIZE(t->cmd_words),
1,
"\r\n\t\v ");
delim);
}
static void uc_cmd_tokenize_destroy(struct UCCmdTokenizer *t)
@@ -147,6 +152,37 @@ 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)
{
struct UCCmdNode *node = NULL;
if (word[0] == '(') { //list (foo|bar|baz)
struct UCCmdTokenizer t;
uc_cmd_tokenize(&t, word, "() |\t\v");
if (t.num_words > 0) {
struct UCCmdList *list = NULL;
for (int i = t.num_words - 1; i >= 0; i--) {
struct UCCmdList *l = calloc(1, sizeof *l);
l->word = strdup(t.cmd_words[i]);
l->next = list;
list = l;
}
node = uc_cmd_node_alloc(UC_CMD_LIST, list, 0);
}
uc_cmd_tokenize_destroy(&t);
} else {
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)
{
size_t word_len;
@@ -168,15 +204,13 @@ static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word)
assert(l->word);
while (l) {
if (strncmp(word, n->data, word_len) == 0) {
if (strncmp(word, l->word, word_len) == 0) {
return 1;
}
l = l->next;
}
return 0;
}
return 0;
}
@@ -187,16 +221,29 @@ static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word)
struct UCCmdNode **location = &parent->child;
assert(parent);
new_node = uc_cmd_node_create(word);
if (new_node == NULL) {
return NULL;
}
while (*location) {
struct UCCmdNode *n = *location;
if (new_node->type != UC_CMD_KEYWORD && n->type != UC_CMD_END) {
//we don't support combinations of keywords and variables
//at the same level.
uc_cmd_node_dealloc(new_node);
return NULL;
}
//TODO check for ambiguous word (e.g. add "short" when "shorter" already exists)
if (uc_cmd_node_match(n, word)) {
return *location;
}
location = &n->next;
}
new_node = uc_cmd_node_alloc(UC_CMD_KEYWORD, strdup(word), 0);
*location = new_node;
return new_node;
@@ -208,7 +255,7 @@ int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd)
struct UCCmdNode *new_node;
struct UCCmdTokenizer t;
uc_cmd_tokenize(&t, cmd->cmd);
uc_cmd_tokenize(&t, cmd->cmd, NULL);
if (t.num_words <= 0) {
uc_cmd_tokenize_destroy(&t);
return -1;
@@ -216,6 +263,9 @@ 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) {
return -2;
}
}
//TODO, check for duplicate command here.
@@ -242,7 +292,7 @@ enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line)
enum UCCmdRc rc = UC_CMDRC_ERROR;
//make a copy since getfields destroys the original string
uc_cmd_tokenize(&t, cmd_line);
uc_cmd_tokenize(&t, cmd_line, NULL);
if (t.num_words <= 0) {
uc_cmd_tokenize_destroy(&t);
return rc;
@@ -334,7 +384,7 @@ int main(int argc, char *argv[])
};
struct UCCmdDef cmd3 = {
"show empty bottles",
"show empty (bottle|glass)",
cmd3_cb,
0,
NULL
@@ -347,7 +397,7 @@ int main(int argc, char *argv[])
uc_cmd_run(&r, "show all");
uc_cmd_run(&r, "show empty");
uc_cmd_run(&r, "show empty bottles");
uc_cmd_run(&r, "show empty bottle");
/*
while (fgets(line,sizeof line, stdin)) {
enum UCCmdRc rc = uc_cmd_run(&r, line);