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>
# 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
-- token file/path
+- token file/path: pour ca il faut changer la logique get_nb/complete
+ pour se rapprocher du opendir/readdir
- libevent example
- better example than calculator
- check all XXX in code
- set prompt example
- too much possible completion
- token string entre guillemets ?
+- cmdline_parse() n'a pas besoin de retourner tokenlen
+- les help() des tokens (string par ex) ne retournent pas "faux" en cas d'erreur
+- on pourrait virer la valeur de retour du start() en reorganisant
+ cmdline_parse() et cmdline_help()
+- genconf: completion des xpath //
+- supprimer tous les basic_char_loop()
+
+---------------
+
+buf->linebuf
+
+- rdline: API peut rester comme ca
+ doit pouvoir etre utiisé seule
+ il fuat un example
+
+- parse:
+ doit aussi pouvoir etre utilisé seule
+ il faut virer les references a cmdline/rdline
+
+/* opaque is given to the token callback func */
+int cmdline_parse(parse_ctx, const char *buf, void *opaque);
+
+
+cmdline_complete_init(cmplt_ctx)
+
+/**
+ * complete() must be called with an initialized context
+ * with *state==-1 (just display choices), then called until it
+ * returns CMDLINE_PARSE_COMPLETED_BUFFER or
+ * CMDLINE_PARSE_COMPLETE_FINISHED.
+ *
+ * It returns < 0 on error.
+ *
+ * Else it returns:
+ * - CMDLINE_PARSE_COMPLETED_BUFFER on completion (one possible
+ * choice). In this case, the chars are appended in dst buffer.
+ * - CMDLINE_PARSE_COMPLETE_AGAIN if there is several possible
+ * choices. In this case, you must call the function again with
+ * the same context
+ * - CMDLINE_PARSE_COMPLETE_FINISHED when the iteration is
+ * finished. The dst is not valid for this last call.
+ *
+ * The returned dst buf ends with \0.
+ */
+int cmdline_complete(parse_ctx, const char *buf, cmplt_ctx,
+ char *dst, unsigned int size);
+
+
+- cmdline est un assemblage de rdline et parse
+ permet de parser des fichiers, de faire un prompt sur stdin, network, unix
+
+tests unitaires
+
+
+--------------
+
+completions possibles
+
+toto
+toti
+titi
+coin
+
+"" -> all (help)
+complete = "toto", complete_len = 4
+complete = "tot", complete_len = 3
+complete = "t", complete_len = 1
+complete = "", complete_len = 0
+n_completions = 4
+
+
+"t" -> "toto" "toti" "titi" (help)
+toklen = 1
+complete = "toto", complete_len = 4
+complete = "tot", complete_len = 3
+complete = "t", complete_len = 1
+n_completions = 3
+
+"to" -> "tot" (complete, not finished)
+toklen = 2
+complete = "toto", complete_len = 4
+complete = "tot", complete_len = 3
+n_completions = 2
+
+"ti" -> "titi " (complete, finished)
+n_completions = 1
+
OBJS = $(SRC:%.c=%.o)
-PROG = server
+PROG = standalone
LDLIB = ../lib/libcmdline.a
all: $(PROG)
#include <stdio.h>
#include <stdint.h>
#include <string.h>
+#include <stdarg.h>
#include <netinet/in.h>
#include <cmdline_parse.h>
/****** 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,
+ },
};
#include <stdio.h>
#include <stdint.h>
#include <string.h>
+#include <stdarg.h>
#include <netinet/in.h>
#include <cmdline_parse.h>
/****** 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,
+ },
};
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/****** 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,
+ },
};
#include <stdint.h>
#include <errno.h>
#include <math.h>
+#include <stdarg.h>
#include <netinet/in.h>
#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)
{
continue;
break;
}
- if (!o) /* not found */
+ if (o == NULL) /* not found */
return -1;
/* store the address of object in structure */
return token_len;
}
-int complete_get_nb_obj_list(cmdline_parse_token_hdr_t *tk)
+static int
+complete_obj_list_start(cmdline_parse_token_hdr_t *tk, void **opaque)
{
struct token_obj_list *tk2 = (struct token_obj_list *)tk;
struct token_obj_list_data *tkd = &tk2->obj_list_data;
struct object *o;
- int ret = 0;
- SLIST_FOREACH(o, tkd->list, next) {
- ret ++;
- }
- return ret;
+ o = SLIST_FIRST(tkd->list);
+ *opaque = (void *)o;
+ if (o == NULL)
+ return -1; /* no completion */
+ return 0;
}
-int complete_get_elt_obj_list(cmdline_parse_token_hdr_t *tk, int idx,
- char *dstbuf, unsigned int size)
+static int
+complete_obj_list_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
+ char *dstbuf, unsigned int size)
{
- struct token_obj_list *tk2 = (struct token_obj_list *)tk;
- struct token_obj_list_data *tkd = &tk2->obj_list_data;
struct object *o;
- unsigned int i = 0, len;
+ int len;
- SLIST_FOREACH(o, tkd->list, next) {
- if (i++ == idx)
- break;
- }
- if (!o)
+ o = *opaque;
+ if (o == NULL)
return -1;
- len = strlen(o->name) + 1;
- if (len > size)
+ len = snprintf(dstbuf, size, "%s", o->name);
+ if (len < 0 || len >= size)
return -1;
strcpy(dstbuf, o->name);
+ o = SLIST_NEXT(o, next);
+ *opaque = o;
return 0;
}
-int get_help_obj_list(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
+static int
+cmdline_help_obj_list(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+ unsigned int size)
{
snprintf(dstbuf, size, "Obj-List");
return 0;
}
+
+struct cmdline_token_ops token_obj_list_ops = {
+ .parse = parse_obj_list,
+ .complete_start = complete_obj_list_start,
+ .complete_iterate = complete_obj_list_iterate,
+ .complete_end = NULL,
+ .help = cmdline_help_obj_list,
+};
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 = { \
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static int get_str(struct cmdline *cl, char *buf, int len)
{
struct rdline rl;
- int ret = 0, n;
- char c;
+ int ret = 0;
+ int c;
char *s;
buf[0] = '\0';
- rdline_init(&rl, cmdline_write_char, NULL, NULL);
+ rdline_init(&rl, cl->s_in, cl->s_out, NULL, NULL, NULL);
rl.opaque = cl;
+
+ /* XXX use rdline() */
rdline_newline(&rl, "edit> ");
- while (ret == 0 || ret == 2) {
- n = read(cl->s_in, &c, 1);
- if (n <= 0)
+ while (ret == RDLINE_RES_SUCCESS) {
+ read(cl->s_in, &c, 1);
+ if (c <= 0)
return -1;
ret = rdline_char_in(&rl, c);
- if (ret == 1) {
+ if (ret == RDLINE_RES_VALIDATED) {
snprintf(buf, len, "%s", rdline_get_buffer(&rl));
s = strchr(buf, '\n');
if (s)
/****** 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,
+ },
};
#include <inttypes.h>
#include <ctype.h>
#include <string.h>
+#include <stdlib.h>
#include <netinet/in.h>
#include <sys/queue.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 '*')*/
-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;
}
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)
{
return token_len;
}
-int complete_get_nb_conf_node(cmdline_parse_token_hdr_t *tk)
+struct confnode_complete_callback {
+ struct confnode *start;
+ struct confnode *cur;
+};
+
+
+static struct confnode *get_next_node(struct confnode *start,
+ struct confnode *cur, unsigned flags,
+ unsigned mask)
+{
+ struct confnode *prev;
+ struct confnode *parent = start;
+
+ while (1) {
+ prev = cur;
+
+ /* no more node to browse */
+ if (cur == start)
+ return NULL;
+
+ /* first iteration */
+ if (cur == NULL)
+ cur = TAILQ_FIRST(&parent->children);
+ else
+ cur = TAILQ_NEXT(cur, next);
+
+ /* no next, return to upper level */
+ if (cur == NULL) {
+ if (prev == NULL)
+ return NULL; /* no node */
+ cur = prev->parent;
+ continue;
+ }
+
+ /* skip invisible nodes, and browse their contents */
+ if (cur->flags & CONFNODE_F_INVISIBLE) {
+ parent = cur;
+ cur = NULL;
+ continue;
+ }
+
+ /* check that flags match what we want */
+ if ((cur->flags & mask) != flags)
+ continue;
+
+ /* standard node, return it */
+ return cur;
+ }
+}
+
+static int
+complete_conf_node_start(cmdline_parse_token_hdr_t *tk, void **opaque)
{
struct token_conf_node *tk2 = (struct token_conf_node *)tk;
struct token_conf_node_data *tkd = &tk2->conf_node_data;
+ struct confnode_complete_callback *cb;
+ struct confnode *n;
+ unsigned flags = tkd->flags;
+ unsigned mask = tkd->mask;
- return count_children(*tkd->cur, tkd->flags, tkd->mask);
+ cb = malloc(sizeof(*cb));
+ if (cb == NULL)
+ return -1;
+
+ n = get_next_node(*tkd->cur, NULL, flags, mask);
+ if (n == NULL)
+ return -1; /* no completion */
+
+ cb->start = *tkd->cur;
+ cb->cur = n;
+ *opaque = cb;
+ return 0;
}
-int complete_get_elt_conf_node(cmdline_parse_token_hdr_t *tk, int idx,
- char *dstbuf, unsigned int size)
+static int
+complete_conf_node_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
+ char *dstbuf, unsigned int size)
{
struct token_conf_node *tk2 = (struct token_conf_node *)tk;
struct token_conf_node_data *tkd = &tk2->conf_node_data;
+ struct confnode_complete_callback *cb;
struct confnode *n;
- unsigned int i = 0, len;
+ unsigned len;
+ unsigned flags = tkd->flags;
+ unsigned mask = tkd->mask;
- n = get_from_idx(*tkd->cur, &i, idx, tkd->flags, tkd->mask);
+ cb = *opaque;
+ n = cb->cur;
if (n == NULL)
return -1;
+ cb->cur = get_next_node(cb->start, n, flags, mask);
len = strlen(n->name) + 1;
if (len > size)
return 0;
}
+static void
+complete_conf_node_end(cmdline_parse_token_hdr_t *tk, void **opaque)
+{
+ free(*opaque);
+}
-int get_help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
+int help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
{
snprintf(dstbuf, size, "conf-node");
return 0;
}
+
+struct cmdline_token_ops token_conf_node_ops = {
+ .parse = parse_conf_node,
+ .complete_start = complete_conf_node_start,
+ .complete_iterate = complete_conf_node_iterate,
+ .complete_end = complete_conf_node_end,
+ .help = help_conf_node,
+};
+
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) \
#include "cmdline.h"
static void
-cmdline_valid_buffer(struct rdline *rdl, const char *buf,
- __attribute__((unused)) unsigned int size)
+cmdline_default_valid_buffer(struct rdline *rdl, const char *buf,
+ __attribute__((unused)) unsigned int size)
{
struct cmdline *cl = rdl->opaque;
int ret;
- ret = cmdline_parse(cl, buf);
+ ret = cmdline_parse(cl->ctx, buf, cl);
if (ret == CMDLINE_PARSE_AMBIGUOUS)
cmdline_printf(cl, "Ambiguous command\n");
else if (ret == CMDLINE_PARSE_NOMATCH)
- cmdline_printf(cl, "Command not found\n");
- else if (ret == CMDLINE_PARSE_BAD_ARGS)
cmdline_printf(cl, "Bad arguments\n");
else if (ret == CMDLINE_PARSE_UNTERMINATED_QUOTE)
cmdline_printf(cl, "Unterminated quote\n");
}
static int
-cmdline_complete_buffer(struct rdline *rdl, const char *buf,
- char *dstbuf, unsigned int dstsize,
- int *state)
+cmdline_default_complete_buffer(struct rdline *rdl, const char *buf,
+ char *dstbuf, unsigned int dstsize)
{
struct cmdline *cl = rdl->opaque;
- return cmdline_complete(cl, buf, state, dstbuf, dstsize);
+ // RDLINE_RES_COMPLETE ?
+ return cmdline_complete(cl->ctx, buf, dstbuf, dstsize);
}
-int
-cmdline_write_char(struct rdline *rdl, char c)
+
+static int
+cmdline_default_help(struct rdline *rdl, const char *buf,
+ rdline_write_t *write_cb, void *write_arg)
{
- int ret = -1;
struct cmdline *cl = rdl->opaque;
+ return cmdline_help(cl->ctx, buf, cmdline_write, write_arg);
+}
- if (cl->s_out >= 0)
- ret = write(cl->s_out, &c, 1);
+/* ---- Some rdline wrappers ---- */
- return ret;
+/* write a buffer, just use rdline */
+ssize_t
+cmdline_write(void *arg, void *buf, size_t count)
+{
+ const struct rdline *rdl = arg;
+ return rdline_write(rdl, buf, count);
}
-
void
cmdline_set_prompt(struct cmdline *cl, const char *prompt)
{
cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt, int s_in, int s_out)
{
struct cmdline *cl;
+
cl = malloc(sizeof(struct cmdline));
if (cl == NULL)
return NULL;
+
+ /* init cmdline structure */
memset(cl, 0, sizeof(struct cmdline));
cl->s_in = s_in;
cl->s_out = s_out;
cl->ctx = ctx;
- rdline_init(&cl->rdl, cmdline_write_char,
- cmdline_valid_buffer, cmdline_complete_buffer);
+ /* init embedded rdline */
+ rdline_init(&cl->rdl, s_in, s_out,
+ cmdline_default_valid_buffer,
+ cmdline_default_complete_buffer,
+ cmdline_default_help);
+
cl->rdl.opaque = cl;
cmdline_set_prompt(cl, prompt);
rdline_newline(&cl->rdl, cl->prompt);
free(cl);
}
-void
+int
cmdline_printf(const struct cmdline *cl, const char *fmt, ...)
{
va_list ap;
-
-#ifdef _GNU_SOURCE
- if (cl->s_out < 0)
- return;
- va_start(ap, fmt);
- vdprintf(cl->s_out, fmt, ap);
- va_end(ap);
-#else
int ret;
- char *buf;
- if (cl->s_out < 0)
- return;
-
- buf = malloc(BUFSIZ);
- if (buf == NULL)
- return;
va_start(ap, fmt);
- ret = vsnprintf(buf, BUFSIZ, fmt, ap);
+ ret = rdline_vprintf(&cl->rdl, fmt, ap);
va_end(ap);
- if (ret < 0)
- return;
- if (ret >= BUFSIZ)
- ret = BUFSIZ - 1;
- write(cl->s_out, buf, ret);
- free(buf);
-#endif
+
+ return ret;
}
+/* Push an input buffer in the command line. Typically, this function
+ * is called by cmdline_interact() to send the input characters to the
+ * cmdline process. It can also be called by a user callback function,
+ * when a buffer is received from the input socket.
+ *
+ * The function returns the number of processed characters, or a
+ * negative value on error (EOF reached or command line exited. */
int
cmdline_in(struct cmdline *cl, const char *buf, int size)
{
- const char *history, *buffer;
int ret = 0;
- int i, same;
+ int i;
- for (i=0; i<size; i++) {
+ for (i = 0; i < size; 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);
- }
else if (ret == RDLINE_RES_EOF)
return -1;
else if (ret == RDLINE_RES_EXITED)
return i;
}
+/* Interrupt a running command line process */
void
cmdline_quit(struct cmdline *cl)
{
rdline_quit(&cl->rdl);
}
+/* Start command line on configured file descriptor. This function
+ * loops until the user explicitelly call cmdline_quit(), or if the
+ * input fd reaches EOF. */
void
cmdline_interact(struct cmdline *cl)
{
#endif
};
-struct cmdline *cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt, int s_in, int s_out);
+/**
+ * Allocate and initialize a new command line structure, using the
+ * specified prompt and specified input/output file descriptors to
+ * communicate with the user.
+ */
+struct cmdline *cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt,
+ int s_in, int s_out);
+
+/**
+ * Set the prompt of the given command line.
+ */
void cmdline_set_prompt(struct cmdline *cl, const char *prompt);
+
+/**
+ * Free a previously allocated command line: data pointed by the cl
+ * pointer is freed. Note: the user should call cmdline_quit() before
+ * calling this function.
+ */
void cmdline_free(struct cmdline *cl);
-void cmdline_printf(const struct cmdline *cl, const char *fmt, ...);
+
+/**
+ * write a buffer, just use rdline
+ */
+ssize_t cmdline_write(void *arg, void *buf, size_t count);
+
+/**
+ * Display data on the output file descriptor of command line, using
+ * printf-like arguments.
+ */
+int cmdline_printf(const struct cmdline *cl, const char *fmt, ...);
+
+/**
+ * Push an input buffer in the command line. Typically, this function
+ * is called by cmdline_interact() to send the inpu characters to the
+ * cmdline process. It can also be called by a user callback function,
+ * when a buffer is received from the input socket.
+ *
+ * The function returns the number of processed characters, or a
+ * negative value on error (EOF reached or command line exited.
+ */
int cmdline_in(struct cmdline *cl, const char *buf, int size);
-int cmdline_write_char(struct rdline *rdl, char c);
+
+/**
+ * Start command line on configured file descriptor. This function
+ * loops until the user explicitelly call cmdline_quit(), or if the
+ * input fd reaches EOF.
+ */
void cmdline_interact(struct cmdline *cl);
+
+/**
+ * Interrupt a running command line process. Actually it will call
+ * rdline_quit() on the rdline descriptor.
+ */
void cmdline_quit(struct cmdline *cl);
#endif /* _CMDLINE_SOCKET_H_ */
#include <ctype.h>
#include <termios.h>
#include <errno.h>
+#include <stdarg.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)
+/* 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
return s;
}
-/* remove quote and stop when we reach the end of token */
-// XXX ret val comment
-int cmdline_unquote_token(char *dst, unsigned dstlen,
- const char *src)
+/* Remove quote and stop when we reach the end of token. Return the
+ * number of "eaten" bytes from the source buffer, or a negative value
+ * on error */
+int cmdline_get_token(char *dst, unsigned dstlen, const char *src)
{
unsigned s = 0, d = 0;
int quoted = 0;
+ /* skip spaces */
+ while (isblank2(src[s]))
+ s++;
+
+ /* empty token */
+ if (cmdline_isendoftoken(src[s]))
+ return -EINVAL;
+
+ /* copy token and remove quotes */
while (src[s] != '\0') {
if (d >= dstlen)
return -EMSGSIZE;
dst[d++] = src[s++];
}
+ /* not enough room in dst buffer */
+ if (d >= (dstlen-1))
+ return -EMSGSIZE;
+
+ /* end of string during quote */
if (quoted)
return -EINVAL;
- if (d >= (dstlen-1))
- return -EMSGSIZE;
dst[d++] = '\0';
return s;
}
-/**
+/* return the nth token from src and copy it in dst. Return the offset
+ * of the token in src, or a negative value on error. Note: the index
+ * of the first token is 0. */
+static int cmdline_get_nth_token(char *dst, unsigned dstlen, int n,
+ const char *src)
+{
+ int ret = 0, offset = 0;
+
+ do {
+ offset += ret;
+
+ /* skip spaces */
+ while (isblank2(src[offset]))
+ offset++;
+
+ /* get the token starting at offset */
+ ret = cmdline_get_token(dst, dstlen, src + offset);
+ if (ret < 0)
+ return ret;
+
+ } while (n--);
+
+ return offset;
+}
+
+/*
* try to match the buffer with an instruction (only the first
* nb_match_token tokens if != 0). Return 0 if we match all the
* tokens, else the number of matched tokens, else -1.
+
+retval à vérifier.
+
*/
static int
-match_inst(cmdline_parse_inst_t *inst, const char *buf,
+match_inst(cmdline_parse_inst_t *inst, const char *linebuf,
unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
{
- unsigned int token_num=0;
- cmdline_parse_token_hdr_t * token_p;
- unsigned int i=0;
+ unsigned int token_num = 0;
+ cmdline_parse_token_hdr_t *token;
int n = 0, res;
- struct cmdline_token_hdr token_hdr;
- char token_str[128];
+ char token_str[CMDLINE_MAX_TOKEN_SIZE];
- token_p = inst->tokens[token_num];
- if (token_p)
- memcpy(&token_hdr, token_p, sizeof(token_hdr));
+ token = inst->tokens[token_num];
/* check if we match all tokens of inst */
- while (token_p && (!nb_match_token || i<nb_match_token)) {
+ while (token) {
+
+ /* we matched enough tokens, return success */
+ if (nb_match_token != 0 && token_num >= nb_match_token)
+ return 0;
+
debug_printf("TK\n");
- /* skip spaces */
- while (isblank2(*buf)) {
- buf++;
- }
- /* end of buf */
- if ( isendofline(*buf) || iscomment(*buf) )
+ /* copy token and remove quotes */
+ n = cmdline_get_token(token_str, sizeof(token_str), linebuf);
+ if (n < 0)
break;
- n = cmdline_unquote_token(token_str, sizeof(token_str), buf);
- if (n == -EINVAL)
- return -EINVAL;
-
+ /* parse this token */
if (resbuf == NULL)
- res = token_hdr.ops->parse(token_p, token_str, NULL, 0);
+ res = token->ops->parse(token, token_str, NULL, 0);
else {
unsigned rb_sz;
- void *rb = (char *)resbuf + token_hdr.offset;
- if (token_hdr.offset > resbuf_size)
+ void *rb = (char *)resbuf + token->offset;
+
+ /* not enough room to store result */
+ if (token->offset > resbuf_size)
return -ENOBUFS;
- rb_sz = resbuf_size - token_hdr.offset;
- res = token_hdr.ops->parse(token_p, token_str,
- rb, rb_sz);
+
+ rb_sz = resbuf_size - token->offset;
+ res = token->ops->parse(token, token_str, rb, rb_sz);
}
+ /* does not match this token */
if (res < 0)
break;
debug_printf("TK parsed (len=%d)\n", n);
- i++;
- buf += n;
-
+ linebuf += n;
token_num ++;
- token_p = inst->tokens[token_num];
- if (token_p)
- memcpy(&token_hdr, token_p, sizeof(token_hdr));
+ token = inst->tokens[token_num];
}
/* does not match */
- if (i==0)
+ if (token_num == 0)
return -ENOENT;
- /* in case we want to match a specific num of token */
- if (nb_match_token) {
- if (i == nb_match_token) {
- return 0;
- }
- return i;
- }
-
/* we don't match all the tokens */
- if (token_p) {
- return i;
- }
+ if (token)
+ return token_num;
/* are there are some tokens more */
- while (isblank2(*buf)) {
- buf++;
- }
+ while (isblank2(*linebuf))
+ linebuf++;
- /* end of buf */
- if ( isendofline(*buf) || iscomment(*buf) )
+ /* end of buf, we match all inst */
+ if (isendofline(*linebuf) || iscomment(*linebuf))
return 0;
/* garbage after inst */
- return i;
+ return token_num;
}
-int
-cmdline_parse(struct cmdline *cl, const char * buf)
+/* Check if a line buffer is valid and can be parsed or completed. The
+ * parsing stops when \n or \0 is reached. The also function checks
+ * that tokens are correctly quoted. The number of tokens in the
+ * buffer is returned. */
+static int cmdline_validate_linebuf(const char *linebuf)
{
- unsigned int inst_num=0;
- cmdline_parse_inst_t *inst;
- const char *curbuf;
- char result_buf[CMDLINE_MAX_DSTBUF_SIZE];
- void (*f)(void *, struct cmdline *, void *) = NULL;
- void *data = NULL;
- int comment = 0;
- int linelen = 0;
- int parse_it = 0;
- int err = CMDLINE_PARSE_NOMATCH;
- int tok;
- cmdline_parse_ctx_t *ctx = cl->ctx;
-#ifdef CMDLINE_DEBUG
- char debug_buf[BUFSIZ];
-#endif
-
- /*
- * - look if the buffer contains at least one line
- * - look if line contains only spaces or comments
- * - count line length
- */
- curbuf = buf;
- while (! isendofline(*curbuf)) {
- if ( *curbuf == '\0' ) {
- debug_printf("Incomplete buf (len=%d)\n", linelen);
- return 0;
+ int quoted = 0, comment = 0;
+ int i = 0, nbtok = 0, token = 0;
+
+ while (linebuf[i] != '\0') {
+ if (isendofline(linebuf[i]) && quoted == 0)
+ break;
+ if (comment == 1) {
+ i++;
+ continue;
}
- if ( iscomment(*curbuf) ) {
+ if (iscomment(linebuf[i]) && quoted == 0) {
comment = 1;
+ i ++;
+ continue;
}
- if ( ! isblank2(*curbuf) && ! comment) {
- parse_it = 1;
+
+ /* end of token */
+ if (isblank2(linebuf[i]) && quoted == 0)
+ token = 0;
+ /* new token */
+ if (!isblank2(linebuf[i]) && token == 0) {
+ token = 1;
+ nbtok++;
}
- curbuf++;
- linelen++;
- }
- /* skip all endofline chars */
- while (isendofline(buf[linelen])) {
- linelen++;
+ if (linebuf[i] == '\\' && linebuf[i+1] == '"') {
+ i += 2;
+ continue;
+ }
+ if (linebuf[i] == '\\' && linebuf[i+1] == '\\') {
+ i += 2;
+ continue;
+ }
+ if (linebuf[i] == '"') {
+ i++;
+ quoted = !quoted;
+ continue;
+ }
+ i++;
}
+ if (quoted)
+ return CMDLINE_PARSE_UNTERMINATED_QUOTE;
+ return nbtok;
+}
- /* empty line */
- if ( parse_it == 0 ) {
- debug_printf("Empty line (len=%d)\n", linelen);
- return linelen;
- }
+/* Try to parse a buffer according to the specified context. The
+ * argument linebuf must end with \n or \0. */
+int
+cmdline_parse(cmdline_parse_ctx_t *ctx, const char *linebuf, void *opaque)
+{
+ cmdline_parse_inst_t **pinst;
+ cmdline_parse_inst_t *inst;
+ char result_buf[CMDLINE_MAX_DSTBUF_SIZE];
+ void (*f)(void *, struct cmdline *, void *) = NULL;
+ void *data = NULL;
+ int ret;
+
+ ret = cmdline_validate_linebuf(linebuf);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return CMDLINE_PARSE_EMPTY;
-#ifdef CMDLINE_DEBUG
- snprintf(debug_buf, (linelen>64 ? 64 : linelen), "%s", buf);
- debug_printf("Parse line : len=%d, <%s>\n", linelen, debug_buf);
-#endif
/* parse it !! */
- inst = ctx[inst_num];
- while (inst) {
- debug_printf("INST %d\n", inst_num);
+ for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) {
+ inst = *pinst;
+ debug_printf("INST\n");
/* fully parsed */
- tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf));
+ ret = match_inst(inst, linebuf, 0, result_buf, sizeof(result_buf));
- if (tok > 0) /* we matched at least one token */
- err = CMDLINE_PARSE_BAD_ARGS;
+ if (ret != 0)
+ continue;
- else if (!tok) {
- debug_printf("INST fully parsed\n");
- /* skip spaces */
- while (isblank2(*curbuf)) {
- curbuf++;
- }
+ debug_printf("INST fully parsed\n");
- /* if end of buf -> there is no garbage after inst */
- if (isendofline(*curbuf) || iscomment(*curbuf)) {
- if (!f) {
- memcpy(&f, &inst->f, sizeof(f));
- memcpy(&data, &inst->data, sizeof(data));
- }
- else {
- /* more than 1 inst matches */
- err = CMDLINE_PARSE_AMBIGUOUS;
- f=NULL;
- debug_printf("Ambiguous cmd\n");
- break;
- }
- }
+ /* if end of buf -> there is no garbage after inst */
+ if (f != NULL) {
+ /* more than 1 inst matches */
+ debug_printf("Ambiguous cmd\n");
+ return CMDLINE_PARSE_AMBIGUOUS;
}
- else if (tok == -EINVAL) {
- err = CMDLINE_PARSE_UNTERMINATED_QUOTE;
- f = NULL;
- debug_printf("Unterminated quote\n");
- break;
- }
-
- inst_num ++;
- inst = ctx[inst_num];
+ f = inst->f;
+ data = inst->data;
}
/* call func */
- if (f) {
- f(result_buf, cl, data);
- }
+ if (f == NULL)
+ return CMDLINE_PARSE_NOMATCH;
- /* no match */
+ f(result_buf, opaque, data);
+ return CMDLINE_PARSE_SUCCESS;
+}
+
+/* called by cmdline_help() and cmdline_complete() to preparse the
+ * line buffer (the operations done are common to these functions) */
+static int cmdline_preparse(struct cmdline_preparse *preparse, const char *buf)
+{
+ int ret, len, nb_tok;
+
+ /* count the number of tokens in the line buffer */
+ ret = cmdline_validate_linebuf(buf);
+ if (ret < 0)
+ return ret;
+ nb_tok = ret;
+
+ /* if last token is not complete, decrement nb_valid_tok */
+ len = strlen(buf);
+ if (nb_tok == 0 || isblank2(buf[len - 1])) {
+ preparse->nb_valid_tok = nb_tok;
+ preparse->comp_tok_offset = len;
+ preparse->comp_tok_buf[0] = '\0';
+ preparse->comp_tok_len = 0;
+ }
else {
- debug_printf("No match err=%d\n", err);
- return err;
+ preparse->nb_valid_tok = nb_tok - 1;
+
+ /* get the incomplete token (can be empty) and return its
+ * offset in the buffer */
+ preparse->comp_tok_offset =
+ cmdline_get_nth_token(preparse->comp_tok_buf,
+ sizeof(preparse->comp_tok_buf),
+ preparse->nb_valid_tok,
+ buf);
+ preparse->comp_tok_len = strlen(preparse->comp_tok_buf);
}
- return linelen;
+ return 0;
}
-int
-cmdline_complete(struct cmdline *cl, const char *buf, int *state,
- char *dst, unsigned int size)
+/* Display a contextual help using the write_buf() function pointer
+ * given as parameter (called with its opaque pointer). The contextual
+ * help depends on the buffer given. */
+int cmdline_help(cmdline_parse_ctx_t *ctx, const char *buf,
+ cmdline_write_t *write_buf, void *opaque)
{
- const char *partial_tok = buf;
- unsigned int inst_num = 0;
+ cmdline_parse_token_hdr_t *token;
+ cmdline_parse_inst_t **pinst;
cmdline_parse_inst_t *inst;
- cmdline_parse_token_hdr_t *token_p;
- struct cmdline_token_hdr token_hdr;
- char tmpbuf[64], comp_buf[64];
- unsigned int partial_tok_len;
- int comp_len = -1;
- int nb_token = 0;
- unsigned int i, n;
- int l;
- unsigned int nb_completable;
- unsigned int nb_non_completable;
- int local_state = 0;
+ struct cmdline_preparse preparse;
+ char helpbuf[CMDLINE_MAX_DSTBUF_SIZE];
+ char tmpbuf[CMDLINE_MAX_DSTBUF_SIZE];
char *help_str;
- cmdline_parse_ctx_t *ctx = cl->ctx;
+ int n, len, iterate;
- debug_printf("%s called\n", __FUNCTION__);
- memset(&token_hdr, 0, sizeof(token_hdr));
-
- /* count the number of complete token to parse */
- for (i=0 ; buf[i] ; i++) {
- if (!isblank2(buf[i]) && isblank2(buf[i+1]))
- nb_token++;
- if (isblank2(buf[i]) && !isblank2(buf[i+1]))
- partial_tok = buf+i+1;
- }
- partial_tok_len = strlen(partial_tok);
-
- /* first call -> do a first pass */
- if (*state <= 0) {
- debug_printf("try complete <%s>\n", buf);
- debug_printf("there is %d complete tokens, <%s> is incomplete\n",
- nb_token, partial_tok);
-
- nb_completable = 0;
- nb_non_completable = 0;
-
- inst = ctx[inst_num];
- while (inst) {
- /* parse the first tokens of the inst */
- if (nb_token && match_inst(inst, buf, nb_token, NULL, 0))
- goto next;
-
- debug_printf("instruction match \n");
- token_p = inst->tokens[nb_token];
- if (token_p)
- memcpy(&token_hdr, token_p, sizeof(token_hdr));
-
- /* non completable */
- if (!token_p ||
- !token_hdr.ops->complete_get_nb ||
- !token_hdr.ops->complete_get_elt ||
- (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
- nb_non_completable++;
- goto next;
- }
+ cmdline_preparse(&preparse, buf);
- debug_printf("%d choices for this token\n", n);
- for (i=0 ; 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 ? */
- 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;
}
#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
* A token is defined by this structure.
*
* parse() takes the token as first argument, then the source buffer
- * starting at the token we want to parse. The 3rd arg is a pointer
+ * containing the token we want to parse. The 3rd arg is a pointer
* where we store the parsed data (as binary), and the 4th arg is the
* size of this area. It returns 0 on success and a negative value on
* error.
*
- * complete_get_nb() returns the number of possible values for this
- * token if completion is possible. If it is NULL or if it returns 0,
- * no completion is possible.
+ * complete_start() prepares a completion operation. The first
+ * argument is the token to complete. The second argument is an opaque
+ * pointer that will be given to complete_iterate() function. It can
+ * be used to store private data for this completion. For each
+ * complete_start() call, the user must call complete_end() at the end
+ * of iterations (if defined). Return a negative value if completion
+ * is not possible, or 0 on success.
+ *
+ * complete_iterate() copy in dstbuf (the size is specified in the
+ * parameter) the next possible completion for this token. Return 0 on
+ * success or a negative value on error (or when there is no more
+ * completion). Refer to cmdline_complete_string_iterate() for an
+ * example.
*
- * complete_get_elt() copy in dstbuf (the size is specified in the
- * parameter) the i-th possible completion for this token. Return 0
- * on success or a negative value on error.
+ * complete_end() is called when the iteration on this token is finished,
+ * this function should free all things allocated during complete_start().
*
- * get_help() fills the dstbuf with the help for the token. It returns
+ * help() fills the dstbuf with the help for the token. It returns
* -1 on error and 0 on success.
*/
struct cmdline_token_ops {
/** parse(token ptr, buf, res pts) */
int (*parse)(cmdline_parse_token_hdr_t *, const char *, void *,
unsigned int);
- /** return the num of possible choices for this token */
- int (*complete_get_nb)(cmdline_parse_token_hdr_t *);
- /** return the elt x for this token (token, idx, dstbuf, size) */
- int (*complete_get_elt)(cmdline_parse_token_hdr_t *, int, char *,
+ /** prepare a completion on this token */
+ int (*complete_start)(cmdline_parse_token_hdr_t *, void **);
+ /** fill dstbuf for this token (token, opaque, dstbuf, size) */
+ int (*complete_iterate)(cmdline_parse_token_hdr_t *, void **, char *,
unsigned int);
+ /* end of completion, used to free the opaque structure */
+ void (*complete_end)(cmdline_parse_token_hdr_t *, void **);
/** get help for this token (token, dstbuf, size) */
- int (*get_help)(cmdline_parse_token_hdr_t *, char *, unsigned int);
+ int (*help)(cmdline_parse_token_hdr_t *, char *, unsigned int);
};
struct cmdline;
* instruction
*
*/
-typedef cmdline_parse_inst_t *cmdline_parse_ctx_t;
+struct cmdline_parse_ctx {
+ const char *name;
+ cmdline_parse_inst_t *insts[];
+};
+typedef struct cmdline_parse_ctx cmdline_parse_ctx_t;
+
+/* return status for parsing */
+#define CMDLINE_PARSE_SUCCESS 0
+#define CMDLINE_PARSE_EMPTY -1
+#define CMDLINE_PARSE_NOMATCH -2
+#define CMDLINE_PARSE_AMBIGUOUS -3
+#define CMDLINE_PARSE_UNTERMINATED_QUOTE -4
/**
* Try to parse a buffer according to the specified context. The
- * argument buf must ends with "\n\0". The function returns
- * CMDLINE_PARSE_AMBIGUOUS, CMDLINE_PARSE_NOMATCH or
- * CMDLINE_PARSE_BAD_ARGS on error. Else it calls the associated
- * function (defined in the context) and returns 0
- * (CMDLINE_PARSE_SUCCESS).
+ * argument linebuf must end with "\n\0".
+ *
+ * The function returns:
+ * - CMDLINE_PARSE_SUCCESS (0) on success
+ * - CMDLINE_PARSE_EMPTY if there is nothing to parse
+ * - CMDLINE_PARSE_NOMATCH if line does not match any command
+ * - CMDLINE_PARSE_AMBIGUOUS if several commands match
+ * - CMDLINE_PARSE_UNTERMINATED_QUOTE if a quote is used incorrectly
*/
-int cmdline_parse(struct cmdline *cl, const char *buf);
+int cmdline_parse(cmdline_parse_ctx_t *ctx, const char *linebuf, void *opaque);
+
+/* return status for completion */
+#define CMDLINE_COMPLETE_APPEND 0
+#define CMDLINE_COMPLETE_NONE -1
+#define CMDLINE_COMPLETE_MANY -2
/**
- * complete() must be called with *state==0 (try to complete) or
- * with *state==-1 (just display choices), then called without
- * modifying *state until it returns CMDLINE_PARSE_COMPLETED_BUFFER or
- * CMDLINE_PARSE_COMPLETED_BUFFER.
- *
- * It returns < 0 on error.
+ * cmdline_complete() tries to complete the buffer given as a parameter.
*
- * Else it returns:
- * - CMDLINE_PARSE_COMPLETED_BUFFER on completion (one possible
- * choice). In this case, the chars are appended in dst buffer.
- * - CMDLINE_PARSE_COMPLETE_AGAIN if there is several possible
- * choices. In this case, you must call the function again,
- * keeping the value of state intact.
- * - CMDLINE_PARSE_COMPLETED_BUFFER when the iteration is
- * finished. The dst is not valid for this last call.
- *
- * The returned dst buf ends with \0.
+ * It returns:
+ * - CMDLINE_COMPLETE_APPEND (0) on success, when a completion is
+ * done (one possible choice). In this case, the chars are
+ * appended in dst buffer.
+ * - CMDLINE_COMPLETE_NONE: error, no possible completion
+ * - CMDLINE_COMPLETE_MANY: error, many possble completions, need to call
+ * cmdline_help() function to see all the possibilities.
*/
-int cmdline_complete(struct cmdline *cl, const char *buf, int *state,
+int cmdline_complete(cmdline_parse_ctx_t *ctx, const char *buf,
char *dst, unsigned int size);
+/**
+ * callback given to rdline_help() to display the content of the
+ * help. The first argument is an opaque pointer. The other args
+ * are buffer and size.
+ */
+typedef ssize_t (cmdline_write_t)(void *, void *, size_t);
+
+/**
+ * Display a contextual help using the write_buf() function pointer
+ * given as parameter (called with its opaque pointer). The contextual
+ * help depends on the buffer given.
+ */
+int cmdline_help(cmdline_parse_ctx_t *ctx, const char *buf,
+ cmdline_write_t *write_buf, void *opaque);
/* return true if(!c || iscomment(c) || isblank(c) ||
* isendofline(c)) */
#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__
return (struct ether_addr *)ðer_addr;
}
-int
+static int
cmdline_parse_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk,
const char *buf, void *res, unsigned ressize)
{
return token_len;
}
-int cmdline_get_help_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk,
- char *dstbuf, unsigned int size)
+static int
+cmdline_help_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk,
+ char *dstbuf, unsigned int size)
{
snprintf(dstbuf, size, "Ethernet address");
return 0;
}
+
+struct cmdline_token_ops cmdline_token_etheraddr_ops = {
+ .parse = cmdline_parse_etheraddr,
+ .complete_start = NULL,
+ .complete_iterate = NULL,
+ .complete_end = NULL,
+ .help = cmdline_help_etheraddr,
+};
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 = { \
#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
return (1);
}
-int
+static int
cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
unsigned ressize)
{
}
-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;
}
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,
+};
+
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 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,
#endif
};
-static inline int
+static int
add_to_res(unsigned int c, uint64_t *res, unsigned int base)
{
/* overflow */
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:
}
/* 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)
{
/* 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;
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,
+};
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 = { \
#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"
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;
return -1;
}
-int
+static int
cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
unsigned ressize)
{
return token_len;
}
-int cmdline_complete_get_nb_string(cmdline_parse_token_hdr_t *tk)
+static int
+cmdline_complete_string_start(cmdline_parse_token_hdr_t *tk, void **opaque)
{
struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk;
struct cmdline_token_string_data *sd = &tk2->string_data;;
- int ret=1;
const char *str;
- if (!sd->str)
- return 0;
-
str = sd->str;
- while( (str = get_next_token(str)) != NULL ) {
- ret++;
- }
- return ret;
+ *opaque = (void *)str;
+ if (str == NULL)
+ return -1; /* no completion */
+ return 0;
}
-int cmdline_complete_get_elt_string(cmdline_parse_token_hdr_t *tk, int idx,
- char *dstbuf, unsigned int size)
+static int
+cmdline_complete_string_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
+ char *dstbuf, unsigned int size)
{
- struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk;
- struct cmdline_token_string_data *sd = &tk2->string_data;;
const char *s;
unsigned int len;
- s = sd->str;
-
- while (idx-- && s)
- s = get_next_token(s);
-
- if (!s)
+ s = *opaque;
+ if (s == NULL)
return -1;
+ *opaque = (void *)get_next_token(s);
len = get_token_len(s);
if (len > size - 1)
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;;
s = sd->str;
- if (s) {
- if (get_next_token(s)) {
+ if (s != NULL) {
+ if (get_next_token(s))
strncpy(dstbuf, MULTISTRING_HELP, size);
- }
- else {
+ else
strncpy(dstbuf, FIXEDSTRING_HELP, size);
- }
}
else {
strncpy(dstbuf, ANYSTRING_HELP, size);
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,
+};
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 = { \
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
+#include <unistd.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);
void
rdline_init(struct rdline *rdl,
- rdline_write_char_t *write_char,
- rdline_validate_t *validate,
- rdline_complete_t *complete)
+ int fd_in, int fd_out,
+ rdline_validate_t *validate,
+ rdline_complete_t *complete,
+ rdline_help_t *help)
{
memset(rdl, 0, sizeof(*rdl));
+ rdl->fd_in = fd_in;
+ rdl->fd_out = fd_out;
rdl->validate = validate;
rdl->complete = complete;
- rdl->write_char = write_char;
+ rdl->help = help;
rdl->status = RDLINE_INIT;
#ifndef NO_RDLINE_HISTORY
cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
void
rdline_newline(struct rdline *rdl, const char *prompt)
{
- unsigned int i;
-
vt100_init(&rdl->vt100);
cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
+ /* if pointer is the same, don't copy it */
if (prompt != rdl->prompt)
- memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
- rdl->prompt_size = strlen(prompt);
+ snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt);
- for (i=0 ; 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
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;
}
if (!force && CIRBUF_IS_EMPTY(&rdl->right))
return;
- rdline_puts(rdl, vt100_clear_right);
+ rdline_printf(rdl, vt100_clear_right);
CIRBUF_FOREACH(&rdl->right, i, tmp) {
- rdl->write_char(rdl, tmp);
+ rdline_printf(rdl, "%c", tmp);
}
if (!CIRBUF_IS_EMPTY(&rdl->right))
- rdline_miniprintf(rdl, vt100_multi_left,
- CIRBUF_GET_LEN(&rdl->right));
+ rdline_printf(rdl, vt100_multi_left,
+ CIRBUF_GET_LEN(&rdl->right));
}
void
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) {
- rdl->write_char(rdl, tmp);
+ rdline_printf(rdl, "%c", tmp);
}
display_right_buffer(rdl, 1);
}
-int
-rdline_char_in(struct rdline *rdl, char c)
+static int
+rdline_parse_char(struct rdline *rdl, char c)
{
unsigned int i;
int cmd;
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;
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:
tmp = cirbuf_get_head(&rdl->right);
cirbuf_del_head(&rdl->right);
cirbuf_add_tail(&rdl->left, tmp);
- rdline_puts(rdl, vt100_right_arr);
+ rdline_printf(rdl, vt100_right_arr);
break;
case CMDLINE_KEY_WLEFT:
while (! CIRBUF_IS_EMPTY(&rdl->left) &&
(tmp = cirbuf_get_tail(&rdl->left)) &&
isblank2(tmp)) {
- rdline_puts(rdl, vt100_left_arr);
+ rdline_printf(rdl, vt100_left_arr);
cirbuf_del_tail(&rdl->left);
cirbuf_add_head(&rdl->right, tmp);
}
while (! CIRBUF_IS_EMPTY(&rdl->left) &&
(tmp = cirbuf_get_tail(&rdl->left)) &&
!isblank2(tmp)) {
- rdline_puts(rdl, vt100_left_arr);
+ rdline_printf(rdl, vt100_left_arr);
cirbuf_del_tail(&rdl->left);
cirbuf_add_head(&rdl->right, tmp);
}
while (! CIRBUF_IS_EMPTY(&rdl->right) &&
(tmp = cirbuf_get_head(&rdl->right)) &&
isblank2(tmp)) {
- rdline_puts(rdl, vt100_right_arr);
+ rdline_printf(rdl, vt100_right_arr);
cirbuf_del_head(&rdl->right);
cirbuf_add_tail(&rdl->left, tmp);
}
while (! CIRBUF_IS_EMPTY(&rdl->right) &&
(tmp = cirbuf_get_head(&rdl->right)) &&
!isblank2(tmp)) {
- rdline_puts(rdl, vt100_right_arr);
+ rdline_printf(rdl, vt100_right_arr);
cirbuf_del_head(&rdl->right);
cirbuf_add_tail(&rdl->left, tmp);
}
case CMDLINE_KEY_BKSPACE:
if(!cirbuf_del_tail_safe(&rdl->left)) {
- rdline_puts(rdl, vt100_bs);
+ rdline_printf(rdl, vt100_bs);
display_right_buffer(rdl, 1);
}
break;
case CMDLINE_KEY_META_BKSPACE:
case CMDLINE_KEY_CTRL_W:
- while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
- rdline_puts(rdl, vt100_bs);
+ while (! CIRBUF_IS_EMPTY(&rdl->left) &&
+ isblank2(cirbuf_get_tail(&rdl->left))) {
+ rdline_printf(rdl, vt100_bs);
cirbuf_del_tail(&rdl->left);
}
- while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
- rdline_puts(rdl, vt100_bs);
+ while (! CIRBUF_IS_EMPTY(&rdl->left) &&
+ !isblank2(cirbuf_get_tail(&rdl->left))) {
+ rdline_printf(rdl, vt100_bs);
cirbuf_del_tail(&rdl->left);
}
display_right_buffer(rdl, 1);
break;
case CMDLINE_KEY_META_D:
- while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
+ while (! CIRBUF_IS_EMPTY(&rdl->right) &&
+ isblank2(cirbuf_get_head(&rdl->right)))
cirbuf_del_head(&rdl->right);
- while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
+ while (! CIRBUF_IS_EMPTY(&rdl->right) &&
+ !isblank2(cirbuf_get_head(&rdl->right)))
cirbuf_del_head(&rdl->right);
display_right_buffer(rdl, 1);
break;
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);
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);
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:
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);
#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_redisplay(rdl);
break;
- case CMDLINE_KEY_TAB:
- case CMDLINE_KEY_HELP:
+ case CMDLINE_KEY_HELP: {
+ if (rdl->help == NULL)
+ break;
+
+ cirbuf_align_left(&rdl->left);
+ rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
+ rdline_printf(rdl, "\r\n");
+ rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
+ rdline_redisplay(rdl);
+ break;
+ }
+
+ case CMDLINE_KEY_TAB: {
+ char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
+ int ret; //, curline = 0;
+ unsigned int tmp_size;
+
+ if (rdl->complete == NULL)
+ break;
+
cirbuf_align_left(&rdl->left);
rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
- if (rdl->complete) {
- char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
- int complete_state;
- int ret;
- unsigned int tmp_size;
-
- if (cmd == CMDLINE_KEY_TAB)
- complete_state = 0;
- else
- complete_state = -1;
-
- /* see in parse.h for help on complete() */
- ret = rdl->complete(rdl, rdl->left_buf,
- tmp_buf, sizeof(tmp_buf),
- &complete_state);
- /* no completion or error */
- if (ret <= 0) {
- return RDLINE_RES_COMPLETE;
- }
-
- tmp_size = strlen(tmp_buf);
- /* add chars */
- if (ret == RDLINE_RES_COMPLETE) {
- i=0;
- while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
- RDLINE_BUF_SIZE &&
- i < tmp_size) {
- cirbuf_add_tail(&rdl->left, tmp_buf[i]);
- rdl->write_char(rdl, tmp_buf[i]);
- i++;
- }
- display_right_buffer(rdl, 1);
- return RDLINE_RES_COMPLETE; /* ?? */
- }
-
- /* choice */
- rdline_puts(rdl, "\r\n");
- while (ret) {
- rdl->write_char(rdl, ' ');
- for (i=0 ; tmp_buf[i] ; i++)
- rdl->write_char(rdl, tmp_buf[i]);
- rdline_puts(rdl, "\r\n");
- ret = rdl->complete(rdl, rdl->left_buf,
- tmp_buf, sizeof(tmp_buf),
- &complete_state);
- }
+ /* see in parse.h for help on complete() */
+ ret = rdl->complete(rdl, rdl->left_buf,
+ tmp_buf, sizeof(tmp_buf));
+ /* no completion or error */
+ if (ret == CMDLINE_COMPLETE_NONE ||
+ ret == CMDLINE_COMPLETE_MANY) {
+ cirbuf_align_left(&rdl->left);
+ rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
+ rdline_printf(rdl, "\r\n");
+ rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
rdline_redisplay(rdl);
+ break;
}
- return RDLINE_RES_COMPLETE;
+
+ tmp_size = strlen(tmp_buf);
+ /* add chars */
+ i = 0;
+ while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
+ RDLINE_BUF_SIZE &&
+ i < tmp_size) {
+ cirbuf_add_tail(&rdl->left, tmp_buf[i]);
+ rdline_printf(rdl, "%c", tmp_buf[i]);
+ i++;
+ }
+ display_right_buffer(rdl, 1);
+ break;
+ }
case CMDLINE_KEY_RETURN:
case CMDLINE_KEY_RETURN2:
- rdline_get_buffer(rdl);
+ cirbuf_align_left(&rdl->left);
+ rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\n';
+ rdl->left_buf[CIRBUF_GET_LEN(&rdl->left) + 1] = '\0';
rdl->status = RDLINE_INIT;
- rdline_puts(rdl, "\r\n");
+ rdline_printf(rdl, "\r\n");
#ifndef NO_RDLINE_HISTORY
if (rdl->history_cur_line != -1)
rdline_remove_first_history_item(rdl);
if (cirbuf_add_tail_safe(&rdl->left, c))
return RDLINE_RES_SUCCESS;
- rdl->write_char(rdl, c);
+ rdline_printf(rdl, "%c", c);
display_right_buffer(rdl, 0);
return RDLINE_RES_SUCCESS;
}
+int
+rdline_char_in(struct rdline *rdl, char c)
+{
+ int ret, same = 0;
+ const char *history, *buffer;
+
+ if (rdl->status == RDLINE_EXITED)
+ return RDLINE_RES_EXITED;
+ if (rdl->status != RDLINE_RUNNING)
+ return RDLINE_RES_NOT_RUNNING;
+
+ ret = rdline_parse_char(rdl, c);
+
+ /* add line to history */
+ if (ret == RDLINE_RES_VALIDATED) {
+ buffer = rdline_get_buffer(rdl);
+ history = rdline_get_history_item(rdl, 0);
+ if (history)
+ same = !strcmp(buffer, history);
+
+ if (strlen(buffer) >= 1 && same == 0)
+ rdline_add_history(rdl, buffer);
+ }
+
+ return ret;
+}
+
+int
+rdline(struct rdline *rdl, const char *prompt)
+{
+ char c;
+ int ret = RDLINE_RES_NOT_RUNNING;
+
+ rdline_newline(rdl, prompt);
+ while (1) {
+ if (read(rdl->fd_in, &c, 1) < 0)
+ break;
+ ret = rdline_char_in(rdl, c);
+ if (ret != RDLINE_RES_SUCCESS)
+ break;
+ }
+
+ return ret;
+}
/* HISTORY */
int
rdline_add_history(struct rdline * rdl, const char * buf)
{
- unsigned int len, i;
+ unsigned int len;
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;
- while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
+ while (len >= CIRBUF_GET_FREELEN(&rdl->history)) {
rdline_remove_old_history_item(rdl);
}
#else /* !NO_RDLINE_HISTORY */
-int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
-void rdline_clear_history(struct rdline * rdl) {}
-char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
+int rdline_add_history(struct rdline * rdl, const char * buf)
+{
+ return -1;
+}
+void rdline_clear_history(struct rdline * rdl)
+{
+ return;
+}
-#endif /* !NO_RDLINE_HISTORY */
+char * rdline_get_history_item(struct rdline * rdl, unsigned int i)
+{
+ return NULL;
+}
-/* STATIC USEFUL FUNCS */
+#endif /* !NO_RDLINE_HISTORY */
-static void
-rdline_puts(struct rdline * rdl, const char * buf)
+
+ssize_t
+rdline_write(const struct rdline *rdl, void *buf, size_t count)
{
- char c;
- while ( (c = *(buf++)) != '\0' ) {
- rdl->write_char(rdl, c);
- }
+ return write(rdl->fd_out, buf, count);
}
-/* a very very basic printf with one arg and one format 'u' */
-static void
-rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
+int
+rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap)
{
- char c, started=0, div=100;
+ int ret;
+#ifndef _GNU_SOURCE
+ char *buf;
+#endif
- while ( (c=*(buf++)) ) {
- if (c != '%') {
- rdl->write_char(rdl, c);
- continue;
- }
- c = *(buf++);
- if (c != 'u') {
- rdl->write_char(rdl, '%');
- rdl->write_char(rdl, c);
- continue;
- }
- /* val is never more than 255 */
- while (div) {
- c = (char)(val / div);
- if (c || started) {
- rdl->write_char(rdl, (char)(c+'0'));
- started = 1;
- }
- val %= div;
- div /= 10;
- }
- }
+ if (rdl->fd_out < 0)
+ return -1;
+
+#ifdef _GNU_SOURCE
+ ret = vdprintf(rdl->fd_out, fmt, ap);
+#else
+ buf = malloc(BUFSIZ);
+ if (buf == NULL)
+ return -1;
+
+ ret = vsnprintf(buf, BUFSIZ, fmt, ap);
+
+ if (ret > 0)
+ write(rdl->fd_out, buf, (ret >= BUFSIZ) ? BUFSIZ : ret);
+ free(buf);
+#endif
+
+ return ret;
}
+int
+rdline_printf(const struct rdline *rdl, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = rdline_vprintf(rdl, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
#define _RDLINE_H_
/**
- * This file is a small equivalent to the GNU readline library, but it
- * was originally designed for small systems, like Atmel AVR
- * microcontrollers (8 bits). Indeed, we don't use any malloc that is
- * sometimes not implemented (or just not recommended) on such
- * systems.
+ * This file is a small equivalent to the GNU readline library, it was
+ * originally designed for very small systems (an 8-bits Atmel AVR
+ * microcontroller), but the library can now run on many emmbedded
+ * systems with or without OS.
*
* Obviously, it does not support as many things as the GNU readline,
* but at least it supports some interresting features like a kill
* buffer and a command history.
*
* It also have a feature that does not have the GNU readline (as far
- * as I know): we can have several instances of it running at the same
- * time, even on a monothread program, since it works with callbacks.
- *
- * The lib is designed for a client-side or a server-side use:
- * - server-side: the server receives all data from a socket, including
- * control chars, like arrows, tabulations, ... The client is
- * very simple, it can be a telnet or a minicom through a serial line.
- * - client-side: the client receives its data through its stdin for
- * instance.
+ * as I know): it is possible to have several instances of readline
+ * running at the same time, even on a monothread program, since it
+ * works with callbacks.
*/
#include <cmdline_cirbuf.h>
#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,
struct rdline;
-typedef int (rdline_write_char_t)(struct rdline *rdl, char);
+/**
+ * callback given to rdline_help() to display the content of the
+ * help. The first argument is an opaque pointer. The other args
+ * are buffer and size.
+ */
+typedef ssize_t (rdline_write_t)(const struct rdline *, void *, size_t);
+
+
typedef void (rdline_validate_t)(struct rdline *rdl,
const char *buf, unsigned int size);
typedef int (rdline_complete_t)(struct rdline *rdl, const char *buf,
- char *dstbuf, unsigned int dstsize,
- int *state);
+ char *dstbuf, unsigned int dstsize);
+typedef int (rdline_help_t)(struct rdline *rdl, const char *buf,
+ rdline_write_t *write, void *opaque);
struct rdline {
enum rdline_status status;
+ int fd_in;
+ int fd_out;
+
/* rdline bufs */
struct cirbuf left;
struct cirbuf right;
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];
#endif
/* callbacks and func pointers */
- rdline_write_char_t *write_char;
rdline_validate_t *validate;
rdline_complete_t *complete;
+ rdline_help_t *help;
/* vt100 parser */
struct cmdline_vt100 vt100;
};
/**
- * Init fields for a struct rdline. Call this only once at the beginning
- * of your program.
- * \param rdl A pointer to an uninitialized struct rdline
- * \param write_char The function used by the function to write a character
- * \param validate A pointer to the function to execute when the
- * user validates the buffer.
- * \param complete A pointer to the function to execute when the
- * user completes the buffer.
+ * Init fields for a struct rdline.
+ *
+ * @param rdl A pointer to an uninitialized struct rdline
+ * @param fd_in
+ * Input file descriptor
+ * @param fd_out
+ * Output file descriptor
+ * @param validate
+ * A pointer to the function to execute when the user validates the
+ * buffer.
+ * @param complete
+ * A pointer to the function to execute when the user completes the
+ * buffer.
+ * @param help
+ * A pointer to the function to execute when the user ask for
+ * contextual help.
*/
void rdline_init(struct rdline *rdl,
- rdline_write_char_t *write_char,
+ int fd_in, int fd_out,
rdline_validate_t *validate,
- rdline_complete_t *complete);
+ rdline_complete_t *complete,
+ rdline_help_t *help);
/**
* Init the current buffer, and display a prompt.
- * \param rdl A pointer to a struct rdline
- * \param prompt A string containing the prompt
+ *
+ * Also set the rdline status to "running", overriding previous
+ * rdline_stop() or rdline_quit().
+ *
+ * @param rdl
+ * A pointer to an initialized struct rdline
+ * @param prompt
+ * A string containing the prompt
*/
void rdline_newline(struct rdline *rdl, const char *prompt);
/**
- * Call it and all received chars will be ignored.
- * \param rdl A pointer to a struct rdline
+ * Ignore all subsequent received chars.
+ *
+ * @param rdl
+ * A pointer to a struct rdline
*/
void rdline_stop(struct rdline *rdl);
/**
+ * Exit from running rdline loop
+ *
* Same than rdline_stop() except that next calls to rdline_char_in()
- * will return RDLINE_RES_EXITED.
- * \param rdl A pointer to a struct rdline
+ * will return RDLINE_RES_EXITED. Hence, any running rdline() function is
+ * interrupted.
+ *
+ * @param rdl
+ * A pointer to a struct rdline
*/
void rdline_quit(struct rdline *rdl);
/**
* Restart after a call to rdline_stop() or rdline_quit()
- * \param rdl A pointer to a struct rdline
+ *
+ * @param rdl
+ * A pointer to a struct rdline
*/
void rdline_restart(struct rdline *rdl);
/**
* Redisplay the current buffer
- * \param rdl A pointer to a struct rdline
+ *
+ * @param rdl
+ * A pointer to a struct rdline
*/
void rdline_redisplay(struct rdline *rdl);
/* return status for rdline_char_in() */
#define RDLINE_RES_SUCCESS 0
#define RDLINE_RES_VALIDATED 1
-#define RDLINE_RES_COMPLETE 2
#define RDLINE_RES_NOT_RUNNING -1
#define RDLINE_RES_EOF -2
#define RDLINE_RES_EXITED -3
/**
- * append a char to the readline buffer.
- * Return RDLINE_RES_VALIDATE when the line has been validated.
- * Return RDLINE_RES_COMPLETE when the user asked to complete the buffer.
- * Return RDLINE_RES_NOT_RUNNING if it is not running.
- * Return RDLINE_RES_EOF if EOF (ctrl-d on an empty line).
- * Else return RDLINE_RES_SUCCESS.
- * XXX error case when the buffer is full ?
+ * Append a char to the readline buffer.
*
- * \param rdl A pointer to a struct rdline
- * \param c The character to append
+ * @param rdl
+ * A pointer to a struct rdline
+ * @param c
+ * The character to append
+ * @return
+ * - RDLINE_RES_VALIDATED when the line has been validated.
+ * - RDLINE_RES_NOT_RUNNING if it is not running.
+ * - RDLINE_RES_EOF if EOF (ctrl-d on an empty line).
+ * - RDLINE_RES_EXITED if user called rdline_quit()
+ * - Else return RDLINE_RES_SUCCESS.
*/
int rdline_char_in(struct rdline *rdl, char c);
+/**
+ * Read (and edit) a line
+ *
+ * @param rdl
+ * A pointer to a struct rdline
+ * @param prompt
+ * The prompt string
+ * @return
+ * - RDLINE_RES_VALIDATED when the line has been validated.
+ * - RDLINE_RES_NOT_RUNNING if it is not running.
+ * - RDLINE_RES_EOF if EOF (ctrl-d on an empty line).
+ * - RDLINE_RES_EXITED if user called rdline_quit()
+ */
+int rdline(struct rdline *rdl, const char *prompt);
+
+/**
+ * write a buffer on rdline file descriptor
+ *
+ * @param rdl
+ * The rdline descriptor
+ * @param buf
+ * Pointer to the buffer
+ * @param count
+ * Number of bytes to write
+ * @return
+ * On success, the number of bytes written is returned (zero
+ * indicates nothing was written). On error, -1 is returned, and
+ * errno is set appropriately
+ */
+ssize_t rdline_write(const struct rdline *rdl, void *buf, size_t count);
+
+/**
+ * write on rdline file descriptor according to a format string
+ *
+ * @param rdl
+ * The rdline descriptor
+ * @param fmt
+ * The format strings
+ * @return
+ * Upon successful return, these functions return the number of
+ * characters printed (not including the trailing '\0' used to end
+ * output to strings). On error, a negative value is returned.
+ */
+int rdline_printf(const struct rdline *rdl, const char *fmt, ...);
+
+/**
+ * write on rdline file descriptor according to a format string
+ *
+ * @param rdl
+ * The rdline descriptor
+ * @param fmt
+ * The format strings
+ * @param ap
+ * Variable argument list
+ * @return
+ * Upon successful return, these functions return the number of
+ * characters printed (not including the trailing '\0' used to end
+ * output to strings). On error, a negative value is returned.
+ */
+int rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap);
+
/**
* Return the current buffer, terminated by '\0'.
- * \param rdl A pointer to a struct rdline
+ *
+ * @param rdl
+ * A pointer to a struct rdline
+ * @return
+ * The rdline buffer
*/
const char *rdline_get_buffer(struct rdline *rdl);
-
/**
* Add the buffer to history.
- * return < 0 on error.
- * \param rdl A pointer to a struct rdline
- * \param buf A buffer that is terminated by '\0'
+ *
+ * @param rdl
+ * A pointer to a struct rdline
+ * @param buf
+ * A buffer that is terminated by '\0'
+ * @return
+ * - 0 on success
+ * - negative on error
*/
int rdline_add_history(struct rdline *rdl, const char *buf);
/**
* Clear current history
- * \param rdl A pointer to a struct rdline
+ *
+ * @param rdl
+ * A pointer to a struct rdline
*/
void rdline_clear_history(struct rdline *rdl);
/**
* Get the i-th history item
+ *
+ * @param rdl
+ * A pointer to a struct rdline
+ * @param i
+ * The index of the history item
+ * @return
+ * The i-th string of history, or NULL on error.
*/
char *rdline_get_history_item(struct rdline *rdl, unsigned int i);