Progress on cmd
This commit is contained in:
+339
-29
@@ -1,54 +1,364 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "ucore/string.h"
|
||||||
|
#include "ucore/utils.h"
|
||||||
|
|
||||||
|
//max words in a single command
|
||||||
|
#define UC_CMD_MAX_WORDS 32
|
||||||
|
|
||||||
enum UCCmdRc {
|
enum UCCmdRc {
|
||||||
/**Executed ok */
|
/**Executed ok */
|
||||||
UC_CMD_OK,
|
UC_CMDRC_OK,
|
||||||
/**Executed with warning */
|
/**Executed with warning */
|
||||||
UC_CMD_WARNING,
|
UC_CMDRC_WARNING,
|
||||||
/*Not executed, incomplete command */
|
/*Not executed, incomplete command */
|
||||||
UC_CMD_INCOMPLETE,
|
UC_CMDRC_INCOMPLETE,
|
||||||
|
/*Command cann't uniquely be distinguished */
|
||||||
|
UC_CMDRC_AMBIGUOUS,
|
||||||
/*Not executed, generic error */
|
/*Not executed, generic error */
|
||||||
UC_CMD_ERROR,
|
UC_CMDRC_ERROR,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum UCCmdRc (*uc_cmd_handler)(int narg, char *args[]);
|
typedef enum UCCmdRc (*uc_cmd_handler)(int narg, char *args[]);
|
||||||
typedef void (*uc_cmd_write_cfg)(void);
|
|
||||||
|
|
||||||
#define UC_CMD_FL_HIDDEN (1 << 0)
|
enum UCCmdType {
|
||||||
|
UC_CMD_ROOT = 1,
|
||||||
struct UCCmdNode;
|
UC_CMD_KEYWORD,
|
||||||
struct UCCmdDef;
|
UC_CMD_LIST,
|
||||||
|
UC_CMD_END,
|
||||||
struct UCCmdSettings {
|
|
||||||
const char *name;
|
|
||||||
const char *version;
|
|
||||||
const char *boot_cfg_file;
|
|
||||||
|
|
||||||
struct UCCmdNode *initial_node;
|
|
||||||
struct UCCmdNode *node_head;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UCCmdNode {
|
struct UCCmdNode {
|
||||||
int id;
|
enum UCCmdType type;
|
||||||
uc_cmd_write_cfg write_func;
|
uint32_t flags;
|
||||||
struct UCCmdDef *cmd_head; //linked list of UCCmdDef
|
void *data;
|
||||||
struct UCCmdNode *next;
|
struct UCCmdNode *next; //next at same level
|
||||||
|
struct UCCmdNode *child;
|
||||||
|
};
|
||||||
|
|
||||||
|
//for UC_CMD_LIST,
|
||||||
|
struct UCCmdList {
|
||||||
|
char *word;
|
||||||
|
struct UCCmdList *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UCCmdRunner {
|
||||||
|
struct UCCmdNode *root_node;
|
||||||
|
struct UCCmdNode _root;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UCCmdDef {
|
struct UCCmdDef {
|
||||||
const char *cmd;
|
const char *cmd;
|
||||||
const char **help;
|
uc_cmd_handler callback;
|
||||||
size_t cmd_len;
|
|
||||||
uc_cmd_handler cb;
|
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
struct UCCmdDef *next;
|
struct UCCmdDef *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void uc_cmd_init(const char *app_name, const char *version);
|
struct UCCmdTokenizer {
|
||||||
int uc_cmd_read_config(const char *filename);
|
char *cmd_copy;
|
||||||
void uc_cmd_set_configfile(const char *filename);
|
char *cmd_words[UC_CMD_MAX_WORDS];
|
||||||
void uc_cmd_add_cmd(struct UCCmdDef *cmd, int parent_node_id);
|
int num_words;
|
||||||
void uc_cmd_set_default_node(int node_id);
|
};
|
||||||
|
|
||||||
|
static void uc_cmd_tokenize(struct UCCmdTokenizer *t, const char *cmd)
|
||||||
|
{
|
||||||
|
t->num_words = 0;
|
||||||
|
t->cmd_copy = strdup(cmd);
|
||||||
|
if (t->cmd_copy == NULL) {
|
||||||
|
assert(t->cmd_copy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
t->num_words = getfields(t->cmd_copy,
|
||||||
|
t->cmd_words,
|
||||||
|
ARRAY_SIZE(t->cmd_words),
|
||||||
|
1,
|
||||||
|
"\r\n\t\v ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uc_cmd_tokenize_destroy(struct UCCmdTokenizer *t)
|
||||||
|
{
|
||||||
|
free(t->cmd_copy);
|
||||||
|
t->num_words = 0;
|
||||||
|
t->cmd_copy = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_cmd_runner_init(struct UCCmdRunner *r)
|
||||||
|
{
|
||||||
|
memset(r, 0, sizeof *r);
|
||||||
|
r->root_node = &r->_root;
|
||||||
|
r->_root.type = UC_CMD_KEYWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uc_cmd_list_free(struct UCCmdList *l)
|
||||||
|
{
|
||||||
|
struct UCCmdList *next;
|
||||||
|
|
||||||
|
while (l) {
|
||||||
|
next = l->next;
|
||||||
|
free(l->word);
|
||||||
|
free(l);
|
||||||
|
l = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_cmd_node_dealloc(struct UCCmdNode *n)
|
||||||
|
{
|
||||||
|
struct UCCmdNode *next;
|
||||||
|
while (n) {
|
||||||
|
uc_cmd_node_dealloc(n->child);
|
||||||
|
next = n->next;
|
||||||
|
if (n->type == UC_CMD_KEYWORD) {
|
||||||
|
free(n->data);
|
||||||
|
} else if (n->type == UC_CMD_LIST) {
|
||||||
|
uc_cmd_list_free(n->data);
|
||||||
|
}
|
||||||
|
free(n);
|
||||||
|
n = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_cmd_runner_destroy(struct UCCmdRunner *r)
|
||||||
|
{
|
||||||
|
uc_cmd_node_dealloc(r->root_node->child);
|
||||||
|
r->root_node->child = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct UCCmdNode *uc_cmd_node_alloc(enum UCCmdType type, void *data, uint32_t flags)
|
||||||
|
{
|
||||||
|
struct UCCmdNode *n;
|
||||||
|
|
||||||
|
n = calloc(1, sizeof *n);
|
||||||
|
if (n == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->type = type;
|
||||||
|
n->data = data;
|
||||||
|
n->flags = flags;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uc_cmd_node_match(const struct UCCmdNode *n, const char *word)
|
||||||
|
{
|
||||||
|
size_t word_len;
|
||||||
|
|
||||||
|
if (word == NULL || word[0] == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
word_len = strlen(word);
|
||||||
|
|
||||||
|
if (n->type == UC_CMD_KEYWORD) {
|
||||||
|
return strncmp(word, n->data, word_len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->type == UC_CMD_LIST) {
|
||||||
|
struct UCCmdList *l = n->data;
|
||||||
|
|
||||||
|
assert(l);
|
||||||
|
assert(l->word);
|
||||||
|
|
||||||
|
while (l) {
|
||||||
|
if (strncmp(word, n->data, word_len) == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
l = l->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct UCCmdNode *uc_cmd_add(struct UCCmdNode *parent, const char *word)
|
||||||
|
{
|
||||||
|
struct UCCmdNode *new_node;
|
||||||
|
struct UCCmdNode **location = &parent->child;
|
||||||
|
|
||||||
|
assert(parent);
|
||||||
|
|
||||||
|
while (*location) {
|
||||||
|
struct UCCmdNode *n = *location;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_cmd_install(struct UCCmdRunner *r, struct UCCmdDef *cmd)
|
||||||
|
{
|
||||||
|
struct UCCmdNode *node = r->root_node;
|
||||||
|
struct UCCmdNode *new_node;
|
||||||
|
struct UCCmdTokenizer t;
|
||||||
|
|
||||||
|
uc_cmd_tokenize(&t, cmd->cmd);
|
||||||
|
if (t.num_words <= 0) {
|
||||||
|
uc_cmd_tokenize_destroy(&t);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < t.num_words; i++) {
|
||||||
|
node = uc_cmd_add(node, t.cmd_words[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO, check for duplicate command here.
|
||||||
|
|
||||||
|
new_node = uc_cmd_node_alloc(UC_CMD_END, cmd, 0);
|
||||||
|
if (new_node == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
new_node->next = node->child;
|
||||||
|
node->child = new_node;
|
||||||
|
|
||||||
|
|
||||||
|
uc_cmd_tokenize_destroy(&t);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum UCCmdRc uc_cmd_run(struct UCCmdRunner *r, const char *cmd_line)
|
||||||
|
{
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
struct UCCmdNode *found = NULL;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
if (uc_cmd_node_match(node, t.cmd_words[i])) {
|
||||||
|
found = node;
|
||||||
|
n_found++;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_found == 1) {
|
||||||
|
node = found->child;
|
||||||
|
} else if (n_found > 1) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node != NULL) {
|
||||||
|
struct UCCmdDef *cmd = node->data;
|
||||||
|
rc = cmd->callback(0, NULL);
|
||||||
|
} else {
|
||||||
|
rc = UC_CMDRC_INCOMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uc_cmd_tokenize_destroy(&t);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum UCCmdRc cmd1_cb(int narg, char *args[])
|
||||||
|
{
|
||||||
|
puts(__func__);
|
||||||
|
|
||||||
|
return UC_CMDRC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UCCmdRc cmd2_cb(int narg, char *args[])
|
||||||
|
{
|
||||||
|
puts(__func__);
|
||||||
|
|
||||||
|
return UC_CMDRC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UCCmdRc cmd3_cb(int narg, char *args[])
|
||||||
|
{
|
||||||
|
puts(__func__);
|
||||||
|
|
||||||
|
return UC_CMDRC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char line[128];
|
||||||
|
struct UCCmdRunner r;
|
||||||
|
struct UCCmdDef cmd1 = {
|
||||||
|
"show all",
|
||||||
|
cmd1_cb,
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UCCmdDef cmd2 = {
|
||||||
|
"show empty",
|
||||||
|
cmd2_cb,
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UCCmdDef cmd3 = {
|
||||||
|
"show empty bottles",
|
||||||
|
cmd3_cb,
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
uc_cmd_runner_init(&r);
|
||||||
|
uc_cmd_install(&r, &cmd1);
|
||||||
|
uc_cmd_install(&r, &cmd2);
|
||||||
|
uc_cmd_install(&r, &cmd3);
|
||||||
|
|
||||||
|
uc_cmd_run(&r, "show all");
|
||||||
|
uc_cmd_run(&r, "show empty");
|
||||||
|
uc_cmd_run(&r, "show empty bottles");
|
||||||
|
/*
|
||||||
|
while (fgets(line,sizeof line, stdin)) {
|
||||||
|
enum UCCmdRc rc = uc_cmd_run(&r, line);
|
||||||
|
if (rc != UC_CMDRC_OK) {
|
||||||
|
printf("Command failed with code %d\n", rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
uc_cmd_runner_destroy(&r);
|
||||||
|
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user