xref: /dpdk/lib/cmdline/cmdline_parse.c (revision 0f1dc8cb671203d52488fd66936f2fe6dcca03cc)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation.
399a2dd95SBruce Richardson  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
499a2dd95SBruce Richardson  * All rights reserved.
599a2dd95SBruce Richardson  */
699a2dd95SBruce Richardson 
799a2dd95SBruce Richardson #include <stdio.h>
899a2dd95SBruce Richardson #include <errno.h>
999a2dd95SBruce Richardson #include <string.h>
10758d9279SBruce Richardson #include <stdbool.h>
1199a2dd95SBruce Richardson 
1299a2dd95SBruce Richardson #include <rte_string_fns.h>
1399a2dd95SBruce Richardson 
1499a2dd95SBruce Richardson #include "cmdline_private.h"
1599a2dd95SBruce Richardson 
1699a2dd95SBruce Richardson #ifdef RTE_LIBRTE_CMDLINE_DEBUG
1799a2dd95SBruce Richardson #define debug_printf printf
1899a2dd95SBruce Richardson #else
19*0f1dc8cbSTyler Retzlaff #define debug_printf(...) do {} while (0)
2099a2dd95SBruce Richardson #endif
2199a2dd95SBruce Richardson 
2299a2dd95SBruce Richardson #define CMDLINE_BUFFER_SIZE 64
2399a2dd95SBruce Richardson 
2499a2dd95SBruce Richardson /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
2599a2dd95SBruce Richardson  * own. */
2699a2dd95SBruce Richardson static int
isblank2(char c)2799a2dd95SBruce Richardson isblank2(char c)
2899a2dd95SBruce Richardson {
2999a2dd95SBruce Richardson 	if (c == ' ' ||
3099a2dd95SBruce Richardson 	    c == '\t' )
3199a2dd95SBruce Richardson 		return 1;
3299a2dd95SBruce Richardson 	return 0;
3399a2dd95SBruce Richardson }
3499a2dd95SBruce Richardson 
3599a2dd95SBruce Richardson static int
isendofline(char c)3699a2dd95SBruce Richardson isendofline(char c)
3799a2dd95SBruce Richardson {
3899a2dd95SBruce Richardson 	if (c == '\n' ||
3999a2dd95SBruce Richardson 	    c == '\r' )
4099a2dd95SBruce Richardson 		return 1;
4199a2dd95SBruce Richardson 	return 0;
4299a2dd95SBruce Richardson }
4399a2dd95SBruce Richardson 
4499a2dd95SBruce Richardson static int
iscomment(char c)4599a2dd95SBruce Richardson iscomment(char c)
4699a2dd95SBruce Richardson {
4799a2dd95SBruce Richardson 	if (c == '#')
4899a2dd95SBruce Richardson 		return 1;
4999a2dd95SBruce Richardson 	return 0;
5099a2dd95SBruce Richardson }
5199a2dd95SBruce Richardson 
5299a2dd95SBruce Richardson int
cmdline_isendoftoken(char c)5399a2dd95SBruce Richardson cmdline_isendoftoken(char c)
5499a2dd95SBruce Richardson {
5599a2dd95SBruce Richardson 	if (!c || iscomment(c) || isblank2(c) || isendofline(c))
5699a2dd95SBruce Richardson 		return 1;
5799a2dd95SBruce Richardson 	return 0;
5899a2dd95SBruce Richardson }
5999a2dd95SBruce Richardson 
6099a2dd95SBruce Richardson int
cmdline_isendofcommand(char c)6199a2dd95SBruce Richardson cmdline_isendofcommand(char c)
6299a2dd95SBruce Richardson {
6399a2dd95SBruce Richardson 	if (!c || iscomment(c) || isendofline(c))
6499a2dd95SBruce Richardson 		return 1;
6599a2dd95SBruce Richardson 	return 0;
6699a2dd95SBruce Richardson }
6799a2dd95SBruce Richardson 
6899a2dd95SBruce Richardson static unsigned int
nb_common_chars(const char * s1,const char * s2)6999a2dd95SBruce Richardson nb_common_chars(const char * s1, const char * s2)
7099a2dd95SBruce Richardson {
7199a2dd95SBruce Richardson 	unsigned int i=0;
7299a2dd95SBruce Richardson 
7399a2dd95SBruce Richardson 	while (*s1==*s2 && *s1) {
7499a2dd95SBruce Richardson 		s1++;
7599a2dd95SBruce Richardson 		s2++;
7699a2dd95SBruce Richardson 		i++;
7799a2dd95SBruce Richardson 	}
7899a2dd95SBruce Richardson 	return i;
7999a2dd95SBruce Richardson }
8099a2dd95SBruce Richardson 
8199a2dd95SBruce Richardson /** Retrieve either static or dynamic token at a given index. */
8299a2dd95SBruce Richardson static cmdline_parse_token_hdr_t *
get_token(cmdline_parse_inst_t * inst,unsigned int index)8399a2dd95SBruce Richardson get_token(cmdline_parse_inst_t *inst, unsigned int index)
8499a2dd95SBruce Richardson {
8599a2dd95SBruce Richardson 	cmdline_parse_token_hdr_t *token_p;
8699a2dd95SBruce Richardson 
8799a2dd95SBruce Richardson 	/* check presence of static tokens first */
8899a2dd95SBruce Richardson 	if (inst->tokens[0] || !inst->f)
8999a2dd95SBruce Richardson 		return inst->tokens[index];
9099a2dd95SBruce Richardson 	/* generate dynamic token */
9199a2dd95SBruce Richardson 	token_p = NULL;
9299a2dd95SBruce Richardson 	inst->f(&token_p, NULL, &inst->tokens[index]);
9399a2dd95SBruce Richardson 	return token_p;
9499a2dd95SBruce Richardson }
9599a2dd95SBruce Richardson 
9699a2dd95SBruce Richardson /**
9799a2dd95SBruce Richardson  * try to match the buffer with an instruction (only the first
9899a2dd95SBruce Richardson  * nb_match_token tokens if != 0). Return 0 if we match all the
9999a2dd95SBruce Richardson  * tokens, else the number of matched tokens, else -1.
10099a2dd95SBruce Richardson  */
10199a2dd95SBruce Richardson static int
match_inst(cmdline_parse_inst_t * inst,const char * buf,unsigned int nb_match_token,void * resbuf,unsigned resbuf_size)10299a2dd95SBruce Richardson match_inst(cmdline_parse_inst_t *inst, const char *buf,
10399a2dd95SBruce Richardson 	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
10499a2dd95SBruce Richardson {
10599a2dd95SBruce Richardson 	cmdline_parse_token_hdr_t *token_p = NULL;
10699a2dd95SBruce Richardson 	unsigned int i=0;
10799a2dd95SBruce Richardson 	int n = 0;
10899a2dd95SBruce Richardson 	struct cmdline_token_hdr token_hdr;
10999a2dd95SBruce Richardson 
11099a2dd95SBruce Richardson 	if (resbuf != NULL)
11199a2dd95SBruce Richardson 		memset(resbuf, 0, resbuf_size);
11299a2dd95SBruce Richardson 	/* check if we match all tokens of inst */
11399a2dd95SBruce Richardson 	while (!nb_match_token || i < nb_match_token) {
11499a2dd95SBruce Richardson 		token_p = get_token(inst, i);
11599a2dd95SBruce Richardson 		if (!token_p)
11699a2dd95SBruce Richardson 			break;
11799a2dd95SBruce Richardson 		memcpy(&token_hdr, token_p, sizeof(token_hdr));
11899a2dd95SBruce Richardson 
11999a2dd95SBruce Richardson 		debug_printf("TK\n");
12099a2dd95SBruce Richardson 		/* skip spaces */
12199a2dd95SBruce Richardson 		while (isblank2(*buf)) {
12299a2dd95SBruce Richardson 			buf++;
12399a2dd95SBruce Richardson 		}
12499a2dd95SBruce Richardson 
12599a2dd95SBruce Richardson 		/* end of buf */
12699a2dd95SBruce Richardson 		if ( isendofline(*buf) || iscomment(*buf) )
12799a2dd95SBruce Richardson 			break;
12899a2dd95SBruce Richardson 
12999a2dd95SBruce Richardson 		if (resbuf == NULL) {
13099a2dd95SBruce Richardson 			n = token_hdr.ops->parse(token_p, buf, NULL, 0);
13199a2dd95SBruce Richardson 		} else {
13299a2dd95SBruce Richardson 			unsigned rb_sz;
13399a2dd95SBruce Richardson 
13499a2dd95SBruce Richardson 			if (token_hdr.offset > resbuf_size) {
13599a2dd95SBruce Richardson 				printf("Parse error(%s:%d): Token offset(%u) "
13699a2dd95SBruce Richardson 					"exceeds maximum size(%u)\n",
13799a2dd95SBruce Richardson 					__FILE__, __LINE__,
13899a2dd95SBruce Richardson 					token_hdr.offset, resbuf_size);
13999a2dd95SBruce Richardson 				return -ENOBUFS;
14099a2dd95SBruce Richardson 			}
14199a2dd95SBruce Richardson 			rb_sz = resbuf_size - token_hdr.offset;
14299a2dd95SBruce Richardson 
14399a2dd95SBruce Richardson 			n = token_hdr.ops->parse(token_p, buf, (char *)resbuf +
14499a2dd95SBruce Richardson 				token_hdr.offset, rb_sz);
14599a2dd95SBruce Richardson 		}
14699a2dd95SBruce Richardson 
14799a2dd95SBruce Richardson 		if (n < 0)
14899a2dd95SBruce Richardson 			break;
14999a2dd95SBruce Richardson 
15099a2dd95SBruce Richardson 		debug_printf("TK parsed (len=%d)\n", n);
15199a2dd95SBruce Richardson 		i++;
15299a2dd95SBruce Richardson 		buf += n;
15399a2dd95SBruce Richardson 	}
15499a2dd95SBruce Richardson 
15599a2dd95SBruce Richardson 	/* does not match */
15699a2dd95SBruce Richardson 	if (i==0)
15799a2dd95SBruce Richardson 		return -1;
15899a2dd95SBruce Richardson 
15999a2dd95SBruce Richardson 	/* in case we want to match a specific num of token */
16099a2dd95SBruce Richardson 	if (nb_match_token) {
16199a2dd95SBruce Richardson 		if (i == nb_match_token) {
16299a2dd95SBruce Richardson 			return 0;
16399a2dd95SBruce Richardson 		}
16499a2dd95SBruce Richardson 		return i;
16599a2dd95SBruce Richardson 	}
16699a2dd95SBruce Richardson 
16799a2dd95SBruce Richardson 	/* we don't match all the tokens */
16899a2dd95SBruce Richardson 	if (token_p) {
16999a2dd95SBruce Richardson 		return i;
17099a2dd95SBruce Richardson 	}
17199a2dd95SBruce Richardson 
17299a2dd95SBruce Richardson 	/* are there are some tokens more */
17399a2dd95SBruce Richardson 	while (isblank2(*buf)) {
17499a2dd95SBruce Richardson 		buf++;
17599a2dd95SBruce Richardson 	}
17699a2dd95SBruce Richardson 
17799a2dd95SBruce Richardson 	/* end of buf */
17899a2dd95SBruce Richardson 	if ( isendofline(*buf) || iscomment(*buf) )
17999a2dd95SBruce Richardson 		return 0;
18099a2dd95SBruce Richardson 
18199a2dd95SBruce Richardson 	/* garbage after inst */
18299a2dd95SBruce Richardson 	return i;
18399a2dd95SBruce Richardson }
18499a2dd95SBruce Richardson 
18599a2dd95SBruce Richardson 
186758d9279SBruce Richardson static inline int
__cmdline_parse(struct cmdline * cl,const char * buf,bool call_fn)187758d9279SBruce Richardson __cmdline_parse(struct cmdline *cl, const char *buf, bool call_fn)
18899a2dd95SBruce Richardson {
18999a2dd95SBruce Richardson 	unsigned int inst_num=0;
19099a2dd95SBruce Richardson 	cmdline_parse_inst_t *inst;
19199a2dd95SBruce Richardson 	const char *curbuf;
19299a2dd95SBruce Richardson 	union {
19399a2dd95SBruce Richardson 		char buf[CMDLINE_PARSE_RESULT_BUFSIZE];
19499a2dd95SBruce Richardson 		long double align; /* strong alignment constraint for buf */
19599a2dd95SBruce Richardson 	} result, tmp_result;
19699a2dd95SBruce Richardson 	void (*f)(void *, struct cmdline *, void *) = NULL;
19799a2dd95SBruce Richardson 	void *data = NULL;
19899a2dd95SBruce Richardson 	int comment = 0;
19999a2dd95SBruce Richardson 	int linelen = 0;
20099a2dd95SBruce Richardson 	int parse_it = 0;
20199a2dd95SBruce Richardson 	int err = CMDLINE_PARSE_NOMATCH;
20299a2dd95SBruce Richardson 	int tok;
20399a2dd95SBruce Richardson 	cmdline_parse_ctx_t *ctx;
20499a2dd95SBruce Richardson 	char *result_buf = result.buf;
20599a2dd95SBruce Richardson 
20699a2dd95SBruce Richardson 	if (!cl || !buf)
20799a2dd95SBruce Richardson 		return CMDLINE_PARSE_BAD_ARGS;
20899a2dd95SBruce Richardson 
20999a2dd95SBruce Richardson 	ctx = cl->ctx;
21099a2dd95SBruce Richardson 
21199a2dd95SBruce Richardson 	/*
21299a2dd95SBruce Richardson 	 * - look if the buffer contains at least one line
21399a2dd95SBruce Richardson 	 * - look if line contains only spaces or comments
21499a2dd95SBruce Richardson 	 * - count line length
21599a2dd95SBruce Richardson 	 */
21699a2dd95SBruce Richardson 	curbuf = buf;
21799a2dd95SBruce Richardson 	while (! isendofline(*curbuf)) {
21899a2dd95SBruce Richardson 		if ( *curbuf == '\0' ) {
21999a2dd95SBruce Richardson 			debug_printf("Incomplete buf (len=%d)\n", linelen);
22099a2dd95SBruce Richardson 			return 0;
22199a2dd95SBruce Richardson 		}
22299a2dd95SBruce Richardson 		if ( iscomment(*curbuf) ) {
22399a2dd95SBruce Richardson 			comment = 1;
22499a2dd95SBruce Richardson 		}
22599a2dd95SBruce Richardson 		if ( ! isblank2(*curbuf) && ! comment) {
22699a2dd95SBruce Richardson 			parse_it = 1;
22799a2dd95SBruce Richardson 		}
22899a2dd95SBruce Richardson 		curbuf++;
22999a2dd95SBruce Richardson 		linelen++;
23099a2dd95SBruce Richardson 	}
23199a2dd95SBruce Richardson 
23299a2dd95SBruce Richardson 	/* skip all endofline chars */
23399a2dd95SBruce Richardson 	while (isendofline(buf[linelen])) {
23499a2dd95SBruce Richardson 		linelen++;
23599a2dd95SBruce Richardson 	}
23699a2dd95SBruce Richardson 
23799a2dd95SBruce Richardson 	/* empty line */
23899a2dd95SBruce Richardson 	if ( parse_it == 0 ) {
23999a2dd95SBruce Richardson 		debug_printf("Empty line (len=%d)\n", linelen);
24099a2dd95SBruce Richardson 		return linelen;
24199a2dd95SBruce Richardson 	}
24299a2dd95SBruce Richardson 
24399a2dd95SBruce Richardson 	debug_printf("Parse line : len=%d, <%.*s>\n",
24499a2dd95SBruce Richardson 		     linelen, linelen > 64 ? 64 : linelen, buf);
24599a2dd95SBruce Richardson 
24699a2dd95SBruce Richardson 	/* parse it !! */
24799a2dd95SBruce Richardson 	inst = ctx[inst_num];
24899a2dd95SBruce Richardson 	while (inst) {
24999a2dd95SBruce Richardson 		debug_printf("INST %d\n", inst_num);
25099a2dd95SBruce Richardson 
25199a2dd95SBruce Richardson 		/* fully parsed */
25299a2dd95SBruce Richardson 		tok = match_inst(inst, buf, 0, result_buf,
25399a2dd95SBruce Richardson 				 CMDLINE_PARSE_RESULT_BUFSIZE);
25499a2dd95SBruce Richardson 
25599a2dd95SBruce Richardson 		if (tok > 0) /* we matched at least one token */
25699a2dd95SBruce Richardson 			err = CMDLINE_PARSE_BAD_ARGS;
25799a2dd95SBruce Richardson 
25899a2dd95SBruce Richardson 		else if (!tok) {
25999a2dd95SBruce Richardson 			debug_printf("INST fully parsed\n");
26099a2dd95SBruce Richardson 			/* skip spaces */
26199a2dd95SBruce Richardson 			while (isblank2(*curbuf)) {
26299a2dd95SBruce Richardson 				curbuf++;
26399a2dd95SBruce Richardson 			}
26499a2dd95SBruce Richardson 
26599a2dd95SBruce Richardson 			/* if end of buf -> there is no garbage after inst */
26699a2dd95SBruce Richardson 			if (isendofline(*curbuf) || iscomment(*curbuf)) {
26799a2dd95SBruce Richardson 				if (!f) {
26899a2dd95SBruce Richardson 					memcpy(&f, &inst->f, sizeof(f));
26999a2dd95SBruce Richardson 					memcpy(&data, &inst->data, sizeof(data));
27099a2dd95SBruce Richardson 					result_buf = tmp_result.buf;
27199a2dd95SBruce Richardson 				}
27299a2dd95SBruce Richardson 				else {
27399a2dd95SBruce Richardson 					/* more than 1 inst matches */
27499a2dd95SBruce Richardson 					err = CMDLINE_PARSE_AMBIGUOUS;
27599a2dd95SBruce Richardson 					f=NULL;
27699a2dd95SBruce Richardson 					debug_printf("Ambiguous cmd\n");
27799a2dd95SBruce Richardson 					break;
27899a2dd95SBruce Richardson 				}
27999a2dd95SBruce Richardson 			}
28099a2dd95SBruce Richardson 		}
28199a2dd95SBruce Richardson 
28299a2dd95SBruce Richardson 		inst_num ++;
28399a2dd95SBruce Richardson 		inst = ctx[inst_num];
28499a2dd95SBruce Richardson 	}
28599a2dd95SBruce Richardson 
28699a2dd95SBruce Richardson 	/* no match */
287758d9279SBruce Richardson 	if (f == NULL) {
28899a2dd95SBruce Richardson 		debug_printf("No match err=%d\n", err);
28999a2dd95SBruce Richardson 		return err;
29099a2dd95SBruce Richardson 	}
29199a2dd95SBruce Richardson 
292758d9279SBruce Richardson 	/* call func if requested */
293758d9279SBruce Richardson 	if (call_fn)
294758d9279SBruce Richardson 		f(result.buf, cl, data);
295758d9279SBruce Richardson 
29699a2dd95SBruce Richardson 	return linelen;
29799a2dd95SBruce Richardson }
29899a2dd95SBruce Richardson 
29999a2dd95SBruce Richardson int
cmdline_parse(struct cmdline * cl,const char * buf)300758d9279SBruce Richardson cmdline_parse(struct cmdline *cl, const char *buf)
301758d9279SBruce Richardson {
302758d9279SBruce Richardson 	return __cmdline_parse(cl, buf, true);
303758d9279SBruce Richardson }
304758d9279SBruce Richardson 
305758d9279SBruce Richardson int
cmdline_parse_check(struct cmdline * cl,const char * buf)306758d9279SBruce Richardson cmdline_parse_check(struct cmdline *cl, const char *buf)
307758d9279SBruce Richardson {
308758d9279SBruce Richardson 	return __cmdline_parse(cl, buf, false);
309758d9279SBruce Richardson }
310758d9279SBruce Richardson 
311758d9279SBruce Richardson int
cmdline_complete(struct cmdline * cl,const char * buf,int * state,char * dst,unsigned int size)31299a2dd95SBruce Richardson cmdline_complete(struct cmdline *cl, const char *buf, int *state,
31399a2dd95SBruce Richardson 		 char *dst, unsigned int size)
31499a2dd95SBruce Richardson {
31599a2dd95SBruce Richardson 	const char *partial_tok = buf;
31699a2dd95SBruce Richardson 	unsigned int inst_num = 0;
31799a2dd95SBruce Richardson 	cmdline_parse_inst_t *inst;
31899a2dd95SBruce Richardson 	cmdline_parse_token_hdr_t *token_p;
31999a2dd95SBruce Richardson 	struct cmdline_token_hdr token_hdr;
32099a2dd95SBruce Richardson 	char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
32199a2dd95SBruce Richardson 	unsigned int partial_tok_len;
32299a2dd95SBruce Richardson 	int comp_len = -1;
32399a2dd95SBruce Richardson 	int tmp_len = -1;
32499a2dd95SBruce Richardson 	int nb_token = 0;
32599a2dd95SBruce Richardson 	unsigned int i, n;
32699a2dd95SBruce Richardson 	int l;
32799a2dd95SBruce Richardson 	unsigned int nb_completable;
32899a2dd95SBruce Richardson 	unsigned int nb_non_completable;
32999a2dd95SBruce Richardson 	int local_state = 0;
33099a2dd95SBruce Richardson 	const char *help_str;
33199a2dd95SBruce Richardson 	cmdline_parse_ctx_t *ctx;
33299a2dd95SBruce Richardson 
33399a2dd95SBruce Richardson 	if (!cl || !buf || !state || !dst)
33499a2dd95SBruce Richardson 		return -1;
33599a2dd95SBruce Richardson 
33699a2dd95SBruce Richardson 	ctx = cl->ctx;
33799a2dd95SBruce Richardson 
33899a2dd95SBruce Richardson 	debug_printf("%s called\n", __func__);
33999a2dd95SBruce Richardson 	memset(&token_hdr, 0, sizeof(token_hdr));
34099a2dd95SBruce Richardson 
34199a2dd95SBruce Richardson 	/* count the number of complete token to parse */
34299a2dd95SBruce Richardson 	for (i=0 ; buf[i] ; i++) {
34399a2dd95SBruce Richardson 		if (!isblank2(buf[i]) && isblank2(buf[i+1]))
34499a2dd95SBruce Richardson 			nb_token++;
34599a2dd95SBruce Richardson 		if (isblank2(buf[i]) && !isblank2(buf[i+1]))
34699a2dd95SBruce Richardson 			partial_tok = buf+i+1;
34799a2dd95SBruce Richardson 	}
34899a2dd95SBruce Richardson 	partial_tok_len = strnlen(partial_tok, RDLINE_BUF_SIZE);
34999a2dd95SBruce Richardson 
35099a2dd95SBruce Richardson 	/* first call -> do a first pass */
35199a2dd95SBruce Richardson 	if (*state <= 0) {
35299a2dd95SBruce Richardson 		debug_printf("try complete <%s>\n", buf);
35399a2dd95SBruce Richardson 		debug_printf("there is %d complete tokens, <%s> is incomplete\n",
35499a2dd95SBruce Richardson 			     nb_token, partial_tok);
35599a2dd95SBruce Richardson 
35699a2dd95SBruce Richardson 		nb_completable = 0;
35799a2dd95SBruce Richardson 		nb_non_completable = 0;
35899a2dd95SBruce Richardson 
35999a2dd95SBruce Richardson 		inst = ctx[inst_num];
36099a2dd95SBruce Richardson 		while (inst) {
36199a2dd95SBruce Richardson 			/* parse the first tokens of the inst */
36299a2dd95SBruce Richardson 			if (nb_token &&
36399a2dd95SBruce Richardson 			    match_inst(inst, buf, nb_token, NULL, 0))
36499a2dd95SBruce Richardson 				goto next;
36599a2dd95SBruce Richardson 
36699a2dd95SBruce Richardson 			debug_printf("instruction match\n");
36799a2dd95SBruce Richardson 			token_p = get_token(inst, nb_token);
36899a2dd95SBruce Richardson 			if (token_p)
36999a2dd95SBruce Richardson 				memcpy(&token_hdr, token_p, sizeof(token_hdr));
37099a2dd95SBruce Richardson 
37199a2dd95SBruce Richardson 			/* non completable */
37299a2dd95SBruce Richardson 			if (!token_p ||
37399a2dd95SBruce Richardson 			    !token_hdr.ops->complete_get_nb ||
37499a2dd95SBruce Richardson 			    !token_hdr.ops->complete_get_elt ||
37599a2dd95SBruce Richardson 			    (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
37699a2dd95SBruce Richardson 				nb_non_completable++;
37799a2dd95SBruce Richardson 				goto next;
37899a2dd95SBruce Richardson 			}
37999a2dd95SBruce Richardson 
38099a2dd95SBruce Richardson 			debug_printf("%d choices for this token\n", n);
38199a2dd95SBruce Richardson 			for (i=0 ; i<n ; i++) {
38299a2dd95SBruce Richardson 				if (token_hdr.ops->complete_get_elt(token_p, i,
38399a2dd95SBruce Richardson 								    tmpbuf,
38499a2dd95SBruce Richardson 								    sizeof(tmpbuf)) < 0)
38599a2dd95SBruce Richardson 					continue;
38699a2dd95SBruce Richardson 
38799a2dd95SBruce Richardson 				/* we have at least room for one char */
38899a2dd95SBruce Richardson 				tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
38999a2dd95SBruce Richardson 				if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
39099a2dd95SBruce Richardson 					tmpbuf[tmp_len] = ' ';
39199a2dd95SBruce Richardson 					tmpbuf[tmp_len+1] = 0;
39299a2dd95SBruce Richardson 				}
39399a2dd95SBruce Richardson 
39499a2dd95SBruce Richardson 				debug_printf("   choice <%s>\n", tmpbuf);
39599a2dd95SBruce Richardson 
39699a2dd95SBruce Richardson 				/* does the completion match the
39799a2dd95SBruce Richardson 				 * beginning of the word ? */
39899a2dd95SBruce Richardson 				if (!strncmp(partial_tok, tmpbuf,
39999a2dd95SBruce Richardson 					     partial_tok_len)) {
40099a2dd95SBruce Richardson 					if (comp_len == -1) {
40199a2dd95SBruce Richardson 						strlcpy(comp_buf,
40299a2dd95SBruce Richardson 							tmpbuf + partial_tok_len,
40399a2dd95SBruce Richardson 							sizeof(comp_buf));
40499a2dd95SBruce Richardson 						comp_len =
40599a2dd95SBruce Richardson 							strnlen(tmpbuf + partial_tok_len,
40699a2dd95SBruce Richardson 									sizeof(tmpbuf) - partial_tok_len);
40799a2dd95SBruce Richardson 
40899a2dd95SBruce Richardson 					}
40999a2dd95SBruce Richardson 					else {
41099a2dd95SBruce Richardson 						comp_len =
41199a2dd95SBruce Richardson 							nb_common_chars(comp_buf,
41299a2dd95SBruce Richardson 									tmpbuf+partial_tok_len);
41399a2dd95SBruce Richardson 						comp_buf[comp_len] = 0;
41499a2dd95SBruce Richardson 					}
41599a2dd95SBruce Richardson 					nb_completable++;
41699a2dd95SBruce Richardson 				}
41799a2dd95SBruce Richardson 			}
41899a2dd95SBruce Richardson 		next:
41999a2dd95SBruce Richardson 			debug_printf("next\n");
42099a2dd95SBruce Richardson 			inst_num ++;
42199a2dd95SBruce Richardson 			inst = ctx[inst_num];
42299a2dd95SBruce Richardson 		}
42399a2dd95SBruce Richardson 
42499a2dd95SBruce Richardson 		debug_printf("total choices %d for this completion\n",
42599a2dd95SBruce Richardson 			     nb_completable);
42699a2dd95SBruce Richardson 
42799a2dd95SBruce Richardson 		/* no possible completion */
42899a2dd95SBruce Richardson 		if (nb_completable == 0 && nb_non_completable == 0)
42999a2dd95SBruce Richardson 			return 0;
43099a2dd95SBruce Richardson 
43199a2dd95SBruce Richardson 		/* if multichoice is not required */
43299a2dd95SBruce Richardson 		if (*state == 0 && partial_tok_len > 0) {
43399a2dd95SBruce Richardson 			/* one or several choices starting with the
43499a2dd95SBruce Richardson 			   same chars */
43599a2dd95SBruce Richardson 			if (comp_len > 0) {
43699a2dd95SBruce Richardson 				if ((unsigned)(comp_len + 1) > size)
43799a2dd95SBruce Richardson 					return 0;
43899a2dd95SBruce Richardson 
43999a2dd95SBruce Richardson 				strlcpy(dst, comp_buf, size);
44099a2dd95SBruce Richardson 				dst[comp_len] = 0;
44199a2dd95SBruce Richardson 				return 2;
44299a2dd95SBruce Richardson 			}
44399a2dd95SBruce Richardson 		}
44499a2dd95SBruce Richardson 	}
44599a2dd95SBruce Richardson 
44699a2dd95SBruce Richardson 	/* init state correctly */
44799a2dd95SBruce Richardson 	if (*state == -1)
44899a2dd95SBruce Richardson 		*state = 0;
44999a2dd95SBruce Richardson 
45099a2dd95SBruce Richardson 	debug_printf("Multiple choice STATE=%d\n", *state);
45199a2dd95SBruce Richardson 
45299a2dd95SBruce Richardson 	inst_num = 0;
45399a2dd95SBruce Richardson 	inst = ctx[inst_num];
45499a2dd95SBruce Richardson 	while (inst) {
45599a2dd95SBruce Richardson 		/* we need to redo it */
45699a2dd95SBruce Richardson 		inst = ctx[inst_num];
45799a2dd95SBruce Richardson 
45899a2dd95SBruce Richardson 		if (nb_token &&
45999a2dd95SBruce Richardson 		    match_inst(inst, buf, nb_token, NULL, 0))
46099a2dd95SBruce Richardson 			goto next2;
46199a2dd95SBruce Richardson 
46299a2dd95SBruce Richardson 		token_p = get_token(inst, nb_token);
46399a2dd95SBruce Richardson 		if (token_p)
46499a2dd95SBruce Richardson 			memcpy(&token_hdr, token_p, sizeof(token_hdr));
46599a2dd95SBruce Richardson 
46699a2dd95SBruce Richardson 		/* one choice for this token */
46799a2dd95SBruce Richardson 		if (!token_p ||
46899a2dd95SBruce Richardson 		    !token_hdr.ops->complete_get_nb ||
46999a2dd95SBruce Richardson 		    !token_hdr.ops->complete_get_elt ||
47099a2dd95SBruce Richardson 		    (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
47199a2dd95SBruce Richardson 			if (local_state < *state) {
47299a2dd95SBruce Richardson 				local_state++;
47399a2dd95SBruce Richardson 				goto next2;
47499a2dd95SBruce Richardson 			}
47599a2dd95SBruce Richardson 			(*state)++;
47699a2dd95SBruce Richardson 			if (token_p && token_hdr.ops->get_help) {
47799a2dd95SBruce Richardson 				token_hdr.ops->get_help(token_p, tmpbuf,
47899a2dd95SBruce Richardson 							sizeof(tmpbuf));
47999a2dd95SBruce Richardson 				help_str = inst->help_str;
48099a2dd95SBruce Richardson 				if (help_str)
48199a2dd95SBruce Richardson 					snprintf(dst, size, "[%s]: %s", tmpbuf,
48299a2dd95SBruce Richardson 						 help_str);
48399a2dd95SBruce Richardson 				else
48499a2dd95SBruce Richardson 					snprintf(dst, size, "[%s]: No help",
48599a2dd95SBruce Richardson 						 tmpbuf);
48699a2dd95SBruce Richardson 			}
48799a2dd95SBruce Richardson 			else {
48899a2dd95SBruce Richardson 				snprintf(dst, size, "[RETURN]");
48999a2dd95SBruce Richardson 			}
49099a2dd95SBruce Richardson 			return 1;
49199a2dd95SBruce Richardson 		}
49299a2dd95SBruce Richardson 
49399a2dd95SBruce Richardson 		/* several choices */
49499a2dd95SBruce Richardson 		for (i=0 ; i<n ; i++) {
49599a2dd95SBruce Richardson 			if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf,
49699a2dd95SBruce Richardson 							    sizeof(tmpbuf)) < 0)
49799a2dd95SBruce Richardson 				continue;
49899a2dd95SBruce Richardson 			/* we have at least room for one char */
49999a2dd95SBruce Richardson 			tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
50099a2dd95SBruce Richardson 			if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
50199a2dd95SBruce Richardson 				tmpbuf[tmp_len] = ' ';
50299a2dd95SBruce Richardson 				tmpbuf[tmp_len + 1] = 0;
50399a2dd95SBruce Richardson 			}
50499a2dd95SBruce Richardson 
50599a2dd95SBruce Richardson 			debug_printf("   choice <%s>\n", tmpbuf);
50699a2dd95SBruce Richardson 
50799a2dd95SBruce Richardson 			/* does the completion match the beginning of
50899a2dd95SBruce Richardson 			 * the word ? */
50999a2dd95SBruce Richardson 			if (!strncmp(partial_tok, tmpbuf,
51099a2dd95SBruce Richardson 				     partial_tok_len)) {
51199a2dd95SBruce Richardson 				if (local_state < *state) {
51299a2dd95SBruce Richardson 					local_state++;
51399a2dd95SBruce Richardson 					continue;
51499a2dd95SBruce Richardson 				}
51599a2dd95SBruce Richardson 				(*state)++;
51699a2dd95SBruce Richardson 				l=strlcpy(dst, tmpbuf, size);
51799a2dd95SBruce Richardson 				if (l>=0 && token_hdr.ops->get_help) {
51899a2dd95SBruce Richardson 					token_hdr.ops->get_help(token_p, tmpbuf,
51999a2dd95SBruce Richardson 								sizeof(tmpbuf));
52099a2dd95SBruce Richardson 					help_str = inst->help_str;
52199a2dd95SBruce Richardson 					if (help_str)
52299a2dd95SBruce Richardson 						snprintf(dst+l, size-l, "[%s]: %s",
52399a2dd95SBruce Richardson 							 tmpbuf, help_str);
52499a2dd95SBruce Richardson 					else
52599a2dd95SBruce Richardson 						snprintf(dst+l, size-l,
52699a2dd95SBruce Richardson 							 "[%s]: No help", tmpbuf);
52799a2dd95SBruce Richardson 				}
52899a2dd95SBruce Richardson 
52999a2dd95SBruce Richardson 				return 1;
53099a2dd95SBruce Richardson 			}
53199a2dd95SBruce Richardson 		}
53299a2dd95SBruce Richardson 	next2:
53399a2dd95SBruce Richardson 		inst_num ++;
53499a2dd95SBruce Richardson 		inst = ctx[inst_num];
53599a2dd95SBruce Richardson 	}
53699a2dd95SBruce Richardson 	return 0;
53799a2dd95SBruce Richardson }
538