xref: /dpdk/lib/cmdline/cmdline_parse.c (revision 30a1de10)
1*99a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
2*99a2dd95SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation.
3*99a2dd95SBruce Richardson  * Copyright (c) 2009, Olivier MATZ <[email protected]>
4*99a2dd95SBruce Richardson  * All rights reserved.
5*99a2dd95SBruce Richardson  */
6*99a2dd95SBruce Richardson 
7*99a2dd95SBruce Richardson #include <stdio.h>
8*99a2dd95SBruce Richardson #include <errno.h>
9*99a2dd95SBruce Richardson #include <string.h>
10*99a2dd95SBruce Richardson 
11*99a2dd95SBruce Richardson #include <rte_string_fns.h>
12*99a2dd95SBruce Richardson 
13*99a2dd95SBruce Richardson #include "cmdline_private.h"
14*99a2dd95SBruce Richardson 
15*99a2dd95SBruce Richardson #ifdef RTE_LIBRTE_CMDLINE_DEBUG
16*99a2dd95SBruce Richardson #define debug_printf printf
17*99a2dd95SBruce Richardson #else
18*99a2dd95SBruce Richardson #define debug_printf(args...) do {} while(0)
19*99a2dd95SBruce Richardson #endif
20*99a2dd95SBruce Richardson 
21*99a2dd95SBruce Richardson #define CMDLINE_BUFFER_SIZE 64
22*99a2dd95SBruce Richardson 
23*99a2dd95SBruce Richardson /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
24*99a2dd95SBruce Richardson  * own. */
25*99a2dd95SBruce Richardson static int
isblank2(char c)26*99a2dd95SBruce Richardson isblank2(char c)
27*99a2dd95SBruce Richardson {
28*99a2dd95SBruce Richardson 	if (c == ' ' ||
29*99a2dd95SBruce Richardson 	    c == '\t' )
30*99a2dd95SBruce Richardson 		return 1;
31*99a2dd95SBruce Richardson 	return 0;
32*99a2dd95SBruce Richardson }
33*99a2dd95SBruce Richardson 
34*99a2dd95SBruce Richardson static int
isendofline(char c)35*99a2dd95SBruce Richardson isendofline(char c)
36*99a2dd95SBruce Richardson {
37*99a2dd95SBruce Richardson 	if (c == '\n' ||
38*99a2dd95SBruce Richardson 	    c == '\r' )
39*99a2dd95SBruce Richardson 		return 1;
40*99a2dd95SBruce Richardson 	return 0;
41*99a2dd95SBruce Richardson }
42*99a2dd95SBruce Richardson 
43*99a2dd95SBruce Richardson static int
iscomment(char c)44*99a2dd95SBruce Richardson iscomment(char c)
45*99a2dd95SBruce Richardson {
46*99a2dd95SBruce Richardson 	if (c == '#')
47*99a2dd95SBruce Richardson 		return 1;
48*99a2dd95SBruce Richardson 	return 0;
49*99a2dd95SBruce Richardson }
50*99a2dd95SBruce Richardson 
51*99a2dd95SBruce Richardson int
cmdline_isendoftoken(char c)52*99a2dd95SBruce Richardson cmdline_isendoftoken(char c)
53*99a2dd95SBruce Richardson {
54*99a2dd95SBruce Richardson 	if (!c || iscomment(c) || isblank2(c) || isendofline(c))
55*99a2dd95SBruce Richardson 		return 1;
56*99a2dd95SBruce Richardson 	return 0;
57*99a2dd95SBruce Richardson }
58*99a2dd95SBruce Richardson 
59*99a2dd95SBruce Richardson int
cmdline_isendofcommand(char c)60*99a2dd95SBruce Richardson cmdline_isendofcommand(char c)
61*99a2dd95SBruce Richardson {
62*99a2dd95SBruce Richardson 	if (!c || iscomment(c) || isendofline(c))
63*99a2dd95SBruce Richardson 		return 1;
64*99a2dd95SBruce Richardson 	return 0;
65*99a2dd95SBruce Richardson }
66*99a2dd95SBruce Richardson 
67*99a2dd95SBruce Richardson static unsigned int
nb_common_chars(const char * s1,const char * s2)68*99a2dd95SBruce Richardson nb_common_chars(const char * s1, const char * s2)
69*99a2dd95SBruce Richardson {
70*99a2dd95SBruce Richardson 	unsigned int i=0;
71*99a2dd95SBruce Richardson 
72*99a2dd95SBruce Richardson 	while (*s1==*s2 && *s1) {
73*99a2dd95SBruce Richardson 		s1++;
74*99a2dd95SBruce Richardson 		s2++;
75*99a2dd95SBruce Richardson 		i++;
76*99a2dd95SBruce Richardson 	}
77*99a2dd95SBruce Richardson 	return i;
78*99a2dd95SBruce Richardson }
79*99a2dd95SBruce Richardson 
80*99a2dd95SBruce Richardson /** Retrieve either static or dynamic token at a given index. */
81*99a2dd95SBruce Richardson static cmdline_parse_token_hdr_t *
get_token(cmdline_parse_inst_t * inst,unsigned int index)82*99a2dd95SBruce Richardson get_token(cmdline_parse_inst_t *inst, unsigned int index)
83*99a2dd95SBruce Richardson {
84*99a2dd95SBruce Richardson 	cmdline_parse_token_hdr_t *token_p;
85*99a2dd95SBruce Richardson 
86*99a2dd95SBruce Richardson 	/* check presence of static tokens first */
87*99a2dd95SBruce Richardson 	if (inst->tokens[0] || !inst->f)
88*99a2dd95SBruce Richardson 		return inst->tokens[index];
89*99a2dd95SBruce Richardson 	/* generate dynamic token */
90*99a2dd95SBruce Richardson 	token_p = NULL;
91*99a2dd95SBruce Richardson 	inst->f(&token_p, NULL, &inst->tokens[index]);
92*99a2dd95SBruce Richardson 	return token_p;
93*99a2dd95SBruce Richardson }
94*99a2dd95SBruce Richardson 
95*99a2dd95SBruce Richardson /**
96*99a2dd95SBruce Richardson  * try to match the buffer with an instruction (only the first
97*99a2dd95SBruce Richardson  * nb_match_token tokens if != 0). Return 0 if we match all the
98*99a2dd95SBruce Richardson  * tokens, else the number of matched tokens, else -1.
99*99a2dd95SBruce Richardson  */
100*99a2dd95SBruce Richardson static int
match_inst(cmdline_parse_inst_t * inst,const char * buf,unsigned int nb_match_token,void * resbuf,unsigned resbuf_size)101*99a2dd95SBruce Richardson match_inst(cmdline_parse_inst_t *inst, const char *buf,
102*99a2dd95SBruce Richardson 	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
103*99a2dd95SBruce Richardson {
104*99a2dd95SBruce Richardson 	cmdline_parse_token_hdr_t *token_p = NULL;
105*99a2dd95SBruce Richardson 	unsigned int i=0;
106*99a2dd95SBruce Richardson 	int n = 0;
107*99a2dd95SBruce Richardson 	struct cmdline_token_hdr token_hdr;
108*99a2dd95SBruce Richardson 
109*99a2dd95SBruce Richardson 	if (resbuf != NULL)
110*99a2dd95SBruce Richardson 		memset(resbuf, 0, resbuf_size);
111*99a2dd95SBruce Richardson 	/* check if we match all tokens of inst */
112*99a2dd95SBruce Richardson 	while (!nb_match_token || i < nb_match_token) {
113*99a2dd95SBruce Richardson 		token_p = get_token(inst, i);
114*99a2dd95SBruce Richardson 		if (!token_p)
115*99a2dd95SBruce Richardson 			break;
116*99a2dd95SBruce Richardson 		memcpy(&token_hdr, token_p, sizeof(token_hdr));
117*99a2dd95SBruce Richardson 
118*99a2dd95SBruce Richardson 		debug_printf("TK\n");
119*99a2dd95SBruce Richardson 		/* skip spaces */
120*99a2dd95SBruce Richardson 		while (isblank2(*buf)) {
121*99a2dd95SBruce Richardson 			buf++;
122*99a2dd95SBruce Richardson 		}
123*99a2dd95SBruce Richardson 
124*99a2dd95SBruce Richardson 		/* end of buf */
125*99a2dd95SBruce Richardson 		if ( isendofline(*buf) || iscomment(*buf) )
126*99a2dd95SBruce Richardson 			break;
127*99a2dd95SBruce Richardson 
128*99a2dd95SBruce Richardson 		if (resbuf == NULL) {
129*99a2dd95SBruce Richardson 			n = token_hdr.ops->parse(token_p, buf, NULL, 0);
130*99a2dd95SBruce Richardson 		} else {
131*99a2dd95SBruce Richardson 			unsigned rb_sz;
132*99a2dd95SBruce Richardson 
133*99a2dd95SBruce Richardson 			if (token_hdr.offset > resbuf_size) {
134*99a2dd95SBruce Richardson 				printf("Parse error(%s:%d): Token offset(%u) "
135*99a2dd95SBruce Richardson 					"exceeds maximum size(%u)\n",
136*99a2dd95SBruce Richardson 					__FILE__, __LINE__,
137*99a2dd95SBruce Richardson 					token_hdr.offset, resbuf_size);
138*99a2dd95SBruce Richardson 				return -ENOBUFS;
139*99a2dd95SBruce Richardson 			}
140*99a2dd95SBruce Richardson 			rb_sz = resbuf_size - token_hdr.offset;
141*99a2dd95SBruce Richardson 
142*99a2dd95SBruce Richardson 			n = token_hdr.ops->parse(token_p, buf, (char *)resbuf +
143*99a2dd95SBruce Richardson 				token_hdr.offset, rb_sz);
144*99a2dd95SBruce Richardson 		}
145*99a2dd95SBruce Richardson 
146*99a2dd95SBruce Richardson 		if (n < 0)
147*99a2dd95SBruce Richardson 			break;
148*99a2dd95SBruce Richardson 
149*99a2dd95SBruce Richardson 		debug_printf("TK parsed (len=%d)\n", n);
150*99a2dd95SBruce Richardson 		i++;
151*99a2dd95SBruce Richardson 		buf += n;
152*99a2dd95SBruce Richardson 	}
153*99a2dd95SBruce Richardson 
154*99a2dd95SBruce Richardson 	/* does not match */
155*99a2dd95SBruce Richardson 	if (i==0)
156*99a2dd95SBruce Richardson 		return -1;
157*99a2dd95SBruce Richardson 
158*99a2dd95SBruce Richardson 	/* in case we want to match a specific num of token */
159*99a2dd95SBruce Richardson 	if (nb_match_token) {
160*99a2dd95SBruce Richardson 		if (i == nb_match_token) {
161*99a2dd95SBruce Richardson 			return 0;
162*99a2dd95SBruce Richardson 		}
163*99a2dd95SBruce Richardson 		return i;
164*99a2dd95SBruce Richardson 	}
165*99a2dd95SBruce Richardson 
166*99a2dd95SBruce Richardson 	/* we don't match all the tokens */
167*99a2dd95SBruce Richardson 	if (token_p) {
168*99a2dd95SBruce Richardson 		return i;
169*99a2dd95SBruce Richardson 	}
170*99a2dd95SBruce Richardson 
171*99a2dd95SBruce Richardson 	/* are there are some tokens more */
172*99a2dd95SBruce Richardson 	while (isblank2(*buf)) {
173*99a2dd95SBruce Richardson 		buf++;
174*99a2dd95SBruce Richardson 	}
175*99a2dd95SBruce Richardson 
176*99a2dd95SBruce Richardson 	/* end of buf */
177*99a2dd95SBruce Richardson 	if ( isendofline(*buf) || iscomment(*buf) )
178*99a2dd95SBruce Richardson 		return 0;
179*99a2dd95SBruce Richardson 
180*99a2dd95SBruce Richardson 	/* garbage after inst */
181*99a2dd95SBruce Richardson 	return i;
182*99a2dd95SBruce Richardson }
183*99a2dd95SBruce Richardson 
184*99a2dd95SBruce Richardson 
185*99a2dd95SBruce Richardson int
cmdline_parse(struct cmdline * cl,const char * buf)186*99a2dd95SBruce Richardson cmdline_parse(struct cmdline *cl, const char * buf)
187*99a2dd95SBruce Richardson {
188*99a2dd95SBruce Richardson 	unsigned int inst_num=0;
189*99a2dd95SBruce Richardson 	cmdline_parse_inst_t *inst;
190*99a2dd95SBruce Richardson 	const char *curbuf;
191*99a2dd95SBruce Richardson 	union {
192*99a2dd95SBruce Richardson 		char buf[CMDLINE_PARSE_RESULT_BUFSIZE];
193*99a2dd95SBruce Richardson 		long double align; /* strong alignment constraint for buf */
194*99a2dd95SBruce Richardson 	} result, tmp_result;
195*99a2dd95SBruce Richardson 	void (*f)(void *, struct cmdline *, void *) = NULL;
196*99a2dd95SBruce Richardson 	void *data = NULL;
197*99a2dd95SBruce Richardson 	int comment = 0;
198*99a2dd95SBruce Richardson 	int linelen = 0;
199*99a2dd95SBruce Richardson 	int parse_it = 0;
200*99a2dd95SBruce Richardson 	int err = CMDLINE_PARSE_NOMATCH;
201*99a2dd95SBruce Richardson 	int tok;
202*99a2dd95SBruce Richardson 	cmdline_parse_ctx_t *ctx;
203*99a2dd95SBruce Richardson 	char *result_buf = result.buf;
204*99a2dd95SBruce Richardson 
205*99a2dd95SBruce Richardson 	if (!cl || !buf)
206*99a2dd95SBruce Richardson 		return CMDLINE_PARSE_BAD_ARGS;
207*99a2dd95SBruce Richardson 
208*99a2dd95SBruce Richardson 	ctx = cl->ctx;
209*99a2dd95SBruce Richardson 
210*99a2dd95SBruce Richardson 	/*
211*99a2dd95SBruce Richardson 	 * - look if the buffer contains at least one line
212*99a2dd95SBruce Richardson 	 * - look if line contains only spaces or comments
213*99a2dd95SBruce Richardson 	 * - count line length
214*99a2dd95SBruce Richardson 	 */
215*99a2dd95SBruce Richardson 	curbuf = buf;
216*99a2dd95SBruce Richardson 	while (! isendofline(*curbuf)) {
217*99a2dd95SBruce Richardson 		if ( *curbuf == '\0' ) {
218*99a2dd95SBruce Richardson 			debug_printf("Incomplete buf (len=%d)\n", linelen);
219*99a2dd95SBruce Richardson 			return 0;
220*99a2dd95SBruce Richardson 		}
221*99a2dd95SBruce Richardson 		if ( iscomment(*curbuf) ) {
222*99a2dd95SBruce Richardson 			comment = 1;
223*99a2dd95SBruce Richardson 		}
224*99a2dd95SBruce Richardson 		if ( ! isblank2(*curbuf) && ! comment) {
225*99a2dd95SBruce Richardson 			parse_it = 1;
226*99a2dd95SBruce Richardson 		}
227*99a2dd95SBruce Richardson 		curbuf++;
228*99a2dd95SBruce Richardson 		linelen++;
229*99a2dd95SBruce Richardson 	}
230*99a2dd95SBruce Richardson 
231*99a2dd95SBruce Richardson 	/* skip all endofline chars */
232*99a2dd95SBruce Richardson 	while (isendofline(buf[linelen])) {
233*99a2dd95SBruce Richardson 		linelen++;
234*99a2dd95SBruce Richardson 	}
235*99a2dd95SBruce Richardson 
236*99a2dd95SBruce Richardson 	/* empty line */
237*99a2dd95SBruce Richardson 	if ( parse_it == 0 ) {
238*99a2dd95SBruce Richardson 		debug_printf("Empty line (len=%d)\n", linelen);
239*99a2dd95SBruce Richardson 		return linelen;
240*99a2dd95SBruce Richardson 	}
241*99a2dd95SBruce Richardson 
242*99a2dd95SBruce Richardson 	debug_printf("Parse line : len=%d, <%.*s>\n",
243*99a2dd95SBruce Richardson 		     linelen, linelen > 64 ? 64 : linelen, buf);
244*99a2dd95SBruce Richardson 
245*99a2dd95SBruce Richardson 	/* parse it !! */
246*99a2dd95SBruce Richardson 	inst = ctx[inst_num];
247*99a2dd95SBruce Richardson 	while (inst) {
248*99a2dd95SBruce Richardson 		debug_printf("INST %d\n", inst_num);
249*99a2dd95SBruce Richardson 
250*99a2dd95SBruce Richardson 		/* fully parsed */
251*99a2dd95SBruce Richardson 		tok = match_inst(inst, buf, 0, result_buf,
252*99a2dd95SBruce Richardson 				 CMDLINE_PARSE_RESULT_BUFSIZE);
253*99a2dd95SBruce Richardson 
254*99a2dd95SBruce Richardson 		if (tok > 0) /* we matched at least one token */
255*99a2dd95SBruce Richardson 			err = CMDLINE_PARSE_BAD_ARGS;
256*99a2dd95SBruce Richardson 
257*99a2dd95SBruce Richardson 		else if (!tok) {
258*99a2dd95SBruce Richardson 			debug_printf("INST fully parsed\n");
259*99a2dd95SBruce Richardson 			/* skip spaces */
260*99a2dd95SBruce Richardson 			while (isblank2(*curbuf)) {
261*99a2dd95SBruce Richardson 				curbuf++;
262*99a2dd95SBruce Richardson 			}
263*99a2dd95SBruce Richardson 
264*99a2dd95SBruce Richardson 			/* if end of buf -> there is no garbage after inst */
265*99a2dd95SBruce Richardson 			if (isendofline(*curbuf) || iscomment(*curbuf)) {
266*99a2dd95SBruce Richardson 				if (!f) {
267*99a2dd95SBruce Richardson 					memcpy(&f, &inst->f, sizeof(f));
268*99a2dd95SBruce Richardson 					memcpy(&data, &inst->data, sizeof(data));
269*99a2dd95SBruce Richardson 					result_buf = tmp_result.buf;
270*99a2dd95SBruce Richardson 				}
271*99a2dd95SBruce Richardson 				else {
272*99a2dd95SBruce Richardson 					/* more than 1 inst matches */
273*99a2dd95SBruce Richardson 					err = CMDLINE_PARSE_AMBIGUOUS;
274*99a2dd95SBruce Richardson 					f=NULL;
275*99a2dd95SBruce Richardson 					debug_printf("Ambiguous cmd\n");
276*99a2dd95SBruce Richardson 					break;
277*99a2dd95SBruce Richardson 				}
278*99a2dd95SBruce Richardson 			}
279*99a2dd95SBruce Richardson 		}
280*99a2dd95SBruce Richardson 
281*99a2dd95SBruce Richardson 		inst_num ++;
282*99a2dd95SBruce Richardson 		inst = ctx[inst_num];
283*99a2dd95SBruce Richardson 	}
284*99a2dd95SBruce Richardson 
285*99a2dd95SBruce Richardson 	/* call func */
286*99a2dd95SBruce Richardson 	if (f) {
287*99a2dd95SBruce Richardson 		f(result.buf, cl, data);
288*99a2dd95SBruce Richardson 	}
289*99a2dd95SBruce Richardson 
290*99a2dd95SBruce Richardson 	/* no match */
291*99a2dd95SBruce Richardson 	else {
292*99a2dd95SBruce Richardson 		debug_printf("No match err=%d\n", err);
293*99a2dd95SBruce Richardson 		return err;
294*99a2dd95SBruce Richardson 	}
295*99a2dd95SBruce Richardson 
296*99a2dd95SBruce Richardson 	return linelen;
297*99a2dd95SBruce Richardson }
298*99a2dd95SBruce Richardson 
299*99a2dd95SBruce Richardson int
cmdline_complete(struct cmdline * cl,const char * buf,int * state,char * dst,unsigned int size)300*99a2dd95SBruce Richardson cmdline_complete(struct cmdline *cl, const char *buf, int *state,
301*99a2dd95SBruce Richardson 		 char *dst, unsigned int size)
302*99a2dd95SBruce Richardson {
303*99a2dd95SBruce Richardson 	const char *partial_tok = buf;
304*99a2dd95SBruce Richardson 	unsigned int inst_num = 0;
305*99a2dd95SBruce Richardson 	cmdline_parse_inst_t *inst;
306*99a2dd95SBruce Richardson 	cmdline_parse_token_hdr_t *token_p;
307*99a2dd95SBruce Richardson 	struct cmdline_token_hdr token_hdr;
308*99a2dd95SBruce Richardson 	char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
309*99a2dd95SBruce Richardson 	unsigned int partial_tok_len;
310*99a2dd95SBruce Richardson 	int comp_len = -1;
311*99a2dd95SBruce Richardson 	int tmp_len = -1;
312*99a2dd95SBruce Richardson 	int nb_token = 0;
313*99a2dd95SBruce Richardson 	unsigned int i, n;
314*99a2dd95SBruce Richardson 	int l;
315*99a2dd95SBruce Richardson 	unsigned int nb_completable;
316*99a2dd95SBruce Richardson 	unsigned int nb_non_completable;
317*99a2dd95SBruce Richardson 	int local_state = 0;
318*99a2dd95SBruce Richardson 	const char *help_str;
319*99a2dd95SBruce Richardson 	cmdline_parse_ctx_t *ctx;
320*99a2dd95SBruce Richardson 
321*99a2dd95SBruce Richardson 	if (!cl || !buf || !state || !dst)
322*99a2dd95SBruce Richardson 		return -1;
323*99a2dd95SBruce Richardson 
324*99a2dd95SBruce Richardson 	ctx = cl->ctx;
325*99a2dd95SBruce Richardson 
326*99a2dd95SBruce Richardson 	debug_printf("%s called\n", __func__);
327*99a2dd95SBruce Richardson 	memset(&token_hdr, 0, sizeof(token_hdr));
328*99a2dd95SBruce Richardson 
329*99a2dd95SBruce Richardson 	/* count the number of complete token to parse */
330*99a2dd95SBruce Richardson 	for (i=0 ; buf[i] ; i++) {
331*99a2dd95SBruce Richardson 		if (!isblank2(buf[i]) && isblank2(buf[i+1]))
332*99a2dd95SBruce Richardson 			nb_token++;
333*99a2dd95SBruce Richardson 		if (isblank2(buf[i]) && !isblank2(buf[i+1]))
334*99a2dd95SBruce Richardson 			partial_tok = buf+i+1;
335*99a2dd95SBruce Richardson 	}
336*99a2dd95SBruce Richardson 	partial_tok_len = strnlen(partial_tok, RDLINE_BUF_SIZE);
337*99a2dd95SBruce Richardson 
338*99a2dd95SBruce Richardson 	/* first call -> do a first pass */
339*99a2dd95SBruce Richardson 	if (*state <= 0) {
340*99a2dd95SBruce Richardson 		debug_printf("try complete <%s>\n", buf);
341*99a2dd95SBruce Richardson 		debug_printf("there is %d complete tokens, <%s> is incomplete\n",
342*99a2dd95SBruce Richardson 			     nb_token, partial_tok);
343*99a2dd95SBruce Richardson 
344*99a2dd95SBruce Richardson 		nb_completable = 0;
345*99a2dd95SBruce Richardson 		nb_non_completable = 0;
346*99a2dd95SBruce Richardson 
347*99a2dd95SBruce Richardson 		inst = ctx[inst_num];
348*99a2dd95SBruce Richardson 		while (inst) {
349*99a2dd95SBruce Richardson 			/* parse the first tokens of the inst */
350*99a2dd95SBruce Richardson 			if (nb_token &&
351*99a2dd95SBruce Richardson 			    match_inst(inst, buf, nb_token, NULL, 0))
352*99a2dd95SBruce Richardson 				goto next;
353*99a2dd95SBruce Richardson 
354*99a2dd95SBruce Richardson 			debug_printf("instruction match\n");
355*99a2dd95SBruce Richardson 			token_p = get_token(inst, nb_token);
356*99a2dd95SBruce Richardson 			if (token_p)
357*99a2dd95SBruce Richardson 				memcpy(&token_hdr, token_p, sizeof(token_hdr));
358*99a2dd95SBruce Richardson 
359*99a2dd95SBruce Richardson 			/* non completable */
360*99a2dd95SBruce Richardson 			if (!token_p ||
361*99a2dd95SBruce Richardson 			    !token_hdr.ops->complete_get_nb ||
362*99a2dd95SBruce Richardson 			    !token_hdr.ops->complete_get_elt ||
363*99a2dd95SBruce Richardson 			    (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
364*99a2dd95SBruce Richardson 				nb_non_completable++;
365*99a2dd95SBruce Richardson 				goto next;
366*99a2dd95SBruce Richardson 			}
367*99a2dd95SBruce Richardson 
368*99a2dd95SBruce Richardson 			debug_printf("%d choices for this token\n", n);
369*99a2dd95SBruce Richardson 			for (i=0 ; i<n ; i++) {
370*99a2dd95SBruce Richardson 				if (token_hdr.ops->complete_get_elt(token_p, i,
371*99a2dd95SBruce Richardson 								    tmpbuf,
372*99a2dd95SBruce Richardson 								    sizeof(tmpbuf)) < 0)
373*99a2dd95SBruce Richardson 					continue;
374*99a2dd95SBruce Richardson 
375*99a2dd95SBruce Richardson 				/* we have at least room for one char */
376*99a2dd95SBruce Richardson 				tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
377*99a2dd95SBruce Richardson 				if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
378*99a2dd95SBruce Richardson 					tmpbuf[tmp_len] = ' ';
379*99a2dd95SBruce Richardson 					tmpbuf[tmp_len+1] = 0;
380*99a2dd95SBruce Richardson 				}
381*99a2dd95SBruce Richardson 
382*99a2dd95SBruce Richardson 				debug_printf("   choice <%s>\n", tmpbuf);
383*99a2dd95SBruce Richardson 
384*99a2dd95SBruce Richardson 				/* does the completion match the
385*99a2dd95SBruce Richardson 				 * beginning of the word ? */
386*99a2dd95SBruce Richardson 				if (!strncmp(partial_tok, tmpbuf,
387*99a2dd95SBruce Richardson 					     partial_tok_len)) {
388*99a2dd95SBruce Richardson 					if (comp_len == -1) {
389*99a2dd95SBruce Richardson 						strlcpy(comp_buf,
390*99a2dd95SBruce Richardson 							tmpbuf + partial_tok_len,
391*99a2dd95SBruce Richardson 							sizeof(comp_buf));
392*99a2dd95SBruce Richardson 						comp_len =
393*99a2dd95SBruce Richardson 							strnlen(tmpbuf + partial_tok_len,
394*99a2dd95SBruce Richardson 									sizeof(tmpbuf) - partial_tok_len);
395*99a2dd95SBruce Richardson 
396*99a2dd95SBruce Richardson 					}
397*99a2dd95SBruce Richardson 					else {
398*99a2dd95SBruce Richardson 						comp_len =
399*99a2dd95SBruce Richardson 							nb_common_chars(comp_buf,
400*99a2dd95SBruce Richardson 									tmpbuf+partial_tok_len);
401*99a2dd95SBruce Richardson 						comp_buf[comp_len] = 0;
402*99a2dd95SBruce Richardson 					}
403*99a2dd95SBruce Richardson 					nb_completable++;
404*99a2dd95SBruce Richardson 				}
405*99a2dd95SBruce Richardson 			}
406*99a2dd95SBruce Richardson 		next:
407*99a2dd95SBruce Richardson 			debug_printf("next\n");
408*99a2dd95SBruce Richardson 			inst_num ++;
409*99a2dd95SBruce Richardson 			inst = ctx[inst_num];
410*99a2dd95SBruce Richardson 		}
411*99a2dd95SBruce Richardson 
412*99a2dd95SBruce Richardson 		debug_printf("total choices %d for this completion\n",
413*99a2dd95SBruce Richardson 			     nb_completable);
414*99a2dd95SBruce Richardson 
415*99a2dd95SBruce Richardson 		/* no possible completion */
416*99a2dd95SBruce Richardson 		if (nb_completable == 0 && nb_non_completable == 0)
417*99a2dd95SBruce Richardson 			return 0;
418*99a2dd95SBruce Richardson 
419*99a2dd95SBruce Richardson 		/* if multichoice is not required */
420*99a2dd95SBruce Richardson 		if (*state == 0 && partial_tok_len > 0) {
421*99a2dd95SBruce Richardson 			/* one or several choices starting with the
422*99a2dd95SBruce Richardson 			   same chars */
423*99a2dd95SBruce Richardson 			if (comp_len > 0) {
424*99a2dd95SBruce Richardson 				if ((unsigned)(comp_len + 1) > size)
425*99a2dd95SBruce Richardson 					return 0;
426*99a2dd95SBruce Richardson 
427*99a2dd95SBruce Richardson 				strlcpy(dst, comp_buf, size);
428*99a2dd95SBruce Richardson 				dst[comp_len] = 0;
429*99a2dd95SBruce Richardson 				return 2;
430*99a2dd95SBruce Richardson 			}
431*99a2dd95SBruce Richardson 		}
432*99a2dd95SBruce Richardson 	}
433*99a2dd95SBruce Richardson 
434*99a2dd95SBruce Richardson 	/* init state correctly */
435*99a2dd95SBruce Richardson 	if (*state == -1)
436*99a2dd95SBruce Richardson 		*state = 0;
437*99a2dd95SBruce Richardson 
438*99a2dd95SBruce Richardson 	debug_printf("Multiple choice STATE=%d\n", *state);
439*99a2dd95SBruce Richardson 
440*99a2dd95SBruce Richardson 	inst_num = 0;
441*99a2dd95SBruce Richardson 	inst = ctx[inst_num];
442*99a2dd95SBruce Richardson 	while (inst) {
443*99a2dd95SBruce Richardson 		/* we need to redo it */
444*99a2dd95SBruce Richardson 		inst = ctx[inst_num];
445*99a2dd95SBruce Richardson 
446*99a2dd95SBruce Richardson 		if (nb_token &&
447*99a2dd95SBruce Richardson 		    match_inst(inst, buf, nb_token, NULL, 0))
448*99a2dd95SBruce Richardson 			goto next2;
449*99a2dd95SBruce Richardson 
450*99a2dd95SBruce Richardson 		token_p = get_token(inst, nb_token);
451*99a2dd95SBruce Richardson 		if (token_p)
452*99a2dd95SBruce Richardson 			memcpy(&token_hdr, token_p, sizeof(token_hdr));
453*99a2dd95SBruce Richardson 
454*99a2dd95SBruce Richardson 		/* one choice for this token */
455*99a2dd95SBruce Richardson 		if (!token_p ||
456*99a2dd95SBruce Richardson 		    !token_hdr.ops->complete_get_nb ||
457*99a2dd95SBruce Richardson 		    !token_hdr.ops->complete_get_elt ||
458*99a2dd95SBruce Richardson 		    (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
459*99a2dd95SBruce Richardson 			if (local_state < *state) {
460*99a2dd95SBruce Richardson 				local_state++;
461*99a2dd95SBruce Richardson 				goto next2;
462*99a2dd95SBruce Richardson 			}
463*99a2dd95SBruce Richardson 			(*state)++;
464*99a2dd95SBruce Richardson 			if (token_p && token_hdr.ops->get_help) {
465*99a2dd95SBruce Richardson 				token_hdr.ops->get_help(token_p, tmpbuf,
466*99a2dd95SBruce Richardson 							sizeof(tmpbuf));
467*99a2dd95SBruce Richardson 				help_str = inst->help_str;
468*99a2dd95SBruce Richardson 				if (help_str)
469*99a2dd95SBruce Richardson 					snprintf(dst, size, "[%s]: %s", tmpbuf,
470*99a2dd95SBruce Richardson 						 help_str);
471*99a2dd95SBruce Richardson 				else
472*99a2dd95SBruce Richardson 					snprintf(dst, size, "[%s]: No help",
473*99a2dd95SBruce Richardson 						 tmpbuf);
474*99a2dd95SBruce Richardson 			}
475*99a2dd95SBruce Richardson 			else {
476*99a2dd95SBruce Richardson 				snprintf(dst, size, "[RETURN]");
477*99a2dd95SBruce Richardson 			}
478*99a2dd95SBruce Richardson 			return 1;
479*99a2dd95SBruce Richardson 		}
480*99a2dd95SBruce Richardson 
481*99a2dd95SBruce Richardson 		/* several choices */
482*99a2dd95SBruce Richardson 		for (i=0 ; i<n ; i++) {
483*99a2dd95SBruce Richardson 			if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf,
484*99a2dd95SBruce Richardson 							    sizeof(tmpbuf)) < 0)
485*99a2dd95SBruce Richardson 				continue;
486*99a2dd95SBruce Richardson 			/* we have at least room for one char */
487*99a2dd95SBruce Richardson 			tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
488*99a2dd95SBruce Richardson 			if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
489*99a2dd95SBruce Richardson 				tmpbuf[tmp_len] = ' ';
490*99a2dd95SBruce Richardson 				tmpbuf[tmp_len + 1] = 0;
491*99a2dd95SBruce Richardson 			}
492*99a2dd95SBruce Richardson 
493*99a2dd95SBruce Richardson 			debug_printf("   choice <%s>\n", tmpbuf);
494*99a2dd95SBruce Richardson 
495*99a2dd95SBruce Richardson 			/* does the completion match the beginning of
496*99a2dd95SBruce Richardson 			 * the word ? */
497*99a2dd95SBruce Richardson 			if (!strncmp(partial_tok, tmpbuf,
498*99a2dd95SBruce Richardson 				     partial_tok_len)) {
499*99a2dd95SBruce Richardson 				if (local_state < *state) {
500*99a2dd95SBruce Richardson 					local_state++;
501*99a2dd95SBruce Richardson 					continue;
502*99a2dd95SBruce Richardson 				}
503*99a2dd95SBruce Richardson 				(*state)++;
504*99a2dd95SBruce Richardson 				l=strlcpy(dst, tmpbuf, size);
505*99a2dd95SBruce Richardson 				if (l>=0 && token_hdr.ops->get_help) {
506*99a2dd95SBruce Richardson 					token_hdr.ops->get_help(token_p, tmpbuf,
507*99a2dd95SBruce Richardson 								sizeof(tmpbuf));
508*99a2dd95SBruce Richardson 					help_str = inst->help_str;
509*99a2dd95SBruce Richardson 					if (help_str)
510*99a2dd95SBruce Richardson 						snprintf(dst+l, size-l, "[%s]: %s",
511*99a2dd95SBruce Richardson 							 tmpbuf, help_str);
512*99a2dd95SBruce Richardson 					else
513*99a2dd95SBruce Richardson 						snprintf(dst+l, size-l,
514*99a2dd95SBruce Richardson 							 "[%s]: No help", tmpbuf);
515*99a2dd95SBruce Richardson 				}
516*99a2dd95SBruce Richardson 
517*99a2dd95SBruce Richardson 				return 1;
518*99a2dd95SBruce Richardson 			}
519*99a2dd95SBruce Richardson 		}
520*99a2dd95SBruce Richardson 	next2:
521*99a2dd95SBruce Richardson 		inst_num ++;
522*99a2dd95SBruce Richardson 		inst = ctx[inst_num];
523*99a2dd95SBruce Richardson 	}
524*99a2dd95SBruce Richardson 	return 0;
525*99a2dd95SBruce Richardson }
526