cmdline: big rework and clean of cmdline library
authorOlivier Matz <zer0@droids-corp.org>
Sat, 25 Dec 2010 18:36:01 +0000 (19:36 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Sun, 13 Mar 2011 10:09:19 +0000 (11:09 +0100)
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 <zer0@droids-corp.org>
26 files changed:
Makefile
TODO.txt
build/calculator_standalone/Makefile
src/calculator_server/commands.c
src/calculator_standalone/commands.c
src/extension_example/commands.c
src/extension_example/main.c
src/extension_example/parse_obj_list.c
src/extension_example/parse_obj_list.h
src/genconf/commands.c
src/genconf/parse_confnode.c
src/genconf/parse_confnode.h
src/lib/cmdline.c
src/lib/cmdline.h
src/lib/cmdline_parse.c
src/lib/cmdline_parse.h
src/lib/cmdline_parse_etheraddr.c
src/lib/cmdline_parse_etheraddr.h
src/lib/cmdline_parse_ipaddr.c
src/lib/cmdline_parse_ipaddr.h
src/lib/cmdline_parse_num.c
src/lib/cmdline_parse_num.h
src/lib/cmdline_parse_string.c
src/lib/cmdline_parse_string.h
src/lib/cmdline_rdline.c
src/lib/cmdline_rdline.h

index 450ab25..9656186 100644 (file)
--- 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
 # 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
 CFLAGS += -DCMDLINE_HAVE_SOCKET
 
 CFLAGS += -Wall -Werror
index 2e8fe54..dbdb2a1 100644 (file)
--- 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 ?
 - 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
+
index 523a8a8..53383be 100755 (executable)
@@ -2,7 +2,7 @@ SRC = main.c commands.c
 
 OBJS = $(SRC:%.c=%.o)
 
 
 OBJS = $(SRC:%.c=%.o)
 
-PROG = server
+PROG = standalone
 LDLIB = ../lib/libcmdline.a
 
 all: $(PROG)
 LDLIB = ../lib/libcmdline.a
 
 all: $(PROG)
index be00d5a..93c11d6 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <string.h>
+#include <stdarg.h>
 #include <netinet/in.h>
 
 #include <cmdline_parse.h>
 #include <netinet/in.h>
 
 #include <cmdline_parse.h>
@@ -164,10 +165,13 @@ cmdline_parse_inst_t cmd_help = {
 /****** CONTEXT (list of instruction) */
 
 /* in progmem */
 /****** 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,
+       },
 };
 
 };
 
index be00d5a..93c11d6 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <string.h>
+#include <stdarg.h>
 #include <netinet/in.h>
 
 #include <cmdline_parse.h>
 #include <netinet/in.h>
 
 #include <cmdline_parse.h>
@@ -164,10 +165,13 @@ cmdline_parse_inst_t cmd_help = {
 /****** CONTEXT (list of instruction) */
 
 /* in progmem */
 /****** 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,
+       },
 };
 
 };
 
index fbef8c9..38b25c7 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
@@ -146,9 +147,12 @@ cmdline_parse_inst_t cmd_obj_add = {
 /****** CONTEXT (list of instruction) */
 
 /* in progmem */
 /****** 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,
+       },
 };
 
 };
 
index 9cf1371..ed8c871 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdint.h>
 #include <errno.h>
 #include <math.h>
 #include <stdint.h>
 #include <errno.h>
 #include <math.h>
+#include <stdarg.h>
 
 #include <netinet/in.h>
 
 
 #include <netinet/in.h>
 
index aa743cf..d561519 100644 (file)
 #include "parse_obj_list.h"
 
 /* This file is an example of extension of libcmdline. It provides an
 #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)
 {
 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;
        }
                        continue;
                break;
        }
-       if (!o) /* not found */
+       if (o == NULL) /* not found */
                return -1;
 
        /* store the address of object in structure */
                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;
 }
 
        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;
 {
        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;
        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;
 
                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);
                return -1;
 
        strcpy(dstbuf, o->name);
+       o = SLIST_NEXT(o, next);
+       *opaque = o;
        return 0;
 }
 
 
        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;
 }
 {
        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,
+};
index 3bde83e..0148520 100644 (file)
@@ -56,13 +56,6 @@ typedef struct token_obj_list parse_token_obj_list_t;
 
 extern struct cmdline_token_ops token_obj_list_ops;
 
 
 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 = {                                                    \
 #define TOKEN_OBJ_LIST_INITIALIZER(structure, field, obj_list_ptr)  \
 {                                                                  \
        .hdr = {                                                    \
index 260502c..957bbef 100644 (file)
@@ -31,6 +31,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <unistd.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <unistd.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -407,22 +408,24 @@ struct cmd_set_str_result {
 static int get_str(struct cmdline *cl, char *buf, int len)
 {
        struct rdline rl;
 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';
 
        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;
        rl.opaque = cl;
+
+       /* XXX use rdline() */
        rdline_newline(&rl, "edit> ");
 
        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);
                        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)
                        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 */
 /****** 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,
+       },
 };
 
 };
 
index b25166f..21ccaac 100644 (file)
@@ -29,6 +29,7 @@
 #include <inttypes.h>
 #include <ctype.h>
 #include <string.h>
 #include <inttypes.h>
 #include <ctype.h>
 #include <string.h>
+#include <stdlib.h>
 #include <netinet/in.h>
 #include <sys/queue.h>
 
 #include <netinet/in.h>
 #include <sys/queue.h>
 
 #include "conf_htable.h"
 #include "parse_confnode.h"
 
 #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 '*')*/
 /* 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;
 {
        if (s == pattern)
                return 0;
@@ -95,46 +90,6 @@ find_in_children(struct confnode_list *l, struct confnode *n,
 }
 
 static int
 }
 
 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)
 {
 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;
 }
 
        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 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 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;
        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;
        if (n == NULL)
                return -1;
+       cb->cur = get_next_node(cb->start, n, flags, mask);
 
        len = strlen(n->name) + 1;
        if (len > size)
 
        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;
 }
 
        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;
 }
 {
        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,
+};
+
index 83a925a..5f19964 100644 (file)
@@ -44,13 +44,6 @@ typedef struct token_conf_node parse_token_conf_node_t;
 
 extern struct cmdline_token_ops token_conf_node_ops;
 
 
 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)          \
 #define TOKEN_CONF_NODE_INITIALIZER(structure, field,              \
                                    root_node_ptr, cur_node_ptr,    \
                                    node_flags, node_mask)          \
index d44e44b..6e8d7b8 100644 (file)
 #include "cmdline.h"
 
 static void
 #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;
 {
        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)
        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_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;
 {
        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;
        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)
 {
 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;
 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;
        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;
 
        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);
        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);
 }
 
        free(cl);
 }
 
-void
+int
 cmdline_printf(const struct cmdline *cl, const char *fmt, ...)
 {
        va_list ap;
 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;
        int ret;
-       char *buf;
 
 
-       if (cl->s_out < 0)
-               return;
-
-       buf = malloc(BUFSIZ);
-       if (buf == NULL)
-               return;
        va_start(ap, fmt);
        va_start(ap, fmt);
-       ret = vsnprintf(buf, BUFSIZ, fmt, ap);
+       ret = rdline_vprintf(&cl->rdl, fmt, ap);
        va_end(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)
 {
 int
 cmdline_in(struct cmdline *cl, const char *buf, int size)
 {
-       const char *history, *buffer;
        int ret = 0;
        int ret = 0;
-       int i, same;
+       int i;
 
 
-       for (i=0; i<size; i++) {
+       for (i = 0; i < size; i++) {
                ret = rdline_char_in(&cl->rdl, buf[i]);
 
                ret = rdline_char_in(&cl->rdl, 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);
                        rdline_newline(&cl->rdl, cl->prompt);
-               }
                else if (ret == RDLINE_RES_EOF)
                        return -1;
                else if (ret == RDLINE_RES_EXITED)
                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;
 }
 
        return i;
 }
 
+/* Interrupt a running command line process */
 void
 cmdline_quit(struct cmdline *cl)
 {
        rdline_quit(&cl->rdl);
 }
 
 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)
 {
 void
 cmdline_interact(struct cmdline *cl)
 {
index d8a7384..20321e8 100644 (file)
@@ -75,13 +75,59 @@ struct cmdline {
 #endif
 };
 
 #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);
 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_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_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);
 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_ */
 void cmdline_quit(struct cmdline *cl);
 
 #endif /* _CMDLINE_SOCKET_H_ */
index 060a8e9..dfc17f1 100644 (file)
 #include <ctype.h>
 #include <termios.h>
 #include <errno.h>
 #include <ctype.h>
 #include <termios.h>
 #include <errno.h>
+#include <stdarg.h>
 
 #include <netinet/in.h>
 
 #include "cmdline_parse.h"
 #include "cmdline.h"
 
 
 #include <netinet/in.h>
 
 #include "cmdline_parse.h"
 #include "cmdline.h"
 
-//#define CMDLINE_DEBUG
 //#define debug_printf printf
 #define debug_printf(args...) do {} while(0)
 
 //#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
 /* 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;
 }
 
        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;
 
 {
        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;
        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++];
        }
 
                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 (quoted)
                return -EINVAL;
 
-       if (d >= (dstlen-1))
-               return -EMSGSIZE;
        dst[d++] = '\0';
        return  s;
 }
 
        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.
  * 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
  */
 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 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;
        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 */
 
        /* check if we match all tokens of inst */
-       while (token_p && (!nb_match_token || i<nb_match_token)) {
+       while (token) {
+
+               /* we matched enough tokens, return success */
+               if (nb_match_token != 0 && token_num >= nb_match_token)
+                       return 0;
+
                debug_printf("TK\n");
                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;
 
                        break;
 
-               n = cmdline_unquote_token(token_str, sizeof(token_str), buf);
-               if (n == -EINVAL)
-                       return -EINVAL;
-
+               /* parse this token */
                if (resbuf == NULL)
                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;
                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;
                                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);
                if (res < 0)
                        break;
 
                debug_printf("TK parsed (len=%d)\n", n);
-               i++;
-               buf += n;
-
+               linebuf += n;
                token_num ++;
                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 */
        }
 
        /* does not match */
-       if (i==0)
+       if (token_num == 0)
                return -ENOENT;
 
                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 */
        /* we don't match all the tokens */
-       if (token_p) {
-               return i;
-       }
+       if (token)
+               return token_num;
 
        /* are there are some tokens more */
 
        /* 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 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;
                        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 !! */
 
        /* 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 */
 
                /* 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 */
        }
 
        /* 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 {
        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_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;
        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 ; i<n ; i++) {
-                               if (token_hdr.ops->complete_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 ; i<n ; i++) {
-                       if (token_hdr.ops->complete_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 ? */
 
                        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;
 }
 
 }
 
index e9e6308..5112ab5 100644 (file)
 #define offsetof(type, field)  ((size_t) &( ((type *)0)->field) )
 #endif
 
 #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
 
 #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
  * 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.
  *
  * 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);
  * -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);
                                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) */
        /** 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;
 };
 
 struct cmdline;
@@ -146,40 +145,65 @@ typedef struct cmdline_inst cmdline_parse_inst_t;
  * instruction
  *
  */
  * 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
 
 /**
  * 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);
 
                     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)) */
 
 /* return true if(!c || iscomment(c) || isblank(c) ||
  * isendofline(c)) */
index 7a50e02..3c0f30b 100644 (file)
 #include "cmdline_parse.h"
 #include "cmdline_parse_etheraddr.h"
 
 #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__
 #define ETHER_ADDRSTRLEN 18
 
 #ifdef __linux__
@@ -108,7 +100,7 @@ my_ether_aton(const char *a)
        return (struct ether_addr *)&ether_addr;
 }
 
        return (struct ether_addr *)&ether_addr;
 }
 
-int
+static int
 cmdline_parse_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk,
                        const char *buf, void *res, unsigned ressize)
 {
 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;
 }
 
        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;
 }
 {
        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,
+};
index 23e54e9..90c2e2b 100644 (file)
@@ -75,11 +75,6 @@ typedef struct cmdline_token_etheraddr cmdline_parse_token_etheraddr_t;
 
 extern struct cmdline_token_ops cmdline_token_etheraddr_ops;
 
 
 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 = {                                            \
 #define TOKEN_ETHERADDR_INITIALIZER(structure, field)      \
 {                                                          \
        .hdr = {                                            \
index cb41562..eab2319 100644 (file)
 #include "cmdline_parse.h"
 #include "cmdline_parse_ipaddr.h"
 
 #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
 
 #define INADDRSZ 4
 #define IN6ADDRSZ 16
 
@@ -289,7 +282,7 @@ inet_pton6(const char *src, unsigned char *dst)
        return (1);
 }
 
        return (1);
 }
 
-int
+static int
 cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
                     unsigned ressize)
 {
 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;
 
 {
        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;
 }
        }
        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,
+};
+
index 33670eb..d28b4dc 100644 (file)
@@ -90,11 +90,6 @@ typedef struct cmdline_token_ipaddr cmdline_parse_token_ipaddr_t;
 
 extern struct cmdline_token_ops cmdline_token_ipaddr_ops;
 
 
 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 = {                                            \
 #define TOKEN_IPADDR_INITIALIZER(structure, field)         \
 {                                                          \
        .hdr = {                                            \
index 8ef2964..31ca78d 100644 (file)
 //#define debug_printf(args...) printf(args)
 #define debug_printf(args...) do {} while(0)
 
 //#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,
 enum num_parse_state_t {
        START,
        DEC_NEG,
@@ -109,7 +101,7 @@ static const char * num_help[] = {
 #endif
 };
 
 #endif
 };
 
-static inline int
+static int
 add_to_res(unsigned int c, uint64_t *res, unsigned int base)
 {
        /* overflow */
 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;
 }
 
        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:
 {
        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 */
 }
 
 /* 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)
 {
 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 */
 
 
 /* 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;
 
 {
        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;
 }
        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,
+};
index 1b8c79d..8afac2f 100644 (file)
@@ -90,11 +90,6 @@ typedef struct cmdline_token_num cmdline_parse_token_num_t;
 
 extern struct cmdline_token_ops cmdline_token_num_ops;
 
 
 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 = {                                           \
 #define TOKEN_NUM_INITIALIZER(structure, field, numtype)   \
 {                                                         \
        .hdr = {                                           \
index 1b9fdb8..1b2c146 100644 (file)
 #include "cmdline_parse.h"
 #include "cmdline_parse_string.h"
 
 #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"
 #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;
 }
 
        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;
 {
        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;
 }
 
        return -1;
 }
 
-int
+static int
 cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
                     unsigned ressize)
 {
 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;
 }
 
        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;;
 {
        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;
 
        const char *str;
 
-       if (!sd->str)
-               return 0;
-
        str = sd->str;
        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;
 
        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;
                return -1;
+       *opaque = (void *)get_next_token(s);
 
        len = get_token_len(s);
        if (len > size - 1)
 
        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;
 }
 
        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;;
 {
        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;
 
 
        s = sd->str;
 
-       if (s) {
-               if (get_next_token(s)) {
+       if (s != NULL) {
+               if (get_next_token(s))
                        strncpy(dstbuf, MULTISTRING_HELP, size);
                        strncpy(dstbuf, MULTISTRING_HELP, size);
-               }
-               else {
+               else
                        strncpy(dstbuf, FIXEDSTRING_HELP, size);
                        strncpy(dstbuf, FIXEDSTRING_HELP, size);
-               }
        }
        else {
                strncpy(dstbuf, ANYSTRING_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;
 }
 
        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,
+};
index d435f81..fc6013f 100644 (file)
@@ -81,14 +81,6 @@ typedef struct cmdline_token_string cmdline_parse_token_string_t;
 
 extern struct cmdline_token_ops cmdline_token_string_ops;
 
 
 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 = {                                            \
 #define TOKEN_STRING_INITIALIZER(structure, field, string)  \
 {                                                          \
        .hdr = {                                            \
index 4352b94..7f34946 100644 (file)
 #include <string.h>
 #include <stdarg.h>
 #include <ctype.h>
 #include <string.h>
 #include <stdarg.h>
 #include <ctype.h>
+#include <unistd.h>
 
 #include "cmdline_cirbuf.h"
 #include "cmdline_rdline.h"
 #include "cmdline_parse.h"
 
 
 #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);
 #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,
 
 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));
 {
        memset(rdl, 0, sizeof(*rdl));
+       rdl->fd_in = fd_in;
+       rdl->fd_out = fd_out;
        rdl->validate = validate;
        rdl->complete = complete;
        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);
        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)
 {
 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);
 
        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)
        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 ; i<rdl->prompt_size ; i++)
-               rdl->write_char(rdl, rdl->prompt[i]);
+       rdline_printf(rdl, "%s", rdl->prompt);
        rdl->status = RDLINE_RUNNING;
 
 #ifndef NO_RDLINE_HISTORY
        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);
 
        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;
 }
 
        return rdl->left_buf;
 }
 
@@ -173,13 +169,13 @@ display_right_buffer(struct rdline *rdl, int force)
        if (!force && CIRBUF_IS_EMPTY(&rdl->right))
                return;
 
        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) {
        CIRBUF_FOREACH(&rdl->right, i, tmp) {
-               rdl->write_char(rdl, tmp);
+               rdline_printf(rdl, "%c", tmp);
        }
        if (!CIRBUF_IS_EMPTY(&rdl->right))
        }
        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
 }
 
 void
@@ -188,17 +184,16 @@ rdline_redisplay(struct rdline *rdl)
        unsigned int i;
        char tmp;
 
        unsigned int i;
        char tmp;
 
-       rdline_puts(rdl, vt100_home);
-       for (i=0 ; i<rdl->prompt_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) {
        CIRBUF_FOREACH(&rdl->left, i, tmp) {
-               rdl->write_char(rdl, tmp);
+               rdline_printf(rdl, "%c", tmp);
        }
        display_right_buffer(rdl, 1);
 }
 
        }
        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;
 {
        unsigned int i;
        int cmd;
@@ -207,11 +202,6 @@ rdline_char_in(struct rdline *rdl, char c)
        char *buf;
 #endif
 
        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;
        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);
                        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:
                        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);
                        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)) {
                        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)) {
                                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);
                        }
                                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)) {
                        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)) {
                                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);
                        }
                                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)) {
 
                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:
                                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);
                        }
                                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:
                                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);
                                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;
                                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;
                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);
                        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;
                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);
                        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);
                        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:
                        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]);
                              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);
                                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:
 #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;
 
                        rdline_newline(rdl, rdl->prompt);
                        break;
 
@@ -366,66 +360,64 @@ rdline_char_in(struct rdline *rdl, char c)
                        rdline_redisplay(rdl);
                        break;
 
                        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';
                        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);
                                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:
 
                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;
                        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);
 #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;
 
        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;
 }
 
        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 */
 
 
 /* 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)
 {
 int
 rdline_add_history(struct rdline * rdl, const char * buf)
 {
-       unsigned int len, i;
+       unsigned int len;
 
        len = strlen(buf);
 
        len = strlen(buf);
-       for (i=0; i<len ; i++) {
-               if (buf[i] == '\n') {
-                       len = i;
-                       break;
-               }
-       }
-
-       if ( len >= RDLINE_HISTORY_BUF_SIZE )
+       if (len >= RDLINE_HISTORY_BUF_SIZE)
                return -1;
 
                return -1;
 
-       while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
+       while (len >= CIRBUF_GET_FREELEN(&rdl->history)) {
                rdline_remove_old_history_item(rdl);
        }
 
                rdline_remove_old_history_item(rdl);
        }
 
@@ -610,52 +639,68 @@ rdline_clear_history(struct rdline * rdl)
 
 #else /* !NO_RDLINE_HISTORY */
 
 
 #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;
+}
index 87a1789..cee438a 100644 (file)
 #define _RDLINE_H_
 
 /**
 #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
  *
  * 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 <cmdline_cirbuf.h>
  */
 
 #include <cmdline_cirbuf.h>
@@ -94,6 +87,7 @@
 #define RDLINE_VT100_BUF_SIZE  8
 #define RDLINE_HISTORY_BUF_SIZE BUFSIZ
 #define RDLINE_HISTORY_MAX_LINE 64
 #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,
 
 enum rdline_status {
        RDLINE_INIT,
@@ -103,15 +97,26 @@ enum rdline_status {
 
 struct rdline;
 
 
 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,
 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;
 
 struct rdline {
        enum rdline_status status;
+       int fd_in;
+       int fd_out;
+
        /* rdline bufs */
        struct cirbuf left;
        struct cirbuf right;
        /* 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];
        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];
 
 #ifndef NO_RDLINE_KILL_BUF
        char kill_buf[RDLINE_BUF_SIZE];
@@ -134,9 +138,9 @@ struct rdline {
 #endif
 
        /* callbacks and func pointers */
 #endif
 
        /* callbacks and func pointers */
-       rdline_write_char_t *write_char;
        rdline_validate_t *validate;
        rdline_complete_t *complete;
        rdline_validate_t *validate;
        rdline_complete_t *complete;
+       rdline_help_t *help;
 
        /* vt100 parser */
        struct cmdline_vt100 vt100;
 
        /* 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,
  */
 void rdline_init(struct rdline *rdl,
-                rdline_write_char_t *write_char,
+                int fd_in, int fd_out,
                 rdline_validate_t *validate,
                 rdline_validate_t *validate,
-                rdline_complete_t *complete);
+                rdline_complete_t *complete,
+                rdline_help_t *help);
 
 
 /**
  * Init the current buffer, and display a prompt.
 
 
 /**
  * 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);
 
 /**
  */
 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);
 
 /**
  */
 void rdline_stop(struct rdline *rdl);
 
 /**
+ * Exit from running rdline loop
+ *
  * Same than rdline_stop() except that next calls to rdline_char_in()
  * 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()
  */
 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
  */
 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);
 
  */
 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
 /* 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
 
 /**
 #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);
 
  */
 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'.
 /**
  * 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);
 
  */
 const char *rdline_get_buffer(struct rdline *rdl);
 
-
 /**
  * Add the buffer to history.
 /**
  * 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
  */
 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
  */
 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);
 
  */
 char *rdline_get_history_item(struct rdline *rdl, unsigned int i);