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
+60 -10
View File
@@ -65,8 +65,9 @@ struct UCCmdTokenizer {
int num_words; 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->num_words = 0;
t->cmd_copy = strdup(cmd); t->cmd_copy = strdup(cmd);
if (t->cmd_copy == NULL) { if (t->cmd_copy == NULL) {
@@ -74,11 +75,15 @@ static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd)
return; return;
} }
if (delim == NULL) {
delim = "\r\n\t\v ";
}
t->num_words = getfields(t->cmd_copy, t->num_words = getfields(t->cmd_copy,
t->cmd_words, t->cmd_words,
ARRAY_SIZE(t->cmd_words), ARRAY_SIZE(t->cmd_words),
1, 1,
"\r\n\t\v "); delim);
} }
static void uc_cmd_tokenize_destroy(struct UCCmdTokenizer *t) 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; 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) static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word)
{ {
size_t word_len; size_t word_len;
@@ -168,15 +204,13 @@ static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word)
assert(l->word); assert(l->word);
while (l) { while (l) {
if (strncmp(word, n->data, word_len) == 0) { if (strncmp(word, l->word, word_len) == 0) {
return 1; return 1;
} }
l = l->next; l = l->next;
} }
return 0;
} }
return 0; return 0;
} }
@@ -188,15 +222,28 @@ static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word)
assert(parent); assert(parent);
new_node = uc_cmd_node_create(word);
if (new_node == NULL) {
return NULL;
}
while (*location) { while (*location) {
struct UCCmdNode *n = *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)) { if (uc_cmd_node_match(n, word)) {
return *location; return *location;
} }
location = &n->next; location = &n->next;
} }
new_node = uc_cmd_node_alloc(UC_CMD_KEYWORD, strdup(word), 0);
*location = new_node; *location = new_node;
return new_node; return new_node;
@@ -208,7 +255,7 @@ int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd)
struct UCCmdNode *new_node; struct UCCmdNode *new_node;
struct UCCmdTokenizer t; struct UCCmdTokenizer t;
uc_cmd_tokenize(&t, cmd->cmd); uc_cmd_tokenize(&t, cmd->cmd, NULL);
if (t.num_words <= 0) { if (t.num_words <= 0) {
uc_cmd_tokenize_destroy(&t); uc_cmd_tokenize_destroy(&t);
return -1; 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++) { 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) {
return -2;
}
} }
//TODO, check for duplicate command here. //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; enum UCCmdRc rc = UC_CMDRC_ERROR;
//make a copy since getfields destroys the original string //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) { if (t.num_words <= 0) {
uc_cmd_tokenize_destroy(&t); uc_cmd_tokenize_destroy(&t);
return rc; return rc;
@@ -334,7 +384,7 @@ int main(int argc, char *argv[])
}; };
struct UCCmdDef cmd3 = { struct UCCmdDef cmd3 = {
"show empty bottles", "show empty (bottle|glass)",
cmd3_cb, cmd3_cb,
0, 0,
NULL NULL
@@ -347,7 +397,7 @@ int main(int argc, char *argv[])
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, "show empty bottles"); uc_cmd_run(&r, "show empty bottle");
/* /*
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);