From: Olivier Matz Date: Sat, 25 Dec 2010 18:36:01 +0000 (+0100) Subject: cmdline: big rework and clean of cmdline library X-Git-Url: http://git.droids-corp.org/?p=libcmdline.git;a=commitdiff_plain;h=f32d30f48ddfb55b86efd695e0dd144e9d15401e cmdline: big rework and clean of cmdline library The main change is that we use the same structure to define a token. The old method (parse, complete_get_nb, complete_get_elt, get_help) is deprecated. We now have (parse, complete_start, complete_iterate, complete_end, help) that will allow to do more complex tokens, like a file token with completion. Signed-off-by: Olivier Matz --- diff --git a/Makefile b/Makefile index 450ab25..9656186 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ SUBDIRS += genconf # really small devices, because it changes the API. # CFLAGS += -DNO_RDLINE_HISTORY # CFLAGS += -DNO_RDLINE_KILL_BUF -# CFLAGS += -DCMDLINE_HAVE_FLOAT +CFLAGS += -DCMDLINE_HAVE_FLOAT CFLAGS += -DCMDLINE_HAVE_SOCKET CFLAGS += -Wall -Werror diff --git a/TODO.txt b/TODO.txt index 2e8fe54..dbdb2a1 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,7 +1,95 @@ -- token file/path +- token file/path: pour ca il faut changer la logique get_nb/complete + pour se rapprocher du opendir/readdir - libevent example - better example than calculator - check all XXX in code - set prompt example - too much possible completion - token string entre guillemets ? +- cmdline_parse() n'a pas besoin de retourner tokenlen +- les help() des tokens (string par ex) ne retournent pas "faux" en cas d'erreur +- on pourrait virer la valeur de retour du start() en reorganisant + cmdline_parse() et cmdline_help() +- genconf: completion des xpath // +- supprimer tous les basic_char_loop() + +--------------- + +buf->linebuf + +- rdline: API peut rester comme ca + doit pouvoir etre utiisé seule + il fuat un example + +- parse: + doit aussi pouvoir etre utilisé seule + il faut virer les references a cmdline/rdline + +/* opaque is given to the token callback func */ +int cmdline_parse(parse_ctx, const char *buf, void *opaque); + + +cmdline_complete_init(cmplt_ctx) + +/** + * complete() must be called with an initialized context + * with *state==-1 (just display choices), then called until it + * returns CMDLINE_PARSE_COMPLETED_BUFFER or + * CMDLINE_PARSE_COMPLETE_FINISHED. + * + * It returns < 0 on error. + * + * Else it returns: + * - CMDLINE_PARSE_COMPLETED_BUFFER on completion (one possible + * choice). In this case, the chars are appended in dst buffer. + * - CMDLINE_PARSE_COMPLETE_AGAIN if there is several possible + * choices. In this case, you must call the function again with + * the same context + * - CMDLINE_PARSE_COMPLETE_FINISHED when the iteration is + * finished. The dst is not valid for this last call. + * + * The returned dst buf ends with \0. + */ +int cmdline_complete(parse_ctx, const char *buf, cmplt_ctx, + char *dst, unsigned int size); + + +- cmdline est un assemblage de rdline et parse + permet de parser des fichiers, de faire un prompt sur stdin, network, unix + +tests unitaires + + +-------------- + +completions possibles + +toto +toti +titi +coin + +"" -> all (help) +complete = "toto", complete_len = 4 +complete = "tot", complete_len = 3 +complete = "t", complete_len = 1 +complete = "", complete_len = 0 +n_completions = 4 + + +"t" -> "toto" "toti" "titi" (help) +toklen = 1 +complete = "toto", complete_len = 4 +complete = "tot", complete_len = 3 +complete = "t", complete_len = 1 +n_completions = 3 + +"to" -> "tot" (complete, not finished) +toklen = 2 +complete = "toto", complete_len = 4 +complete = "tot", complete_len = 3 +n_completions = 2 + +"ti" -> "titi " (complete, finished) +n_completions = 1 + diff --git a/build/calculator_standalone/Makefile b/build/calculator_standalone/Makefile index 523a8a8..53383be 100755 --- a/build/calculator_standalone/Makefile +++ b/build/calculator_standalone/Makefile @@ -2,7 +2,7 @@ SRC = main.c commands.c OBJS = $(SRC:%.c=%.o) -PROG = server +PROG = standalone LDLIB = ../lib/libcmdline.a all: $(PROG) diff --git a/src/calculator_server/commands.c b/src/calculator_server/commands.c index be00d5a..93c11d6 100644 --- a/src/calculator_server/commands.c +++ b/src/calculator_server/commands.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -164,10 +165,13 @@ cmdline_parse_inst_t cmd_help = { /****** CONTEXT (list of instruction) */ /* in progmem */ -cmdline_parse_ctx_t main_ctx[] = { - (cmdline_parse_inst_t *)&cmd_float, - (cmdline_parse_inst_t *)&cmd_trigo, - (cmdline_parse_inst_t *)&cmd_help, - NULL, +cmdline_parse_ctx_t main_ctx = { + .name = "main", + .insts = { + &cmd_float, + &cmd_trigo, + &cmd_help, + NULL, + }, }; diff --git a/src/calculator_standalone/commands.c b/src/calculator_standalone/commands.c index be00d5a..93c11d6 100644 --- a/src/calculator_standalone/commands.c +++ b/src/calculator_standalone/commands.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -164,10 +165,13 @@ cmdline_parse_inst_t cmd_help = { /****** CONTEXT (list of instruction) */ /* in progmem */ -cmdline_parse_ctx_t main_ctx[] = { - (cmdline_parse_inst_t *)&cmd_float, - (cmdline_parse_inst_t *)&cmd_trigo, - (cmdline_parse_inst_t *)&cmd_help, - NULL, +cmdline_parse_ctx_t main_ctx = { + .name = "main", + .insts = { + &cmd_float, + &cmd_trigo, + &cmd_help, + NULL, + }, }; diff --git a/src/extension_example/commands.c b/src/extension_example/commands.c index fbef8c9..38b25c7 100644 --- a/src/extension_example/commands.c +++ b/src/extension_example/commands.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -146,9 +147,12 @@ cmdline_parse_inst_t cmd_obj_add = { /****** CONTEXT (list of instruction) */ /* in progmem */ -cmdline_parse_ctx_t main_ctx[] = { - (cmdline_parse_inst_t *)&cmd_obj_del_show, - (cmdline_parse_inst_t *)&cmd_obj_add, - NULL, +cmdline_parse_ctx_t main_ctx = { + .name = "main", + .insts = { + &cmd_obj_del_show, + &cmd_obj_add, + NULL, + }, }; diff --git a/src/extension_example/main.c b/src/extension_example/main.c index 9cf1371..ed8c871 100644 --- a/src/extension_example/main.c +++ b/src/extension_example/main.c @@ -30,6 +30,7 @@ #include #include #include +#include #include diff --git a/src/extension_example/parse_obj_list.c b/src/extension_example/parse_obj_list.c index aa743cf..d561519 100644 --- a/src/extension_example/parse_obj_list.c +++ b/src/extension_example/parse_obj_list.c @@ -37,16 +37,10 @@ #include "parse_obj_list.h" /* This file is an example of extension of libcmdline. It provides an - * example of objects stored in a list. */ + * example of named objects stored in a list, supporting the + * completion on objects name */ -struct cmdline_token_ops token_obj_list_ops = { - .parse = parse_obj_list, - .complete_get_nb = complete_get_nb_obj_list, - .complete_get_elt = complete_get_elt_obj_list, - .get_help = get_help_obj_list, -}; - -int +static int parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, unsigned ressize) { @@ -65,7 +59,7 @@ parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, continue; break; } - if (!o) /* not found */ + if (o == NULL) /* not found */ return -1; /* store the address of object in structure */ @@ -75,45 +69,54 @@ parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, return token_len; } -int complete_get_nb_obj_list(cmdline_parse_token_hdr_t *tk) +static int +complete_obj_list_start(cmdline_parse_token_hdr_t *tk, void **opaque) { struct token_obj_list *tk2 = (struct token_obj_list *)tk; struct token_obj_list_data *tkd = &tk2->obj_list_data; struct object *o; - int ret = 0; - SLIST_FOREACH(o, tkd->list, next) { - ret ++; - } - return ret; + o = SLIST_FIRST(tkd->list); + *opaque = (void *)o; + if (o == NULL) + return -1; /* no completion */ + return 0; } -int complete_get_elt_obj_list(cmdline_parse_token_hdr_t *tk, int idx, - char *dstbuf, unsigned int size) +static int +complete_obj_list_iterate(cmdline_parse_token_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) { - struct token_obj_list *tk2 = (struct token_obj_list *)tk; - struct token_obj_list_data *tkd = &tk2->obj_list_data; struct object *o; - unsigned int i = 0, len; + int len; - SLIST_FOREACH(o, tkd->list, next) { - if (i++ == idx) - break; - } - if (!o) + o = *opaque; + if (o == NULL) return -1; - len = strlen(o->name) + 1; - if (len > size) + len = snprintf(dstbuf, size, "%s", o->name); + if (len < 0 || len >= size) return -1; strcpy(dstbuf, o->name); + o = SLIST_NEXT(o, next); + *opaque = o; return 0; } -int get_help_obj_list(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size) +static int +cmdline_help_obj_list(cmdline_parse_token_hdr_t *tk, char *dstbuf, + unsigned int size) { snprintf(dstbuf, size, "Obj-List"); return 0; } + +struct cmdline_token_ops token_obj_list_ops = { + .parse = parse_obj_list, + .complete_start = complete_obj_list_start, + .complete_iterate = complete_obj_list_iterate, + .complete_end = NULL, + .help = cmdline_help_obj_list, +}; diff --git a/src/extension_example/parse_obj_list.h b/src/extension_example/parse_obj_list.h index 3bde83e..0148520 100644 --- a/src/extension_example/parse_obj_list.h +++ b/src/extension_example/parse_obj_list.h @@ -56,13 +56,6 @@ typedef struct token_obj_list parse_token_obj_list_t; extern struct cmdline_token_ops token_obj_list_ops; -int parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *srcbuf, - void *res, unsigned ressize); -int complete_get_nb_obj_list(cmdline_parse_token_hdr_t *tk); -int complete_get_elt_obj_list(cmdline_parse_token_hdr_t *tk, int idx, - char *dstbuf, unsigned int size); -int get_help_obj_list(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size); - #define TOKEN_OBJ_LIST_INITIALIZER(structure, field, obj_list_ptr) \ { \ .hdr = { \ diff --git a/src/genconf/commands.c b/src/genconf/commands.c index 260502c..957bbef 100644 --- a/src/genconf/commands.c +++ b/src/genconf/commands.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -407,22 +408,24 @@ struct cmd_set_str_result { static int get_str(struct cmdline *cl, char *buf, int len) { struct rdline rl; - int ret = 0, n; - char c; + int ret = 0; + int c; char *s; buf[0] = '\0'; - rdline_init(&rl, cmdline_write_char, NULL, NULL); + rdline_init(&rl, cl->s_in, cl->s_out, NULL, NULL, NULL); rl.opaque = cl; + + /* XXX use rdline() */ rdline_newline(&rl, "edit> "); - while (ret == 0 || ret == 2) { - n = read(cl->s_in, &c, 1); - if (n <= 0) + while (ret == RDLINE_RES_SUCCESS) { + read(cl->s_in, &c, 1); + if (c <= 0) return -1; ret = rdline_char_in(&rl, c); - if (ret == 1) { + if (ret == RDLINE_RES_VALIDATED) { snprintf(buf, len, "%s", rdline_get_buffer(&rl)); s = strchr(buf, '\n'); if (s) @@ -665,22 +668,25 @@ cmdline_parse_inst_t cmd_savefile = { /****** CONTEXT (list of instruction) */ /* in progmem */ -cmdline_parse_ctx_t main_ctx[] = { - (cmdline_parse_inst_t *)&cmd_ls, - (cmdline_parse_inst_t *)&cmd_ls_node, - (cmdline_parse_inst_t *)&cmd_pwd, - (cmdline_parse_inst_t *)&cmd_cd_prev, - (cmdline_parse_inst_t *)&cmd_cd_root, - (cmdline_parse_inst_t *)&cmd_cd_node, - (cmdline_parse_inst_t *)&cmd_set_bool, - (cmdline_parse_inst_t *)&cmd_set_int, - (cmdline_parse_inst_t *)&cmd_set_str, - (cmdline_parse_inst_t *)&cmd_show_node, - (cmdline_parse_inst_t *)&cmd_show_node_a, - (cmdline_parse_inst_t *)&cmd_load, - (cmdline_parse_inst_t *)&cmd_loadfile, - (cmdline_parse_inst_t *)&cmd_save, - (cmdline_parse_inst_t *)&cmd_savefile, - NULL, +cmdline_parse_ctx_t main_ctx = { + .name = "main", + .insts = { + &cmd_ls, + &cmd_ls_node, + &cmd_pwd, + &cmd_cd_prev, + &cmd_cd_root, + &cmd_cd_node, + &cmd_set_bool, + &cmd_set_int, + &cmd_set_str, + &cmd_show_node, + &cmd_show_node_a, + &cmd_load, + &cmd_loadfile, + &cmd_save, + &cmd_savefile, + NULL, + }, }; diff --git a/src/genconf/parse_confnode.c b/src/genconf/parse_confnode.c index b25166f..21ccaac 100644 --- a/src/genconf/parse_confnode.c +++ b/src/genconf/parse_confnode.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -41,15 +42,9 @@ #include "conf_htable.h" #include "parse_confnode.h" -struct cmdline_token_ops token_conf_node_ops = { - .parse = parse_conf_node, - .complete_get_nb = complete_get_nb_conf_node, - .complete_get_elt = complete_get_elt_conf_node, - .get_help = get_help_conf_node, -}; - /* return 0 if string matches pattern (string must not contain '*')*/ -static int strcmp_joker(const char *s, const char *pattern) +static int +strcmp_joker(const char *s, const char *pattern) { if (s == pattern) return 0; @@ -95,46 +90,6 @@ find_in_children(struct confnode_list *l, struct confnode *n, } static int -count_children(struct confnode *n, int flags, int mask) -{ - struct confnode *c; - int i = 0; - - TAILQ_FOREACH(c, &n->children, next) { - /* if it's a "if" node, parse children */ - if (c->flags & CONFNODE_F_INVISIBLE) - i += count_children(c, flags, mask); - else if ((c->flags & mask) == flags) - i ++; - } - return i; -} - -static struct confnode * -get_from_idx(struct confnode *n, unsigned int *cur, unsigned int idx, - int flags, int mask) -{ - struct confnode *c, *tmp; - - TAILQ_FOREACH(c, &n->children, next) { - /* if it's a "if" node, parse children */ - if (c->flags & CONFNODE_F_INVISIBLE) { - tmp = get_from_idx(c, cur, idx, flags, mask); - if (tmp) - return tmp; - } - else { - if ((c->flags & mask) != flags) - continue; - if (*cur == idx) - return c; - *cur = *cur + 1; - } - } - return NULL; -} - -int parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, unsigned ressize) { @@ -181,25 +136,97 @@ parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, return token_len; } -int complete_get_nb_conf_node(cmdline_parse_token_hdr_t *tk) +struct confnode_complete_callback { + struct confnode *start; + struct confnode *cur; +}; + + +static struct confnode *get_next_node(struct confnode *start, + struct confnode *cur, unsigned flags, + unsigned mask) +{ + struct confnode *prev; + struct confnode *parent = start; + + while (1) { + prev = cur; + + /* no more node to browse */ + if (cur == start) + return NULL; + + /* first iteration */ + if (cur == NULL) + cur = TAILQ_FIRST(&parent->children); + else + cur = TAILQ_NEXT(cur, next); + + /* no next, return to upper level */ + if (cur == NULL) { + if (prev == NULL) + return NULL; /* no node */ + cur = prev->parent; + continue; + } + + /* skip invisible nodes, and browse their contents */ + if (cur->flags & CONFNODE_F_INVISIBLE) { + parent = cur; + cur = NULL; + continue; + } + + /* check that flags match what we want */ + if ((cur->flags & mask) != flags) + continue; + + /* standard node, return it */ + return cur; + } +} + +static int +complete_conf_node_start(cmdline_parse_token_hdr_t *tk, void **opaque) { struct token_conf_node *tk2 = (struct token_conf_node *)tk; struct token_conf_node_data *tkd = &tk2->conf_node_data; + struct confnode_complete_callback *cb; + struct confnode *n; + unsigned flags = tkd->flags; + unsigned mask = tkd->mask; - return count_children(*tkd->cur, tkd->flags, tkd->mask); + cb = malloc(sizeof(*cb)); + if (cb == NULL) + return -1; + + n = get_next_node(*tkd->cur, NULL, flags, mask); + if (n == NULL) + return -1; /* no completion */ + + cb->start = *tkd->cur; + cb->cur = n; + *opaque = cb; + return 0; } -int complete_get_elt_conf_node(cmdline_parse_token_hdr_t *tk, int idx, - char *dstbuf, unsigned int size) +static int +complete_conf_node_iterate(cmdline_parse_token_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) { struct token_conf_node *tk2 = (struct token_conf_node *)tk; struct token_conf_node_data *tkd = &tk2->conf_node_data; + struct confnode_complete_callback *cb; struct confnode *n; - unsigned int i = 0, len; + unsigned len; + unsigned flags = tkd->flags; + unsigned mask = tkd->mask; - n = get_from_idx(*tkd->cur, &i, idx, tkd->flags, tkd->mask); + cb = *opaque; + n = cb->cur; if (n == NULL) return -1; + cb->cur = get_next_node(cb->start, n, flags, mask); len = strlen(n->name) + 1; if (len > size) @@ -209,9 +236,23 @@ int complete_get_elt_conf_node(cmdline_parse_token_hdr_t *tk, int idx, return 0; } +static void +complete_conf_node_end(cmdline_parse_token_hdr_t *tk, void **opaque) +{ + free(*opaque); +} -int get_help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size) +int help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size) { snprintf(dstbuf, size, "conf-node"); return 0; } + +struct cmdline_token_ops token_conf_node_ops = { + .parse = parse_conf_node, + .complete_start = complete_conf_node_start, + .complete_iterate = complete_conf_node_iterate, + .complete_end = complete_conf_node_end, + .help = help_conf_node, +}; + diff --git a/src/genconf/parse_confnode.h b/src/genconf/parse_confnode.h index 83a925a..5f19964 100644 --- a/src/genconf/parse_confnode.h +++ b/src/genconf/parse_confnode.h @@ -44,13 +44,6 @@ typedef struct token_conf_node parse_token_conf_node_t; extern struct cmdline_token_ops token_conf_node_ops; -int parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *srcbuf, - void *res, unsigned ressize); -int complete_get_nb_conf_node(cmdline_parse_token_hdr_t *tk); -int complete_get_elt_conf_node(cmdline_parse_token_hdr_t *tk, int idx, - char *dstbuf, unsigned int size); -int get_help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size); - #define TOKEN_CONF_NODE_INITIALIZER(structure, field, \ root_node_ptr, cur_node_ptr, \ node_flags, node_mask) \ diff --git a/src/lib/cmdline.c b/src/lib/cmdline.c index d44e44b..6e8d7b8 100644 --- a/src/lib/cmdline.c +++ b/src/lib/cmdline.c @@ -74,44 +74,48 @@ #include "cmdline.h" static void -cmdline_valid_buffer(struct rdline *rdl, const char *buf, - __attribute__((unused)) unsigned int size) +cmdline_default_valid_buffer(struct rdline *rdl, const char *buf, + __attribute__((unused)) unsigned int size) { struct cmdline *cl = rdl->opaque; int ret; - ret = cmdline_parse(cl, buf); + ret = cmdline_parse(cl->ctx, buf, cl); if (ret == CMDLINE_PARSE_AMBIGUOUS) cmdline_printf(cl, "Ambiguous command\n"); else if (ret == CMDLINE_PARSE_NOMATCH) - cmdline_printf(cl, "Command not found\n"); - else if (ret == CMDLINE_PARSE_BAD_ARGS) cmdline_printf(cl, "Bad arguments\n"); else if (ret == CMDLINE_PARSE_UNTERMINATED_QUOTE) cmdline_printf(cl, "Unterminated quote\n"); } static int -cmdline_complete_buffer(struct rdline *rdl, const char *buf, - char *dstbuf, unsigned int dstsize, - int *state) +cmdline_default_complete_buffer(struct rdline *rdl, const char *buf, + char *dstbuf, unsigned int dstsize) { struct cmdline *cl = rdl->opaque; - return cmdline_complete(cl, buf, state, dstbuf, dstsize); + // RDLINE_RES_COMPLETE ? + return cmdline_complete(cl->ctx, buf, dstbuf, dstsize); } -int -cmdline_write_char(struct rdline *rdl, char c) + +static int +cmdline_default_help(struct rdline *rdl, const char *buf, + rdline_write_t *write_cb, void *write_arg) { - int ret = -1; struct cmdline *cl = rdl->opaque; + return cmdline_help(cl->ctx, buf, cmdline_write, write_arg); +} - if (cl->s_out >= 0) - ret = write(cl->s_out, &c, 1); +/* ---- Some rdline wrappers ---- */ - return ret; +/* write a buffer, just use rdline */ +ssize_t +cmdline_write(void *arg, void *buf, size_t count) +{ + const struct rdline *rdl = arg; + return rdline_write(rdl, buf, count); } - void cmdline_set_prompt(struct cmdline *cl, const char *prompt) { @@ -122,16 +126,23 @@ struct cmdline * cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt, int s_in, int s_out) { struct cmdline *cl; + cl = malloc(sizeof(struct cmdline)); if (cl == NULL) return NULL; + + /* init cmdline structure */ memset(cl, 0, sizeof(struct cmdline)); cl->s_in = s_in; cl->s_out = s_out; cl->ctx = ctx; - rdline_init(&cl->rdl, cmdline_write_char, - cmdline_valid_buffer, cmdline_complete_buffer); + /* init embedded rdline */ + rdline_init(&cl->rdl, s_in, s_out, + cmdline_default_valid_buffer, + cmdline_default_complete_buffer, + cmdline_default_help); + cl->rdl.opaque = cl; cmdline_set_prompt(cl, prompt); rdline_newline(&cl->rdl, cl->prompt); @@ -150,62 +161,37 @@ cmdline_free(struct cmdline *cl) free(cl); } -void +int cmdline_printf(const struct cmdline *cl, const char *fmt, ...) { va_list ap; - -#ifdef _GNU_SOURCE - if (cl->s_out < 0) - return; - va_start(ap, fmt); - vdprintf(cl->s_out, fmt, ap); - va_end(ap); -#else int ret; - char *buf; - if (cl->s_out < 0) - return; - - buf = malloc(BUFSIZ); - if (buf == NULL) - return; va_start(ap, fmt); - ret = vsnprintf(buf, BUFSIZ, fmt, ap); + ret = rdline_vprintf(&cl->rdl, fmt, ap); va_end(ap); - if (ret < 0) - return; - if (ret >= BUFSIZ) - ret = BUFSIZ - 1; - write(cl->s_out, buf, ret); - free(buf); -#endif + + return ret; } +/* Push an input buffer in the command line. Typically, this function + * is called by cmdline_interact() to send the input characters to the + * cmdline process. It can also be called by a user callback function, + * when a buffer is received from the input socket. + * + * The function returns the number of processed characters, or a + * negative value on error (EOF reached or command line exited. */ int cmdline_in(struct cmdline *cl, const char *buf, int size) { - const char *history, *buffer; int ret = 0; - int i, same; + int i; - for (i=0; irdl, buf[i]); - if (ret == RDLINE_RES_VALIDATED) { - buffer = rdline_get_buffer(&cl->rdl); - history = rdline_get_history_item(&cl->rdl, 0); - if (history) { - same = !memcmp(buffer, history, strlen(history)) && - buffer[strlen(history)] == '\n'; - } - else - same = 0; - if (strlen(buffer) > 1 && !same) - rdline_add_history(&cl->rdl, buffer); + if (ret == RDLINE_RES_VALIDATED) rdline_newline(&cl->rdl, cl->prompt); - } else if (ret == RDLINE_RES_EOF) return -1; else if (ret == RDLINE_RES_EXITED) @@ -214,12 +200,16 @@ cmdline_in(struct cmdline *cl, const char *buf, int size) return i; } +/* Interrupt a running command line process */ void cmdline_quit(struct cmdline *cl) { rdline_quit(&cl->rdl); } +/* Start command line on configured file descriptor. This function + * loops until the user explicitelly call cmdline_quit(), or if the + * input fd reaches EOF. */ void cmdline_interact(struct cmdline *cl) { diff --git a/src/lib/cmdline.h b/src/lib/cmdline.h index d8a7384..20321e8 100644 --- a/src/lib/cmdline.h +++ b/src/lib/cmdline.h @@ -75,13 +75,59 @@ struct cmdline { #endif }; -struct cmdline *cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt, int s_in, int s_out); +/** + * Allocate and initialize a new command line structure, using the + * specified prompt and specified input/output file descriptors to + * communicate with the user. + */ +struct cmdline *cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt, + int s_in, int s_out); + +/** + * Set the prompt of the given command line. + */ void cmdline_set_prompt(struct cmdline *cl, const char *prompt); + +/** + * Free a previously allocated command line: data pointed by the cl + * pointer is freed. Note: the user should call cmdline_quit() before + * calling this function. + */ void cmdline_free(struct cmdline *cl); -void cmdline_printf(const struct cmdline *cl, const char *fmt, ...); + +/** + * write a buffer, just use rdline + */ +ssize_t cmdline_write(void *arg, void *buf, size_t count); + +/** + * Display data on the output file descriptor of command line, using + * printf-like arguments. + */ +int cmdline_printf(const struct cmdline *cl, const char *fmt, ...); + +/** + * Push an input buffer in the command line. Typically, this function + * is called by cmdline_interact() to send the inpu characters to the + * cmdline process. It can also be called by a user callback function, + * when a buffer is received from the input socket. + * + * The function returns the number of processed characters, or a + * negative value on error (EOF reached or command line exited. + */ int cmdline_in(struct cmdline *cl, const char *buf, int size); -int cmdline_write_char(struct rdline *rdl, char c); + +/** + * Start command line on configured file descriptor. This function + * loops until the user explicitelly call cmdline_quit(), or if the + * input fd reaches EOF. + */ void cmdline_interact(struct cmdline *cl); + +/** + * Interrupt a running command line process. Actually it will call + * rdline_quit() on the rdline descriptor. + */ void cmdline_quit(struct cmdline *cl); #endif /* _CMDLINE_SOCKET_H_ */ diff --git a/src/lib/cmdline_parse.c b/src/lib/cmdline_parse.c index 060a8e9..dfc17f1 100644 --- a/src/lib/cmdline_parse.c +++ b/src/lib/cmdline_parse.c @@ -65,16 +65,25 @@ #include #include #include +#include #include #include "cmdline_parse.h" #include "cmdline.h" -//#define CMDLINE_DEBUG //#define debug_printf printf #define debug_printf(args...) do {} while(0) +/* used internally for cmdline_help() and cmdline_complete() */ +struct cmdline_preparse { + int nb_valid_tok; /* number of valid tokens in the buffer */ + void *opaque; /* pointer to opaque data */ + char comp_tok_buf[CMDLINE_MAX_TOKEN_SIZE]; /* token to complete */ + int comp_tok_len; /* length of the token to complete */ + int comp_tok_offset; /* offset of token to complete in the line buf */ +}; + /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our * own. */ static int @@ -153,14 +162,23 @@ int cmdline_quote_token(char *dst, unsigned dstlen, const char *src) return s; } -/* remove quote and stop when we reach the end of token */ -// XXX ret val comment -int cmdline_unquote_token(char *dst, unsigned dstlen, - const char *src) +/* Remove quote and stop when we reach the end of token. Return the + * number of "eaten" bytes from the source buffer, or a negative value + * on error */ +int cmdline_get_token(char *dst, unsigned dstlen, const char *src) { unsigned s = 0, d = 0; int quoted = 0; + /* skip spaces */ + while (isblank2(src[s])) + s++; + + /* empty token */ + if (cmdline_isendoftoken(src[s])) + return -EINVAL; + + /* copy token and remove quotes */ while (src[s] != '\0') { if (d >= dstlen) return -EMSGSIZE; @@ -186,427 +204,509 @@ int cmdline_unquote_token(char *dst, unsigned dstlen, dst[d++] = src[s++]; } + /* not enough room in dst buffer */ + if (d >= (dstlen-1)) + return -EMSGSIZE; + + /* end of string during quote */ if (quoted) return -EINVAL; - if (d >= (dstlen-1)) - return -EMSGSIZE; dst[d++] = '\0'; return s; } -/** +/* return the nth token from src and copy it in dst. Return the offset + * of the token in src, or a negative value on error. Note: the index + * of the first token is 0. */ +static int cmdline_get_nth_token(char *dst, unsigned dstlen, int n, + const char *src) +{ + int ret = 0, offset = 0; + + do { + offset += ret; + + /* skip spaces */ + while (isblank2(src[offset])) + offset++; + + /* get the token starting at offset */ + ret = cmdline_get_token(dst, dstlen, src + offset); + if (ret < 0) + return ret; + + } while (n--); + + return offset; +} + +/* * try to match the buffer with an instruction (only the first * nb_match_token tokens if != 0). Return 0 if we match all the * tokens, else the number of matched tokens, else -1. + +retval à vérifier. + */ static int -match_inst(cmdline_parse_inst_t *inst, const char *buf, +match_inst(cmdline_parse_inst_t *inst, const char *linebuf, unsigned int nb_match_token, void *resbuf, unsigned resbuf_size) { - unsigned int token_num=0; - cmdline_parse_token_hdr_t * token_p; - unsigned int i=0; + unsigned int token_num = 0; + cmdline_parse_token_hdr_t *token; int n = 0, res; - struct cmdline_token_hdr token_hdr; - char token_str[128]; + char token_str[CMDLINE_MAX_TOKEN_SIZE]; - token_p = inst->tokens[token_num]; - if (token_p) - memcpy(&token_hdr, token_p, sizeof(token_hdr)); + token = inst->tokens[token_num]; /* check if we match all tokens of inst */ - while (token_p && (!nb_match_token || i= nb_match_token) + return 0; + debug_printf("TK\n"); - /* skip spaces */ - while (isblank2(*buf)) { - buf++; - } - /* end of buf */ - if ( isendofline(*buf) || iscomment(*buf) ) + /* copy token and remove quotes */ + n = cmdline_get_token(token_str, sizeof(token_str), linebuf); + if (n < 0) break; - n = cmdline_unquote_token(token_str, sizeof(token_str), buf); - if (n == -EINVAL) - return -EINVAL; - + /* parse this token */ if (resbuf == NULL) - res = token_hdr.ops->parse(token_p, token_str, NULL, 0); + res = token->ops->parse(token, token_str, NULL, 0); else { unsigned rb_sz; - void *rb = (char *)resbuf + token_hdr.offset; - if (token_hdr.offset > resbuf_size) + void *rb = (char *)resbuf + token->offset; + + /* not enough room to store result */ + if (token->offset > resbuf_size) return -ENOBUFS; - rb_sz = resbuf_size - token_hdr.offset; - res = token_hdr.ops->parse(token_p, token_str, - rb, rb_sz); + + rb_sz = resbuf_size - token->offset; + res = token->ops->parse(token, token_str, rb, rb_sz); } + /* does not match this token */ if (res < 0) break; debug_printf("TK parsed (len=%d)\n", n); - i++; - buf += n; - + linebuf += n; token_num ++; - token_p = inst->tokens[token_num]; - if (token_p) - memcpy(&token_hdr, token_p, sizeof(token_hdr)); + token = inst->tokens[token_num]; } /* does not match */ - if (i==0) + if (token_num == 0) return -ENOENT; - /* in case we want to match a specific num of token */ - if (nb_match_token) { - if (i == nb_match_token) { - return 0; - } - return i; - } - /* we don't match all the tokens */ - if (token_p) { - return i; - } + if (token) + return token_num; /* are there are some tokens more */ - while (isblank2(*buf)) { - buf++; - } + while (isblank2(*linebuf)) + linebuf++; - /* end of buf */ - if ( isendofline(*buf) || iscomment(*buf) ) + /* end of buf, we match all inst */ + if (isendofline(*linebuf) || iscomment(*linebuf)) return 0; /* garbage after inst */ - return i; + return token_num; } -int -cmdline_parse(struct cmdline *cl, const char * buf) +/* Check if a line buffer is valid and can be parsed or completed. The + * parsing stops when \n or \0 is reached. The also function checks + * that tokens are correctly quoted. The number of tokens in the + * buffer is returned. */ +static int cmdline_validate_linebuf(const char *linebuf) { - unsigned int inst_num=0; - cmdline_parse_inst_t *inst; - const char *curbuf; - char result_buf[CMDLINE_MAX_DSTBUF_SIZE]; - void (*f)(void *, struct cmdline *, void *) = NULL; - void *data = NULL; - int comment = 0; - int linelen = 0; - int parse_it = 0; - int err = CMDLINE_PARSE_NOMATCH; - int tok; - cmdline_parse_ctx_t *ctx = cl->ctx; -#ifdef CMDLINE_DEBUG - char debug_buf[BUFSIZ]; -#endif - - /* - * - look if the buffer contains at least one line - * - look if line contains only spaces or comments - * - count line length - */ - curbuf = buf; - while (! isendofline(*curbuf)) { - if ( *curbuf == '\0' ) { - debug_printf("Incomplete buf (len=%d)\n", linelen); - return 0; + int quoted = 0, comment = 0; + int i = 0, nbtok = 0, token = 0; + + while (linebuf[i] != '\0') { + if (isendofline(linebuf[i]) && quoted == 0) + break; + if (comment == 1) { + i++; + continue; } - if ( iscomment(*curbuf) ) { + if (iscomment(linebuf[i]) && quoted == 0) { comment = 1; + i ++; + continue; } - if ( ! isblank2(*curbuf) && ! comment) { - parse_it = 1; + + /* end of token */ + if (isblank2(linebuf[i]) && quoted == 0) + token = 0; + /* new token */ + if (!isblank2(linebuf[i]) && token == 0) { + token = 1; + nbtok++; } - curbuf++; - linelen++; - } - /* skip all endofline chars */ - while (isendofline(buf[linelen])) { - linelen++; + if (linebuf[i] == '\\' && linebuf[i+1] == '"') { + i += 2; + continue; + } + if (linebuf[i] == '\\' && linebuf[i+1] == '\\') { + i += 2; + continue; + } + if (linebuf[i] == '"') { + i++; + quoted = !quoted; + continue; + } + i++; } + if (quoted) + return CMDLINE_PARSE_UNTERMINATED_QUOTE; + return nbtok; +} - /* empty line */ - if ( parse_it == 0 ) { - debug_printf("Empty line (len=%d)\n", linelen); - return linelen; - } +/* Try to parse a buffer according to the specified context. The + * argument linebuf must end with \n or \0. */ +int +cmdline_parse(cmdline_parse_ctx_t *ctx, const char *linebuf, void *opaque) +{ + cmdline_parse_inst_t **pinst; + cmdline_parse_inst_t *inst; + char result_buf[CMDLINE_MAX_DSTBUF_SIZE]; + void (*f)(void *, struct cmdline *, void *) = NULL; + void *data = NULL; + int ret; + + ret = cmdline_validate_linebuf(linebuf); + if (ret < 0) + return ret; + if (ret == 0) + return CMDLINE_PARSE_EMPTY; -#ifdef CMDLINE_DEBUG - snprintf(debug_buf, (linelen>64 ? 64 : linelen), "%s", buf); - debug_printf("Parse line : len=%d, <%s>\n", linelen, debug_buf); -#endif /* parse it !! */ - inst = ctx[inst_num]; - while (inst) { - debug_printf("INST %d\n", inst_num); + for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) { + inst = *pinst; + debug_printf("INST\n"); /* fully parsed */ - tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf)); + ret = match_inst(inst, linebuf, 0, result_buf, sizeof(result_buf)); - if (tok > 0) /* we matched at least one token */ - err = CMDLINE_PARSE_BAD_ARGS; + if (ret != 0) + continue; - else if (!tok) { - debug_printf("INST fully parsed\n"); - /* skip spaces */ - while (isblank2(*curbuf)) { - curbuf++; - } + debug_printf("INST fully parsed\n"); - /* if end of buf -> there is no garbage after inst */ - if (isendofline(*curbuf) || iscomment(*curbuf)) { - if (!f) { - memcpy(&f, &inst->f, sizeof(f)); - memcpy(&data, &inst->data, sizeof(data)); - } - else { - /* more than 1 inst matches */ - err = CMDLINE_PARSE_AMBIGUOUS; - f=NULL; - debug_printf("Ambiguous cmd\n"); - break; - } - } + /* if end of buf -> there is no garbage after inst */ + if (f != NULL) { + /* more than 1 inst matches */ + debug_printf("Ambiguous cmd\n"); + return CMDLINE_PARSE_AMBIGUOUS; } - else if (tok == -EINVAL) { - err = CMDLINE_PARSE_UNTERMINATED_QUOTE; - f = NULL; - debug_printf("Unterminated quote\n"); - break; - } - - inst_num ++; - inst = ctx[inst_num]; + f = inst->f; + data = inst->data; } /* call func */ - if (f) { - f(result_buf, cl, data); - } + if (f == NULL) + return CMDLINE_PARSE_NOMATCH; - /* no match */ + f(result_buf, opaque, data); + return CMDLINE_PARSE_SUCCESS; +} + +/* called by cmdline_help() and cmdline_complete() to preparse the + * line buffer (the operations done are common to these functions) */ +static int cmdline_preparse(struct cmdline_preparse *preparse, const char *buf) +{ + int ret, len, nb_tok; + + /* count the number of tokens in the line buffer */ + ret = cmdline_validate_linebuf(buf); + if (ret < 0) + return ret; + nb_tok = ret; + + /* if last token is not complete, decrement nb_valid_tok */ + len = strlen(buf); + if (nb_tok == 0 || isblank2(buf[len - 1])) { + preparse->nb_valid_tok = nb_tok; + preparse->comp_tok_offset = len; + preparse->comp_tok_buf[0] = '\0'; + preparse->comp_tok_len = 0; + } else { - debug_printf("No match err=%d\n", err); - return err; + preparse->nb_valid_tok = nb_tok - 1; + + /* get the incomplete token (can be empty) and return its + * offset in the buffer */ + preparse->comp_tok_offset = + cmdline_get_nth_token(preparse->comp_tok_buf, + sizeof(preparse->comp_tok_buf), + preparse->nb_valid_tok, + buf); + preparse->comp_tok_len = strlen(preparse->comp_tok_buf); } - return linelen; + return 0; } -int -cmdline_complete(struct cmdline *cl, const char *buf, int *state, - char *dst, unsigned int size) +/* Display a contextual help using the write_buf() function pointer + * given as parameter (called with its opaque pointer). The contextual + * help depends on the buffer given. */ +int cmdline_help(cmdline_parse_ctx_t *ctx, const char *buf, + cmdline_write_t *write_buf, void *opaque) { - const char *partial_tok = buf; - unsigned int inst_num = 0; + cmdline_parse_token_hdr_t *token; + cmdline_parse_inst_t **pinst; cmdline_parse_inst_t *inst; - cmdline_parse_token_hdr_t *token_p; - struct cmdline_token_hdr token_hdr; - char tmpbuf[64], comp_buf[64]; - unsigned int partial_tok_len; - int comp_len = -1; - int nb_token = 0; - unsigned int i, n; - int l; - unsigned int nb_completable; - unsigned int nb_non_completable; - int local_state = 0; + struct cmdline_preparse preparse; + char helpbuf[CMDLINE_MAX_DSTBUF_SIZE]; + char tmpbuf[CMDLINE_MAX_DSTBUF_SIZE]; char *help_str; - cmdline_parse_ctx_t *ctx = cl->ctx; + int n, len, iterate; - debug_printf("%s called\n", __FUNCTION__); - memset(&token_hdr, 0, sizeof(token_hdr)); - - /* count the number of complete token to parse */ - for (i=0 ; buf[i] ; i++) { - if (!isblank2(buf[i]) && isblank2(buf[i+1])) - nb_token++; - if (isblank2(buf[i]) && !isblank2(buf[i+1])) - partial_tok = buf+i+1; - } - partial_tok_len = strlen(partial_tok); - - /* first call -> do a first pass */ - if (*state <= 0) { - debug_printf("try complete <%s>\n", buf); - debug_printf("there is %d complete tokens, <%s> is incomplete\n", - nb_token, partial_tok); - - nb_completable = 0; - nb_non_completable = 0; - - inst = ctx[inst_num]; - while (inst) { - /* parse the first tokens of the inst */ - if (nb_token && match_inst(inst, buf, nb_token, NULL, 0)) - goto next; - - debug_printf("instruction match \n"); - token_p = inst->tokens[nb_token]; - if (token_p) - memcpy(&token_hdr, token_p, sizeof(token_hdr)); - - /* non completable */ - if (!token_p || - !token_hdr.ops->complete_get_nb || - !token_hdr.ops->complete_get_elt || - (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { - nb_non_completable++; - goto next; - } + cmdline_preparse(&preparse, buf); - debug_printf("%d choices for this token\n", n); - for (i=0 ; icomplete_get_elt(token_p, i, - tmpbuf, - sizeof(tmpbuf)) < 0) - continue; + debug_printf("display contextual help\n"); - /* we have at least room for one char */ - strcat(tmpbuf, " "); + for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) { + inst = *pinst; - debug_printf(" choice <%s>\n", tmpbuf); - - /* does the completion match the - * beginning of the word ? */ - if (!strncmp(partial_tok, tmpbuf, - partial_tok_len)) { - if (comp_len == -1) { - strcpy(comp_buf, - tmpbuf + partial_tok_len); - comp_len = - strlen(tmpbuf + - partial_tok_len); - - } - else { - comp_len = - nb_common_chars(comp_buf, - tmpbuf+partial_tok_len); - comp_buf[comp_len] = 0; - } - nb_completable++; - } - } - next: - inst_num ++; - inst = ctx[inst_num]; + /* match the beginning of the command */ + if (preparse.nb_valid_tok != 0 && + match_inst(inst, buf, preparse.nb_valid_tok, + NULL, 0) != 0) + continue; + + token = inst->tokens[preparse.nb_valid_tok]; + + /* end of inst */ + if (token == NULL) { + n = snprintf(helpbuf, sizeof(helpbuf), "[RETURN]\n"); + if (n > 0) + write_buf(opaque, helpbuf, n); + continue; } - debug_printf("total choices %d for this completion\n", - nb_completable); + /* token matches, but no completion */ + if (token->ops->complete_start == NULL || + token->ops->complete_iterate == NULL) + iterate = 0; + else + iterate = 1; + + if (iterate == 1 && + token->ops->complete_start(token, &preparse.opaque) < 0) { + /* cancel iteration, complete_start() returned + * a negative value, meaning no completion */ + iterate = 0; + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, + &preparse.opaque); + } - /* no possible completion */ - if (nb_completable == 0 && nb_non_completable == 0) - return 0; + if (iterate == 0) { + /* get token dynamic help string */ + if ((token->ops->help == NULL) || + (token->ops->help(token, tmpbuf, sizeof(tmpbuf)) < 0)) + snprintf(tmpbuf, sizeof(tmpbuf), "unknown"); - /* if multichoice is not required */ - if (*state == 0 && partial_tok_len > 0) { - /* one or several choices starting with the - same chars */ - if (comp_len > 0) { - if ((unsigned)(comp_len + 1) > size) - return 0; + /* get instruction static help string */ + help_str = inst->help_str; + if (help_str == NULL) + help_str = "No help"; - strcpy(dst, comp_buf); - return 2; - } + /* send it to callback function */ + n = snprintf(helpbuf, sizeof(helpbuf), + "[%s]: %s\n", tmpbuf, help_str); + if (n >= 0) + write_buf(opaque, helpbuf, n); + + continue; } + + + /* iterate over all possible completion for this inst */ + while (token->ops->complete_iterate(token, + &preparse.opaque, + tmpbuf, + sizeof(tmpbuf)) == 0) { + + + debug_printf(" choice <%s>\n", tmpbuf); + + /* does the completion match the beginning of + * the word ? */ + if (strncmp(preparse.comp_tok_buf, tmpbuf, + preparse.comp_tok_len)) + continue; + + /* get the token and add it in help buffer */ + n = snprintf(helpbuf, sizeof(helpbuf), "%s ", tmpbuf); + if (n < 0 || n >= sizeof(helpbuf)) + continue; + len = n; + + /* get token dynamic help */ + if ((token->ops->help == NULL) || + (token->ops->help(token, tmpbuf, sizeof(tmpbuf)) < 0)) + snprintf(tmpbuf, sizeof(tmpbuf), "unknown"); + + /* get instruction static help string */ + help_str = inst->help_str; + if (help_str == NULL) + help_str = "No help"; + + /* send it to callback function */ + n = snprintf(helpbuf + len, sizeof(helpbuf) - len, + "[%s]: %s\n", tmpbuf, help_str); + if (n >= 0) + write_buf(opaque, helpbuf, n + len); + } + + /* no more completion, go to next inst */ + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, &preparse.opaque); } - /* init state correctly */ - if (*state == -1) - *state = 0; - - debug_printf("Multiple choice STATE=%d\n", *state); - - inst_num = 0; - inst = ctx[inst_num]; - while (inst) { - /* we need to redo it */ - inst = ctx[inst_num]; - - if (nb_token && match_inst(inst, buf, nb_token, NULL, 0)) - goto next2; - - token_p = inst->tokens[nb_token]; - if (token_p) - memcpy(&token_hdr, token_p, sizeof(token_hdr)); - - /* one choice for this token */ - if (!token_p || - !token_hdr.ops->complete_get_nb || - !token_hdr.ops->complete_get_elt || - (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { - if (local_state < *state) { - local_state++; - goto next2; - } - (*state)++; - if (token_p && token_hdr.ops->get_help) { - token_hdr.ops->get_help(token_p, tmpbuf, - sizeof(tmpbuf)); - help_str = inst->help_str; - if (help_str) - snprintf(dst, size, "[%s]: %s", tmpbuf, - help_str); - else - snprintf(dst, size, "[%s]: No help", - tmpbuf); - } - else { - snprintf(dst, size, "[RETURN]"); - } - return 1; + return 0; +} + +/* try to complete the buffer given as a parameter */ +int +cmdline_complete(cmdline_parse_ctx_t *ctx, const char *buf, + char *dst, unsigned int dstsize) +{ + cmdline_parse_token_hdr_t *token; + cmdline_parse_inst_t **pinst; + cmdline_parse_inst_t *inst; + struct cmdline_preparse preparse; + int nb_match = 0; + int nb_completion = 0; + int completion_len = CMDLINE_MAX_TOKEN_SIZE; + char completion_buf[CMDLINE_MAX_TOKEN_SIZE]; + char tmpbuf[CMDLINE_MAX_TOKEN_SIZE]; + int ret, n; + + debug_printf("%s called\n", __FUNCTION__); + + /* fill the preparse structure that contains infos that will + * help us to complete the buffer */ + ret = cmdline_preparse(&preparse, buf); + if (ret < 0) + return CMDLINE_COMPLETE_NONE; + + /* try to complete !! */ + for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) { + inst = *pinst; + + debug_printf("INST\n"); + + /* try to match the first tokens */ + if (preparse.nb_valid_tok != 0 && + match_inst(inst, buf, preparse.nb_valid_tok, + NULL, 0) != 0) + continue; + + nb_match ++; + token = inst->tokens[preparse.nb_valid_tok]; + + /* non completable */ + if (token == NULL || + token->ops->complete_start == NULL || + token->ops->complete_iterate == NULL) + continue; + + /* non completable */ + if (token->ops->complete_start(token, &preparse.opaque) < 0) { + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, + &preparse.opaque); + continue; } - /* several choices */ - for (i=0 ; icomplete_get_elt(token_p, i, tmpbuf, - sizeof(tmpbuf)) < 0) - continue; - /* we have at least room for one char */ - strcat(tmpbuf, " "); + /* all possible completion for this token */ + while (1) { + + ret = token->ops->complete_iterate(token, + &preparse.opaque, + tmpbuf, + sizeof(tmpbuf)-1); + + if (ret < 0) + break; + + debug_printf("Completion %s\n", tmpbuf); + + /* we kept at least the room for one char */ + /* XXX to be updated for non-final completion */ + if (ret == 0) + strcat(tmpbuf, " "); debug_printf(" choice <%s>\n", tmpbuf); /* does the completion match the beginning of * the word ? */ - if (!strncmp(partial_tok, tmpbuf, - partial_tok_len)) { - if (local_state < *state) { - local_state++; - continue; - } - (*state)++; - l=snprintf(dst, size, "%s", tmpbuf); - if (l>=0 && token_hdr.ops->get_help) { - token_hdr.ops->get_help(token_p, tmpbuf, - sizeof(tmpbuf)); - help_str = inst->help_str; - if (help_str) - snprintf(dst+l, size-l, "[%s]: %s", - tmpbuf, help_str); - else - snprintf(dst+l, size-l, - "[%s]: No help", tmpbuf); - } - - return 1; + if (strncmp(preparse.comp_tok_buf, tmpbuf, + preparse.comp_tok_len)) + continue; + + /* first one, save the buffer */ + if (nb_completion == 0) { + completion_len = snprintf(completion_buf, + sizeof(completion_buf), + "%s", tmpbuf); + } + else { + n = nb_common_chars(completion_buf, tmpbuf); + if (n < completion_len) + completion_len = n; } + nb_completion ++; + + /* we cannot add any char, just display help */ + if (completion_len == preparse.comp_tok_len) + break; } - next2: - inst_num ++; - inst = ctx[inst_num]; + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, &preparse.opaque); + + if (completion_len == preparse.comp_tok_len) + break; } - return 0; + + debug_printf("nb_completion=%d, completion_len=%d\n", + nb_completion, completion_len); + + /* one choice, append chars and return */ + if (nb_completion == 1) { + snprintf(dst, dstsize, "%s", + completion_buf + preparse.comp_tok_len); + return CMDLINE_COMPLETE_APPEND; + } + + /* many choices, but starting with same chars: append chars + * and return */ + if (nb_completion != 0 && completion_len > preparse.comp_tok_len) { + if (completion_len >= dstsize) + completion_len = dstsize - 1; + strncpy(dst, completion_buf + preparse.comp_tok_len, + completion_len); + dst[completion_len] = '\0'; + return CMDLINE_COMPLETE_APPEND; + } + + /* no match, nothing to do */ + if (nb_match == 0 || nb_completion == 0) + return CMDLINE_COMPLETE_NONE; + + return CMDLINE_COMPLETE_MANY; } diff --git a/src/lib/cmdline_parse.h b/src/lib/cmdline_parse.h index e9e6308..5112ab5 100644 --- a/src/lib/cmdline_parse.h +++ b/src/lib/cmdline_parse.h @@ -68,18 +68,6 @@ #define offsetof(type, field) ((size_t) &( ((type *)0)->field) ) #endif -/* return status for parsing */ -#define CMDLINE_PARSE_SUCCESS 0 -#define CMDLINE_PARSE_AMBIGUOUS -1 -#define CMDLINE_PARSE_NOMATCH -2 -#define CMDLINE_PARSE_BAD_ARGS -3 -#define CMDLINE_PARSE_UNTERMINATED_QUOTE -4 - -/* return status for completion */ -#define CMDLINE_PARSE_COMPLETE_FINISHED 0 -#define CMDLINE_PARSE_COMPLETE_AGAIN 1 -#define CMDLINE_PARSE_COMPLETED_BUFFER 2 - #define CMDLINE_MAX_TOKEN_SIZE 128 /* including '\0' */ #define CMDLINE_MAX_DSTBUF_SIZE 1024 @@ -97,33 +85,44 @@ typedef struct cmdline_token_hdr cmdline_parse_token_hdr_t; * A token is defined by this structure. * * parse() takes the token as first argument, then the source buffer - * starting at the token we want to parse. The 3rd arg is a pointer + * containing the token we want to parse. The 3rd arg is a pointer * where we store the parsed data (as binary), and the 4th arg is the * size of this area. It returns 0 on success and a negative value on * error. * - * complete_get_nb() returns the number of possible values for this - * token if completion is possible. If it is NULL or if it returns 0, - * no completion is possible. + * complete_start() prepares a completion operation. The first + * argument is the token to complete. The second argument is an opaque + * pointer that will be given to complete_iterate() function. It can + * be used to store private data for this completion. For each + * complete_start() call, the user must call complete_end() at the end + * of iterations (if defined). Return a negative value if completion + * is not possible, or 0 on success. + * + * complete_iterate() copy in dstbuf (the size is specified in the + * parameter) the next possible completion for this token. Return 0 on + * success or a negative value on error (or when there is no more + * completion). Refer to cmdline_complete_string_iterate() for an + * example. * - * complete_get_elt() copy in dstbuf (the size is specified in the - * parameter) the i-th possible completion for this token. Return 0 - * on success or a negative value on error. + * complete_end() is called when the iteration on this token is finished, + * this function should free all things allocated during complete_start(). * - * get_help() fills the dstbuf with the help for the token. It returns + * help() fills the dstbuf with the help for the token. It returns * -1 on error and 0 on success. */ struct cmdline_token_ops { /** parse(token ptr, buf, res pts) */ int (*parse)(cmdline_parse_token_hdr_t *, const char *, void *, unsigned int); - /** return the num of possible choices for this token */ - int (*complete_get_nb)(cmdline_parse_token_hdr_t *); - /** return the elt x for this token (token, idx, dstbuf, size) */ - int (*complete_get_elt)(cmdline_parse_token_hdr_t *, int, char *, + /** prepare a completion on this token */ + int (*complete_start)(cmdline_parse_token_hdr_t *, void **); + /** fill dstbuf for this token (token, opaque, dstbuf, size) */ + int (*complete_iterate)(cmdline_parse_token_hdr_t *, void **, char *, unsigned int); + /* end of completion, used to free the opaque structure */ + void (*complete_end)(cmdline_parse_token_hdr_t *, void **); /** get help for this token (token, dstbuf, size) */ - int (*get_help)(cmdline_parse_token_hdr_t *, char *, unsigned int); + int (*help)(cmdline_parse_token_hdr_t *, char *, unsigned int); }; struct cmdline; @@ -146,40 +145,65 @@ typedef struct cmdline_inst cmdline_parse_inst_t; * instruction * */ -typedef cmdline_parse_inst_t *cmdline_parse_ctx_t; +struct cmdline_parse_ctx { + const char *name; + cmdline_parse_inst_t *insts[]; +}; +typedef struct cmdline_parse_ctx cmdline_parse_ctx_t; + +/* return status for parsing */ +#define CMDLINE_PARSE_SUCCESS 0 +#define CMDLINE_PARSE_EMPTY -1 +#define CMDLINE_PARSE_NOMATCH -2 +#define CMDLINE_PARSE_AMBIGUOUS -3 +#define CMDLINE_PARSE_UNTERMINATED_QUOTE -4 /** * Try to parse a buffer according to the specified context. The - * argument buf must ends with "\n\0". The function returns - * CMDLINE_PARSE_AMBIGUOUS, CMDLINE_PARSE_NOMATCH or - * CMDLINE_PARSE_BAD_ARGS on error. Else it calls the associated - * function (defined in the context) and returns 0 - * (CMDLINE_PARSE_SUCCESS). + * argument linebuf must end with "\n\0". + * + * The function returns: + * - CMDLINE_PARSE_SUCCESS (0) on success + * - CMDLINE_PARSE_EMPTY if there is nothing to parse + * - CMDLINE_PARSE_NOMATCH if line does not match any command + * - CMDLINE_PARSE_AMBIGUOUS if several commands match + * - CMDLINE_PARSE_UNTERMINATED_QUOTE if a quote is used incorrectly */ -int cmdline_parse(struct cmdline *cl, const char *buf); +int cmdline_parse(cmdline_parse_ctx_t *ctx, const char *linebuf, void *opaque); + +/* return status for completion */ +#define CMDLINE_COMPLETE_APPEND 0 +#define CMDLINE_COMPLETE_NONE -1 +#define CMDLINE_COMPLETE_MANY -2 /** - * complete() must be called with *state==0 (try to complete) or - * with *state==-1 (just display choices), then called without - * modifying *state until it returns CMDLINE_PARSE_COMPLETED_BUFFER or - * CMDLINE_PARSE_COMPLETED_BUFFER. - * - * It returns < 0 on error. + * cmdline_complete() tries to complete the buffer given as a parameter. * - * Else it returns: - * - CMDLINE_PARSE_COMPLETED_BUFFER on completion (one possible - * choice). In this case, the chars are appended in dst buffer. - * - CMDLINE_PARSE_COMPLETE_AGAIN if there is several possible - * choices. In this case, you must call the function again, - * keeping the value of state intact. - * - CMDLINE_PARSE_COMPLETED_BUFFER when the iteration is - * finished. The dst is not valid for this last call. - * - * The returned dst buf ends with \0. + * It returns: + * - CMDLINE_COMPLETE_APPEND (0) on success, when a completion is + * done (one possible choice). In this case, the chars are + * appended in dst buffer. + * - CMDLINE_COMPLETE_NONE: error, no possible completion + * - CMDLINE_COMPLETE_MANY: error, many possble completions, need to call + * cmdline_help() function to see all the possibilities. */ -int cmdline_complete(struct cmdline *cl, const char *buf, int *state, +int cmdline_complete(cmdline_parse_ctx_t *ctx, const char *buf, char *dst, unsigned int size); +/** + * callback given to rdline_help() to display the content of the + * help. The first argument is an opaque pointer. The other args + * are buffer and size. + */ +typedef ssize_t (cmdline_write_t)(void *, void *, size_t); + +/** + * Display a contextual help using the write_buf() function pointer + * given as parameter (called with its opaque pointer). The contextual + * help depends on the buffer given. + */ +int cmdline_help(cmdline_parse_ctx_t *ctx, const char *buf, + cmdline_write_t *write_buf, void *opaque); /* return true if(!c || iscomment(c) || isblank(c) || * isendofline(c)) */ diff --git a/src/lib/cmdline_parse_etheraddr.c b/src/lib/cmdline_parse_etheraddr.c index 7a50e02..3c0f30b 100644 --- a/src/lib/cmdline_parse_etheraddr.c +++ b/src/lib/cmdline_parse_etheraddr.c @@ -70,14 +70,6 @@ #include "cmdline_parse.h" #include "cmdline_parse_etheraddr.h" -struct cmdline_token_ops cmdline_token_etheraddr_ops = { - .parse = cmdline_parse_etheraddr, - .complete_get_nb = NULL, - .complete_get_elt = NULL, - .get_help = cmdline_get_help_etheraddr, -}; - - #define ETHER_ADDRSTRLEN 18 #ifdef __linux__ @@ -108,7 +100,7 @@ my_ether_aton(const char *a) return (struct ether_addr *)ðer_addr; } -int +static int cmdline_parse_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk, const char *buf, void *res, unsigned ressize) { @@ -133,9 +125,18 @@ cmdline_parse_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk, return token_len; } -int cmdline_get_help_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk, - char *dstbuf, unsigned int size) +static int +cmdline_help_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk, + char *dstbuf, unsigned int size) { snprintf(dstbuf, size, "Ethernet address"); return 0; } + +struct cmdline_token_ops cmdline_token_etheraddr_ops = { + .parse = cmdline_parse_etheraddr, + .complete_start = NULL, + .complete_iterate = NULL, + .complete_end = NULL, + .help = cmdline_help_etheraddr, +}; diff --git a/src/lib/cmdline_parse_etheraddr.h b/src/lib/cmdline_parse_etheraddr.h index 23e54e9..90c2e2b 100644 --- a/src/lib/cmdline_parse_etheraddr.h +++ b/src/lib/cmdline_parse_etheraddr.h @@ -75,11 +75,6 @@ typedef struct cmdline_token_etheraddr cmdline_parse_token_etheraddr_t; extern struct cmdline_token_ops cmdline_token_etheraddr_ops; -int cmdline_parse_etheraddr(cmdline_parse_token_hdr_t *tk, const char *srcbuf, - void *res, unsigned ressize); -int cmdline_get_help_etheraddr(cmdline_parse_token_hdr_t *tk, char *dstbuf, - unsigned int size); - #define TOKEN_ETHERADDR_INITIALIZER(structure, field) \ { \ .hdr = { \ diff --git a/src/lib/cmdline_parse_ipaddr.c b/src/lib/cmdline_parse_ipaddr.c index cb41562..eab2319 100644 --- a/src/lib/cmdline_parse_ipaddr.c +++ b/src/lib/cmdline_parse_ipaddr.c @@ -93,13 +93,6 @@ #include "cmdline_parse.h" #include "cmdline_parse_ipaddr.h" -struct cmdline_token_ops cmdline_token_ipaddr_ops = { - .parse = cmdline_parse_ipaddr, - .complete_get_nb = NULL, - .complete_get_elt = NULL, - .get_help = cmdline_get_help_ipaddr, -}; - #define INADDRSZ 4 #define IN6ADDRSZ 16 @@ -289,7 +282,7 @@ inet_pton6(const char *src, unsigned char *dst) return (1); } -int +static int cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, unsigned ressize) { @@ -340,8 +333,9 @@ cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, } -int cmdline_get_help_ipaddr(cmdline_parse_token_hdr_t *tk, char *dstbuf, - unsigned int size) +static int +cmdline_help_ipaddr(cmdline_parse_token_hdr_t *tk, char *dstbuf, + unsigned int size) { struct cmdline_token_ipaddr *tk2 = (struct cmdline_token_ipaddr *)tk; @@ -370,3 +364,12 @@ int cmdline_get_help_ipaddr(cmdline_parse_token_hdr_t *tk, char *dstbuf, } return 0; } + +struct cmdline_token_ops cmdline_token_ipaddr_ops = { + .parse = cmdline_parse_ipaddr, + .complete_start = NULL, + .complete_iterate = NULL, + .complete_end = NULL, + .help = cmdline_help_ipaddr, +}; + diff --git a/src/lib/cmdline_parse_ipaddr.h b/src/lib/cmdline_parse_ipaddr.h index 33670eb..d28b4dc 100644 --- a/src/lib/cmdline_parse_ipaddr.h +++ b/src/lib/cmdline_parse_ipaddr.h @@ -90,11 +90,6 @@ typedef struct cmdline_token_ipaddr cmdline_parse_token_ipaddr_t; extern struct cmdline_token_ops cmdline_token_ipaddr_ops; -int cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *srcbuf, - void *res, unsigned ressize); -int cmdline_get_help_ipaddr(cmdline_parse_token_hdr_t *tk, char *dstbuf, - unsigned int size); - #define TOKEN_IPADDR_INITIALIZER(structure, field) \ { \ .hdr = { \ diff --git a/src/lib/cmdline_parse_num.c b/src/lib/cmdline_parse_num.c index 8ef2964..31ca78d 100644 --- a/src/lib/cmdline_parse_num.c +++ b/src/lib/cmdline_parse_num.c @@ -71,14 +71,6 @@ //#define debug_printf(args...) printf(args) #define debug_printf(args...) do {} while(0) -struct cmdline_token_ops cmdline_token_num_ops = { - .parse = cmdline_parse_num, - .complete_get_nb = NULL, - .complete_get_elt = NULL, - .get_help = cmdline_get_help_num, -}; - - enum num_parse_state_t { START, DEC_NEG, @@ -109,7 +101,7 @@ static const char * num_help[] = { #endif }; -static inline int +static int add_to_res(unsigned int c, uint64_t *res, unsigned int base) { /* overflow */ @@ -121,7 +113,8 @@ add_to_res(unsigned int c, uint64_t *res, unsigned int base) return 0; } -static int check_res_size(struct cmdline_token_num_data *nd, unsigned ressize) +static int +check_res_size(struct cmdline_token_num_data *nd, unsigned ressize) { switch (nd->type) { case INT8: @@ -157,7 +150,7 @@ static int check_res_size(struct cmdline_token_num_data *nd, unsigned ressize) } /* parse an int or a float */ -int +static int cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res, unsigned ressize) { @@ -510,8 +503,8 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, /* parse an int or a float */ -int -cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size) +static int +cmdline_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size) { struct cmdline_token_num_data nd; @@ -525,3 +518,12 @@ cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int s dstbuf[size-1] = '\0'; return 0; } + + +struct cmdline_token_ops cmdline_token_num_ops = { + .parse = cmdline_parse_num, + .complete_start = NULL, + .complete_iterate = NULL, + .complete_end = NULL, + .help = cmdline_help_num, +}; diff --git a/src/lib/cmdline_parse_num.h b/src/lib/cmdline_parse_num.h index 1b8c79d..8afac2f 100644 --- a/src/lib/cmdline_parse_num.h +++ b/src/lib/cmdline_parse_num.h @@ -90,11 +90,6 @@ typedef struct cmdline_token_num cmdline_parse_token_num_t; extern struct cmdline_token_ops cmdline_token_num_ops; -int cmdline_parse_num(cmdline_parse_token_hdr_t *tk, - const char *srcbuf, void *res, unsigned ressize); -int cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, - char *dstbuf, unsigned int size); - #define TOKEN_NUM_INITIALIZER(structure, field, numtype) \ { \ .hdr = { \ diff --git a/src/lib/cmdline_parse_string.c b/src/lib/cmdline_parse_string.c index 1b9fdb8..1b2c146 100644 --- a/src/lib/cmdline_parse_string.c +++ b/src/lib/cmdline_parse_string.c @@ -67,13 +67,6 @@ #include "cmdline_parse.h" #include "cmdline_parse_string.h" -struct cmdline_token_ops cmdline_token_string_ops = { - .parse = cmdline_parse_string, - .complete_get_nb = cmdline_complete_get_nb_string, - .complete_get_elt = cmdline_complete_get_elt_string, - .get_help = cmdline_get_help_string, -}; - #define MULTISTRING_HELP "Mul-choice STRING" #define ANYSTRING_HELP "Any STRING" #define FIXEDSTRING_HELP "Fixed STRING" @@ -102,8 +95,9 @@ get_next_token(const char *s) return NULL; } -static int parse_fixed_string(struct cmdline_token_string_data *sd, - const char *buf, unsigned token_len) +static int +parse_fixed_string(struct cmdline_token_string_data *sd, + const char *buf, unsigned token_len) { unsigned int conf_token_len; const char *str; @@ -126,7 +120,7 @@ static int parse_fixed_string(struct cmdline_token_string_data *sd, return -1; } -int +static int cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, unsigned ressize) { @@ -155,38 +149,31 @@ cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, return token_len; } -int cmdline_complete_get_nb_string(cmdline_parse_token_hdr_t *tk) +static int +cmdline_complete_string_start(cmdline_parse_token_hdr_t *tk, void **opaque) { struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk; struct cmdline_token_string_data *sd = &tk2->string_data;; - int ret=1; const char *str; - if (!sd->str) - return 0; - str = sd->str; - while( (str = get_next_token(str)) != NULL ) { - ret++; - } - return ret; + *opaque = (void *)str; + if (str == NULL) + return -1; /* no completion */ + return 0; } -int cmdline_complete_get_elt_string(cmdline_parse_token_hdr_t *tk, int idx, - char *dstbuf, unsigned int size) +static int +cmdline_complete_string_iterate(cmdline_parse_token_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) { - struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk; - struct cmdline_token_string_data *sd = &tk2->string_data;; const char *s; unsigned int len; - s = sd->str; - - while (idx-- && s) - s = get_next_token(s); - - if (!s) + s = *opaque; + if (s == NULL) return -1; + *opaque = (void *)get_next_token(s); len = get_token_len(s); if (len > size - 1) @@ -197,9 +184,9 @@ int cmdline_complete_get_elt_string(cmdline_parse_token_hdr_t *tk, int idx, return 0; } - -int cmdline_get_help_string(cmdline_parse_token_hdr_t *tk, char *dstbuf, - unsigned int size) +static int +cmdline_help_string(cmdline_parse_token_hdr_t *tk, char *dstbuf, + unsigned int size) { struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk; struct cmdline_token_string_data *sd = &tk2->string_data;; @@ -207,13 +194,11 @@ int cmdline_get_help_string(cmdline_parse_token_hdr_t *tk, char *dstbuf, s = sd->str; - if (s) { - if (get_next_token(s)) { + if (s != NULL) { + if (get_next_token(s)) strncpy(dstbuf, MULTISTRING_HELP, size); - } - else { + else strncpy(dstbuf, FIXEDSTRING_HELP, size); - } } else { strncpy(dstbuf, ANYSTRING_HELP, size); @@ -223,3 +208,11 @@ int cmdline_get_help_string(cmdline_parse_token_hdr_t *tk, char *dstbuf, return 0; } + +struct cmdline_token_ops cmdline_token_string_ops = { + .parse = cmdline_parse_string, + .complete_start = cmdline_complete_string_start, + .complete_iterate = cmdline_complete_string_iterate, + .complete_end = NULL, + .help = cmdline_help_string, +}; diff --git a/src/lib/cmdline_parse_string.h b/src/lib/cmdline_parse_string.h index d435f81..fc6013f 100644 --- a/src/lib/cmdline_parse_string.h +++ b/src/lib/cmdline_parse_string.h @@ -81,14 +81,6 @@ typedef struct cmdline_token_string cmdline_parse_token_string_t; extern struct cmdline_token_ops cmdline_token_string_ops; -int cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *srcbuf, - void *res, unsigned ressize); -int cmdline_complete_get_nb_string(cmdline_parse_token_hdr_t *tk); -int cmdline_complete_get_elt_string(cmdline_parse_token_hdr_t *tk, int idx, - char *dstbuf, unsigned int size); -int cmdline_get_help_string(cmdline_parse_token_hdr_t *tk, char *dstbuf, - unsigned int size); - #define TOKEN_STRING_INITIALIZER(structure, field, string) \ { \ .hdr = { \ diff --git a/src/lib/cmdline_rdline.c b/src/lib/cmdline_rdline.c index 4352b94..7f34946 100644 --- a/src/lib/cmdline_rdline.c +++ b/src/lib/cmdline_rdline.c @@ -65,15 +65,12 @@ #include #include #include +#include #include "cmdline_cirbuf.h" #include "cmdline_rdline.h" #include "cmdline_parse.h" -static void rdline_puts(struct rdline *rdl, const char *buf); -static void rdline_miniprintf(struct rdline *rdl, - const char *buf, unsigned int val); - #ifndef NO_RDLINE_HISTORY static void rdline_remove_old_history_item(struct rdline *rdl); static void rdline_remove_first_history_item(struct rdline *rdl); @@ -94,14 +91,17 @@ isblank2(char c) void rdline_init(struct rdline *rdl, - rdline_write_char_t *write_char, - rdline_validate_t *validate, - rdline_complete_t *complete) + int fd_in, int fd_out, + rdline_validate_t *validate, + rdline_complete_t *complete, + rdline_help_t *help) { memset(rdl, 0, sizeof(*rdl)); + rdl->fd_in = fd_in; + rdl->fd_out = fd_out; rdl->validate = validate; rdl->complete = complete; - rdl->write_char = write_char; + rdl->help = help; rdl->status = RDLINE_INIT; #ifndef NO_RDLINE_HISTORY cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE); @@ -111,18 +111,15 @@ rdline_init(struct rdline *rdl, void rdline_newline(struct rdline *rdl, const char *prompt) { - unsigned int i; - vt100_init(&rdl->vt100); cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); + /* if pointer is the same, don't copy it */ if (prompt != rdl->prompt) - memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1); - rdl->prompt_size = strlen(prompt); + snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt); - for (i=0 ; iprompt_size ; i++) - rdl->write_char(rdl, rdl->prompt[i]); + rdline_printf(rdl, "%s", rdl->prompt); rdl->status = RDLINE_RUNNING; #ifndef NO_RDLINE_HISTORY @@ -159,8 +156,7 @@ rdline_get_buffer(struct rdline *rdl) len_r = CIRBUF_GET_LEN(&rdl->right); memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r); - rdl->left_buf[len_l + len_r] = '\n'; - rdl->left_buf[len_l + len_r + 1] = '\0'; + rdl->left_buf[len_l + len_r] = '\0'; return rdl->left_buf; } @@ -173,13 +169,13 @@ display_right_buffer(struct rdline *rdl, int force) if (!force && CIRBUF_IS_EMPTY(&rdl->right)) return; - rdline_puts(rdl, vt100_clear_right); + rdline_printf(rdl, vt100_clear_right); CIRBUF_FOREACH(&rdl->right, i, tmp) { - rdl->write_char(rdl, tmp); + rdline_printf(rdl, "%c", tmp); } if (!CIRBUF_IS_EMPTY(&rdl->right)) - rdline_miniprintf(rdl, vt100_multi_left, - CIRBUF_GET_LEN(&rdl->right)); + rdline_printf(rdl, vt100_multi_left, + CIRBUF_GET_LEN(&rdl->right)); } void @@ -188,17 +184,16 @@ rdline_redisplay(struct rdline *rdl) unsigned int i; char tmp; - rdline_puts(rdl, vt100_home); - for (i=0 ; iprompt_size ; i++) - rdl->write_char(rdl, rdl->prompt[i]); + rdline_printf(rdl, vt100_home); + rdline_printf(rdl, "%s", rdl->prompt); CIRBUF_FOREACH(&rdl->left, i, tmp) { - rdl->write_char(rdl, tmp); + rdline_printf(rdl, "%c", tmp); } display_right_buffer(rdl, 1); } -int -rdline_char_in(struct rdline *rdl, char c) +static int +rdline_parse_char(struct rdline *rdl, char c) { unsigned int i; int cmd; @@ -207,11 +202,6 @@ rdline_char_in(struct rdline *rdl, char c) char *buf; #endif - if (rdl->status == RDLINE_EXITED) - return RDLINE_RES_EXITED; - if (rdl->status != RDLINE_RUNNING) - return RDLINE_RES_NOT_RUNNING; - cmd = vt100_parser(&rdl->vt100, c); if (cmd == VT100_NOT_COMPLETE) return RDLINE_RES_SUCCESS; @@ -225,7 +215,7 @@ rdline_char_in(struct rdline *rdl, char c) tmp = cirbuf_get_tail(&rdl->left); cirbuf_del_tail(&rdl->left); cirbuf_add_head(&rdl->right, tmp); - rdline_puts(rdl, vt100_left_arr); + rdline_printf(rdl, vt100_left_arr); break; case CMDLINE_KEY_CTRL_F: @@ -235,21 +225,21 @@ rdline_char_in(struct rdline *rdl, char c) tmp = cirbuf_get_head(&rdl->right); cirbuf_del_head(&rdl->right); cirbuf_add_tail(&rdl->left, tmp); - rdline_puts(rdl, vt100_right_arr); + rdline_printf(rdl, vt100_right_arr); break; case CMDLINE_KEY_WLEFT: while (! CIRBUF_IS_EMPTY(&rdl->left) && (tmp = cirbuf_get_tail(&rdl->left)) && isblank2(tmp)) { - rdline_puts(rdl, vt100_left_arr); + rdline_printf(rdl, vt100_left_arr); cirbuf_del_tail(&rdl->left); cirbuf_add_head(&rdl->right, tmp); } while (! CIRBUF_IS_EMPTY(&rdl->left) && (tmp = cirbuf_get_tail(&rdl->left)) && !isblank2(tmp)) { - rdline_puts(rdl, vt100_left_arr); + rdline_printf(rdl, vt100_left_arr); cirbuf_del_tail(&rdl->left); cirbuf_add_head(&rdl->right, tmp); } @@ -259,14 +249,14 @@ rdline_char_in(struct rdline *rdl, char c) while (! CIRBUF_IS_EMPTY(&rdl->right) && (tmp = cirbuf_get_head(&rdl->right)) && isblank2(tmp)) { - rdline_puts(rdl, vt100_right_arr); + rdline_printf(rdl, vt100_right_arr); cirbuf_del_head(&rdl->right); cirbuf_add_tail(&rdl->left, tmp); } while (! CIRBUF_IS_EMPTY(&rdl->right) && (tmp = cirbuf_get_head(&rdl->right)) && !isblank2(tmp)) { - rdline_puts(rdl, vt100_right_arr); + rdline_printf(rdl, vt100_right_arr); cirbuf_del_head(&rdl->right); cirbuf_add_tail(&rdl->left, tmp); } @@ -274,28 +264,32 @@ rdline_char_in(struct rdline *rdl, char c) case CMDLINE_KEY_BKSPACE: if(!cirbuf_del_tail_safe(&rdl->left)) { - rdline_puts(rdl, vt100_bs); + rdline_printf(rdl, vt100_bs); display_right_buffer(rdl, 1); } break; case CMDLINE_KEY_META_BKSPACE: case CMDLINE_KEY_CTRL_W: - while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) { - rdline_puts(rdl, vt100_bs); + while (! CIRBUF_IS_EMPTY(&rdl->left) && + isblank2(cirbuf_get_tail(&rdl->left))) { + rdline_printf(rdl, vt100_bs); cirbuf_del_tail(&rdl->left); } - while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) { - rdline_puts(rdl, vt100_bs); + while (! CIRBUF_IS_EMPTY(&rdl->left) && + !isblank2(cirbuf_get_tail(&rdl->left))) { + rdline_printf(rdl, vt100_bs); cirbuf_del_tail(&rdl->left); } display_right_buffer(rdl, 1); break; case CMDLINE_KEY_META_D: - while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right))) + while (! CIRBUF_IS_EMPTY(&rdl->right) && + isblank2(cirbuf_get_head(&rdl->right))) cirbuf_del_head(&rdl->right); - while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right))) + while (! CIRBUF_IS_EMPTY(&rdl->right) && + !isblank2(cirbuf_get_head(&rdl->right))) cirbuf_del_head(&rdl->right); display_right_buffer(rdl, 1); break; @@ -315,8 +309,8 @@ rdline_char_in(struct rdline *rdl, char c) case CMDLINE_KEY_CTRL_A: if (CIRBUF_IS_EMPTY(&rdl->left)) break; - rdline_miniprintf(rdl, vt100_multi_left, - CIRBUF_GET_LEN(&rdl->left)); + rdline_printf(rdl, vt100_multi_left, + CIRBUF_GET_LEN(&rdl->left)); while (! CIRBUF_IS_EMPTY(&rdl->left)) { tmp = cirbuf_get_tail(&rdl->left); cirbuf_del_tail(&rdl->left); @@ -327,8 +321,8 @@ rdline_char_in(struct rdline *rdl, char c) case CMDLINE_KEY_CTRL_E: if (CIRBUF_IS_EMPTY(&rdl->right)) break; - rdline_miniprintf(rdl, vt100_multi_right, - CIRBUF_GET_LEN(&rdl->right)); + rdline_printf(rdl, vt100_multi_right, + CIRBUF_GET_LEN(&rdl->right)); while (! CIRBUF_IS_EMPTY(&rdl->right)) { tmp = cirbuf_get_head(&rdl->right); cirbuf_del_head(&rdl->right); @@ -341,7 +335,7 @@ rdline_char_in(struct rdline *rdl, char c) cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE); rdl->kill_size = CIRBUF_GET_LEN(&rdl->right); cirbuf_del_buf_head(&rdl->right, rdl->kill_size); - rdline_puts(rdl, vt100_clear_right); + rdline_printf(rdl, vt100_clear_right); break; case CMDLINE_KEY_CTRL_Y: @@ -350,7 +344,7 @@ rdline_char_in(struct rdline *rdl, char c) RDLINE_BUF_SIZE && i < rdl->kill_size) { cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]); - rdl->write_char(rdl, rdl->kill_buf[i]); + rdline_printf(rdl, "%c", rdl->kill_buf[i]); i++; } display_right_buffer(rdl, 0); @@ -358,7 +352,7 @@ rdline_char_in(struct rdline *rdl, char c) #endif /* !NO_RDLINE_KILL_BUF */ case CMDLINE_KEY_CTRL_C: - rdline_puts(rdl, "\r\n"); + rdline_printf(rdl, "\r\n"); rdline_newline(rdl, rdl->prompt); break; @@ -366,66 +360,64 @@ rdline_char_in(struct rdline *rdl, char c) rdline_redisplay(rdl); break; - case CMDLINE_KEY_TAB: - case CMDLINE_KEY_HELP: + case CMDLINE_KEY_HELP: { + if (rdl->help == NULL) + break; + + cirbuf_align_left(&rdl->left); + rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0'; + rdline_printf(rdl, "\r\n"); + rdl->help(rdl, rdl->left_buf, rdline_write, rdl); + rdline_redisplay(rdl); + break; + } + + case CMDLINE_KEY_TAB: { + char tmp_buf[CMDLINE_MAX_TOKEN_SIZE]; + int ret; //, curline = 0; + unsigned int tmp_size; + + if (rdl->complete == NULL) + break; + cirbuf_align_left(&rdl->left); rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0'; - if (rdl->complete) { - char tmp_buf[CMDLINE_MAX_TOKEN_SIZE]; - int complete_state; - int ret; - unsigned int tmp_size; - - if (cmd == CMDLINE_KEY_TAB) - complete_state = 0; - else - complete_state = -1; - - /* see in parse.h for help on complete() */ - ret = rdl->complete(rdl, rdl->left_buf, - tmp_buf, sizeof(tmp_buf), - &complete_state); - /* no completion or error */ - if (ret <= 0) { - return RDLINE_RES_COMPLETE; - } - - tmp_size = strlen(tmp_buf); - /* add chars */ - if (ret == RDLINE_RES_COMPLETE) { - i=0; - while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) < - RDLINE_BUF_SIZE && - i < tmp_size) { - cirbuf_add_tail(&rdl->left, tmp_buf[i]); - rdl->write_char(rdl, tmp_buf[i]); - i++; - } - display_right_buffer(rdl, 1); - return RDLINE_RES_COMPLETE; /* ?? */ - } - - /* choice */ - rdline_puts(rdl, "\r\n"); - while (ret) { - rdl->write_char(rdl, ' '); - for (i=0 ; tmp_buf[i] ; i++) - rdl->write_char(rdl, tmp_buf[i]); - rdline_puts(rdl, "\r\n"); - ret = rdl->complete(rdl, rdl->left_buf, - tmp_buf, sizeof(tmp_buf), - &complete_state); - } + /* see in parse.h for help on complete() */ + ret = rdl->complete(rdl, rdl->left_buf, + tmp_buf, sizeof(tmp_buf)); + /* no completion or error */ + if (ret == CMDLINE_COMPLETE_NONE || + ret == CMDLINE_COMPLETE_MANY) { + cirbuf_align_left(&rdl->left); + rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0'; + rdline_printf(rdl, "\r\n"); + rdl->help(rdl, rdl->left_buf, rdline_write, rdl); rdline_redisplay(rdl); + break; } - return RDLINE_RES_COMPLETE; + + tmp_size = strlen(tmp_buf); + /* add chars */ + i = 0; + while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) < + RDLINE_BUF_SIZE && + i < tmp_size) { + cirbuf_add_tail(&rdl->left, tmp_buf[i]); + rdline_printf(rdl, "%c", tmp_buf[i]); + i++; + } + display_right_buffer(rdl, 1); + break; + } case CMDLINE_KEY_RETURN: case CMDLINE_KEY_RETURN2: - rdline_get_buffer(rdl); + cirbuf_align_left(&rdl->left); + rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\n'; + rdl->left_buf[CIRBUF_GET_LEN(&rdl->left) + 1] = '\0'; rdl->status = RDLINE_INIT; - rdline_puts(rdl, "\r\n"); + rdline_printf(rdl, "\r\n"); #ifndef NO_RDLINE_HISTORY if (rdl->history_cur_line != -1) rdline_remove_first_history_item(rdl); @@ -497,12 +489,56 @@ rdline_char_in(struct rdline *rdl, char c) if (cirbuf_add_tail_safe(&rdl->left, c)) return RDLINE_RES_SUCCESS; - rdl->write_char(rdl, c); + rdline_printf(rdl, "%c", c); display_right_buffer(rdl, 0); return RDLINE_RES_SUCCESS; } +int +rdline_char_in(struct rdline *rdl, char c) +{ + int ret, same = 0; + const char *history, *buffer; + + if (rdl->status == RDLINE_EXITED) + return RDLINE_RES_EXITED; + if (rdl->status != RDLINE_RUNNING) + return RDLINE_RES_NOT_RUNNING; + + ret = rdline_parse_char(rdl, c); + + /* add line to history */ + if (ret == RDLINE_RES_VALIDATED) { + buffer = rdline_get_buffer(rdl); + history = rdline_get_history_item(rdl, 0); + if (history) + same = !strcmp(buffer, history); + + if (strlen(buffer) >= 1 && same == 0) + rdline_add_history(rdl, buffer); + } + + return ret; +} + +int +rdline(struct rdline *rdl, const char *prompt) +{ + char c; + int ret = RDLINE_RES_NOT_RUNNING; + + rdline_newline(rdl, prompt); + while (1) { + if (read(rdl->fd_in, &c, 1) < 0) + break; + ret = rdline_char_in(rdl, c); + if (ret != RDLINE_RES_SUCCESS) + break; + } + + return ret; +} /* HISTORY */ @@ -579,20 +615,13 @@ rdline_get_history_item(struct rdline * rdl, unsigned int idx) int rdline_add_history(struct rdline * rdl, const char * buf) { - unsigned int len, i; + unsigned int len; len = strlen(buf); - for (i=0; i= RDLINE_HISTORY_BUF_SIZE ) + if (len >= RDLINE_HISTORY_BUF_SIZE) return -1; - while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) { + while (len >= CIRBUF_GET_FREELEN(&rdl->history)) { rdline_remove_old_history_item(rdl); } @@ -610,52 +639,68 @@ rdline_clear_history(struct rdline * rdl) #else /* !NO_RDLINE_HISTORY */ -int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;} -void rdline_clear_history(struct rdline * rdl) {} -char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;} +int rdline_add_history(struct rdline * rdl, const char * buf) +{ + return -1; +} +void rdline_clear_history(struct rdline * rdl) +{ + return; +} -#endif /* !NO_RDLINE_HISTORY */ +char * rdline_get_history_item(struct rdline * rdl, unsigned int i) +{ + return NULL; +} -/* STATIC USEFUL FUNCS */ +#endif /* !NO_RDLINE_HISTORY */ -static void -rdline_puts(struct rdline * rdl, const char * buf) + +ssize_t +rdline_write(const struct rdline *rdl, void *buf, size_t count) { - char c; - while ( (c = *(buf++)) != '\0' ) { - rdl->write_char(rdl, c); - } + return write(rdl->fd_out, buf, count); } -/* a very very basic printf with one arg and one format 'u' */ -static void -rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val) +int +rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap) { - char c, started=0, div=100; + int ret; +#ifndef _GNU_SOURCE + char *buf; +#endif - while ( (c=*(buf++)) ) { - if (c != '%') { - rdl->write_char(rdl, c); - continue; - } - c = *(buf++); - if (c != 'u') { - rdl->write_char(rdl, '%'); - rdl->write_char(rdl, c); - continue; - } - /* val is never more than 255 */ - while (div) { - c = (char)(val / div); - if (c || started) { - rdl->write_char(rdl, (char)(c+'0')); - started = 1; - } - val %= div; - div /= 10; - } - } + if (rdl->fd_out < 0) + return -1; + +#ifdef _GNU_SOURCE + ret = vdprintf(rdl->fd_out, fmt, ap); +#else + buf = malloc(BUFSIZ); + if (buf == NULL) + return -1; + + ret = vsnprintf(buf, BUFSIZ, fmt, ap); + + if (ret > 0) + write(rdl->fd_out, buf, (ret >= BUFSIZ) ? BUFSIZ : ret); + free(buf); +#endif + + return ret; } +int +rdline_printf(const struct rdline *rdl, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = rdline_vprintf(rdl, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/src/lib/cmdline_rdline.h b/src/lib/cmdline_rdline.h index 87a1789..cee438a 100644 --- a/src/lib/cmdline_rdline.h +++ b/src/lib/cmdline_rdline.h @@ -63,26 +63,19 @@ #define _RDLINE_H_ /** - * This file is a small equivalent to the GNU readline library, but it - * was originally designed for small systems, like Atmel AVR - * microcontrollers (8 bits). Indeed, we don't use any malloc that is - * sometimes not implemented (or just not recommended) on such - * systems. + * This file is a small equivalent to the GNU readline library, it was + * originally designed for very small systems (an 8-bits Atmel AVR + * microcontroller), but the library can now run on many emmbedded + * systems with or without OS. * * Obviously, it does not support as many things as the GNU readline, * but at least it supports some interresting features like a kill * buffer and a command history. * * It also have a feature that does not have the GNU readline (as far - * as I know): we can have several instances of it running at the same - * time, even on a monothread program, since it works with callbacks. - * - * The lib is designed for a client-side or a server-side use: - * - server-side: the server receives all data from a socket, including - * control chars, like arrows, tabulations, ... The client is - * very simple, it can be a telnet or a minicom through a serial line. - * - client-side: the client receives its data through its stdin for - * instance. + * as I know): it is possible to have several instances of readline + * running at the same time, even on a monothread program, since it + * works with callbacks. */ #include @@ -94,6 +87,7 @@ #define RDLINE_VT100_BUF_SIZE 8 #define RDLINE_HISTORY_BUF_SIZE BUFSIZ #define RDLINE_HISTORY_MAX_LINE 64 +#define RDLINE_MAX_LINES 39 enum rdline_status { RDLINE_INIT, @@ -103,15 +97,26 @@ enum rdline_status { struct rdline; -typedef int (rdline_write_char_t)(struct rdline *rdl, char); +/** + * callback given to rdline_help() to display the content of the + * help. The first argument is an opaque pointer. The other args + * are buffer and size. + */ +typedef ssize_t (rdline_write_t)(const struct rdline *, void *, size_t); + + typedef void (rdline_validate_t)(struct rdline *rdl, const char *buf, unsigned int size); typedef int (rdline_complete_t)(struct rdline *rdl, const char *buf, - char *dstbuf, unsigned int dstsize, - int *state); + char *dstbuf, unsigned int dstsize); +typedef int (rdline_help_t)(struct rdline *rdl, const char *buf, + rdline_write_t *write, void *opaque); struct rdline { enum rdline_status status; + int fd_in; + int fd_out; + /* rdline bufs */ struct cirbuf left; struct cirbuf right; @@ -119,7 +124,6 @@ struct rdline { char right_buf[RDLINE_BUF_SIZE]; char prompt[RDLINE_PROMPT_SIZE]; - unsigned int prompt_size; #ifndef NO_RDLINE_KILL_BUF char kill_buf[RDLINE_BUF_SIZE]; @@ -134,9 +138,9 @@ struct rdline { #endif /* callbacks and func pointers */ - rdline_write_char_t *write_char; rdline_validate_t *validate; rdline_complete_t *complete; + rdline_help_t *help; /* vt100 parser */ struct cmdline_vt100 vt100; @@ -146,50 +150,76 @@ struct rdline { }; /** - * Init fields for a struct rdline. Call this only once at the beginning - * of your program. - * \param rdl A pointer to an uninitialized struct rdline - * \param write_char The function used by the function to write a character - * \param validate A pointer to the function to execute when the - * user validates the buffer. - * \param complete A pointer to the function to execute when the - * user completes the buffer. + * Init fields for a struct rdline. + * + * @param rdl A pointer to an uninitialized struct rdline + * @param fd_in + * Input file descriptor + * @param fd_out + * Output file descriptor + * @param validate + * A pointer to the function to execute when the user validates the + * buffer. + * @param complete + * A pointer to the function to execute when the user completes the + * buffer. + * @param help + * A pointer to the function to execute when the user ask for + * contextual help. */ void rdline_init(struct rdline *rdl, - rdline_write_char_t *write_char, + int fd_in, int fd_out, rdline_validate_t *validate, - rdline_complete_t *complete); + rdline_complete_t *complete, + rdline_help_t *help); /** * Init the current buffer, and display a prompt. - * \param rdl A pointer to a struct rdline - * \param prompt A string containing the prompt + * + * Also set the rdline status to "running", overriding previous + * rdline_stop() or rdline_quit(). + * + * @param rdl + * A pointer to an initialized struct rdline + * @param prompt + * A string containing the prompt */ void rdline_newline(struct rdline *rdl, const char *prompt); /** - * Call it and all received chars will be ignored. - * \param rdl A pointer to a struct rdline + * Ignore all subsequent received chars. + * + * @param rdl + * A pointer to a struct rdline */ void rdline_stop(struct rdline *rdl); /** + * Exit from running rdline loop + * * Same than rdline_stop() except that next calls to rdline_char_in() - * will return RDLINE_RES_EXITED. - * \param rdl A pointer to a struct rdline + * will return RDLINE_RES_EXITED. Hence, any running rdline() function is + * interrupted. + * + * @param rdl + * A pointer to a struct rdline */ void rdline_quit(struct rdline *rdl); /** * Restart after a call to rdline_stop() or rdline_quit() - * \param rdl A pointer to a struct rdline + * + * @param rdl + * A pointer to a struct rdline */ void rdline_restart(struct rdline *rdl); /** * Redisplay the current buffer - * \param rdl A pointer to a struct rdline + * + * @param rdl + * A pointer to a struct rdline */ void rdline_redisplay(struct rdline *rdl); @@ -197,48 +227,127 @@ void rdline_redisplay(struct rdline *rdl); /* return status for rdline_char_in() */ #define RDLINE_RES_SUCCESS 0 #define RDLINE_RES_VALIDATED 1 -#define RDLINE_RES_COMPLETE 2 #define RDLINE_RES_NOT_RUNNING -1 #define RDLINE_RES_EOF -2 #define RDLINE_RES_EXITED -3 /** - * append a char to the readline buffer. - * Return RDLINE_RES_VALIDATE when the line has been validated. - * Return RDLINE_RES_COMPLETE when the user asked to complete the buffer. - * Return RDLINE_RES_NOT_RUNNING if it is not running. - * Return RDLINE_RES_EOF if EOF (ctrl-d on an empty line). - * Else return RDLINE_RES_SUCCESS. - * XXX error case when the buffer is full ? + * Append a char to the readline buffer. * - * \param rdl A pointer to a struct rdline - * \param c The character to append + * @param rdl + * A pointer to a struct rdline + * @param c + * The character to append + * @return + * - RDLINE_RES_VALIDATED when the line has been validated. + * - RDLINE_RES_NOT_RUNNING if it is not running. + * - RDLINE_RES_EOF if EOF (ctrl-d on an empty line). + * - RDLINE_RES_EXITED if user called rdline_quit() + * - Else return RDLINE_RES_SUCCESS. */ int rdline_char_in(struct rdline *rdl, char c); +/** + * Read (and edit) a line + * + * @param rdl + * A pointer to a struct rdline + * @param prompt + * The prompt string + * @return + * - RDLINE_RES_VALIDATED when the line has been validated. + * - RDLINE_RES_NOT_RUNNING if it is not running. + * - RDLINE_RES_EOF if EOF (ctrl-d on an empty line). + * - RDLINE_RES_EXITED if user called rdline_quit() + */ +int rdline(struct rdline *rdl, const char *prompt); + +/** + * write a buffer on rdline file descriptor + * + * @param rdl + * The rdline descriptor + * @param buf + * Pointer to the buffer + * @param count + * Number of bytes to write + * @return + * On success, the number of bytes written is returned (zero + * indicates nothing was written). On error, -1 is returned, and + * errno is set appropriately + */ +ssize_t rdline_write(const struct rdline *rdl, void *buf, size_t count); + +/** + * write on rdline file descriptor according to a format string + * + * @param rdl + * The rdline descriptor + * @param fmt + * The format strings + * @return + * Upon successful return, these functions return the number of + * characters printed (not including the trailing '\0' used to end + * output to strings). On error, a negative value is returned. + */ +int rdline_printf(const struct rdline *rdl, const char *fmt, ...); + +/** + * write on rdline file descriptor according to a format string + * + * @param rdl + * The rdline descriptor + * @param fmt + * The format strings + * @param ap + * Variable argument list + * @return + * Upon successful return, these functions return the number of + * characters printed (not including the trailing '\0' used to end + * output to strings). On error, a negative value is returned. + */ +int rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap); + /** * Return the current buffer, terminated by '\0'. - * \param rdl A pointer to a struct rdline + * + * @param rdl + * A pointer to a struct rdline + * @return + * The rdline buffer */ const char *rdline_get_buffer(struct rdline *rdl); - /** * Add the buffer to history. - * return < 0 on error. - * \param rdl A pointer to a struct rdline - * \param buf A buffer that is terminated by '\0' + * + * @param rdl + * A pointer to a struct rdline + * @param buf + * A buffer that is terminated by '\0' + * @return + * - 0 on success + * - negative on error */ int rdline_add_history(struct rdline *rdl, const char *buf); /** * Clear current history - * \param rdl A pointer to a struct rdline + * + * @param rdl + * A pointer to a struct rdline */ void rdline_clear_history(struct rdline *rdl); /** * Get the i-th history item + * + * @param rdl + * A pointer to a struct rdline + * @param i + * The index of the history item + * @return + * The i-th string of history, or NULL on error. */ char *rdline_get_history_item(struct rdline *rdl, unsigned int i);