197f17497SC.J. Collier/*-
297f17497SC.J. Collier *   BSD LICENSE
397f17497SC.J. Collier *
497f17497SC.J. Collier *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
597f17497SC.J. Collier *   All rights reserved.
697f17497SC.J. Collier *
797f17497SC.J. Collier *   Redistribution and use in source and binary forms, with or without
897f17497SC.J. Collier *   modification, are permitted provided that the following conditions
997f17497SC.J. Collier *   are met:
1097f17497SC.J. Collier *
1197f17497SC.J. Collier *     * Redistributions of source code must retain the above copyright
1297f17497SC.J. Collier *       notice, this list of conditions and the following disclaimer.
1397f17497SC.J. Collier *     * Redistributions in binary form must reproduce the above copyright
1497f17497SC.J. Collier *       notice, this list of conditions and the following disclaimer in
1597f17497SC.J. Collier *       the documentation and/or other materials provided with the
1697f17497SC.J. Collier *       distribution.
1797f17497SC.J. Collier *     * Neither the name of Intel Corporation nor the names of its
1897f17497SC.J. Collier *       contributors may be used to endorse or promote products derived
1997f17497SC.J. Collier *       from this software without specific prior written permission.
2097f17497SC.J. Collier *
2197f17497SC.J. Collier *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2297f17497SC.J. Collier *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2397f17497SC.J. Collier *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2497f17497SC.J. Collier *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2597f17497SC.J. Collier *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2697f17497SC.J. Collier *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2797f17497SC.J. Collier *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2897f17497SC.J. Collier *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2997f17497SC.J. Collier *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3097f17497SC.J. Collier *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3197f17497SC.J. Collier *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3297f17497SC.J. Collier */
3397f17497SC.J. Collier
3497f17497SC.J. Collier/*
3597f17497SC.J. Collier * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
3697f17497SC.J. Collier * All rights reserved.
3797f17497SC.J. Collier * Redistribution and use in source and binary forms, with or without
3897f17497SC.J. Collier * modification, are permitted provided that the following conditions are met:
3997f17497SC.J. Collier *
4097f17497SC.J. Collier *     * Redistributions of source code must retain the above copyright
4197f17497SC.J. Collier *       notice, this list of conditions and the following disclaimer.
4297f17497SC.J. Collier *     * Redistributions in binary form must reproduce the above copyright
4397f17497SC.J. Collier *       notice, this list of conditions and the following disclaimer in the
4497f17497SC.J. Collier *       documentation and/or other materials provided with the distribution.
4597f17497SC.J. Collier *     * Neither the name of the University of California, Berkeley nor the
4697f17497SC.J. Collier *       names of its contributors may be used to endorse or promote products
4797f17497SC.J. Collier *       derived from this software without specific prior written permission.
4897f17497SC.J. Collier *
4997f17497SC.J. Collier * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
5097f17497SC.J. Collier * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
5197f17497SC.J. Collier * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
5297f17497SC.J. Collier * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
5397f17497SC.J. Collier * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
5497f17497SC.J. Collier * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
5597f17497SC.J. Collier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
5697f17497SC.J. Collier * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5797f17497SC.J. Collier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
5897f17497SC.J. Collier * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5997f17497SC.J. Collier */
6097f17497SC.J. Collier
6197f17497SC.J. Collier#include <stdio.h>
6297f17497SC.J. Collier#include <stdarg.h>
6397f17497SC.J. Collier#include <errno.h>
6497f17497SC.J. Collier#include <string.h>
6597f17497SC.J. Collier#include <inttypes.h>
6697f17497SC.J. Collier#include <ctype.h>
6797f17497SC.J. Collier#include <termios.h>
6897f17497SC.J. Collier
6997f17497SC.J. Collier#include <netinet/in.h>
7097f17497SC.J. Collier
7197f17497SC.J. Collier#include <rte_string_fns.h>
7297f17497SC.J. Collier
7397f17497SC.J. Collier#include "cmdline_rdline.h"
7497f17497SC.J. Collier#include "cmdline_parse.h"
7597f17497SC.J. Collier#include "cmdline.h"
7697f17497SC.J. Collier
7797f17497SC.J. Collier#ifdef RTE_LIBRTE_CMDLINE_DEBUG
7897f17497SC.J. Collier#define debug_printf printf
7997f17497SC.J. Collier#else
8097f17497SC.J. Collier#define debug_printf(args...) do {} while(0)
8197f17497SC.J. Collier#endif
8297f17497SC.J. Collier
8397f17497SC.J. Collier#define CMDLINE_BUFFER_SIZE 64
8497f17497SC.J. Collier
8597f17497SC.J. Collier/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
8697f17497SC.J. Collier * own. */
8797f17497SC.J. Collierstatic int
8897f17497SC.J. Collierisblank2(char c)
8997f17497SC.J. Collier{
9097f17497SC.J. Collier	if (c == ' ' ||
9197f17497SC.J. Collier	    c == '\t' )
9297f17497SC.J. Collier		return 1;
9397f17497SC.J. Collier	return 0;
9497f17497SC.J. Collier}
9597f17497SC.J. Collier
9697f17497SC.J. Collierstatic int
9797f17497SC.J. Collierisendofline(char c)
9897f17497SC.J. Collier{
9997f17497SC.J. Collier	if (c == '\n' ||
10097f17497SC.J. Collier	    c == '\r' )
10197f17497SC.J. Collier		return 1;
10297f17497SC.J. Collier	return 0;
10397f17497SC.J. Collier}
10497f17497SC.J. Collier
10597f17497SC.J. Collierstatic int
10697f17497SC.J. Collieriscomment(char c)
10797f17497SC.J. Collier{
10897f17497SC.J. Collier	if (c == '#')
10997f17497SC.J. Collier		return 1;
11097f17497SC.J. Collier	return 0;
11197f17497SC.J. Collier}
11297f17497SC.J. Collier
11397f17497SC.J. Collierint
11497f17497SC.J. Colliercmdline_isendoftoken(char c)
11597f17497SC.J. Collier{
11697f17497SC.J. Collier	if (!c || iscomment(c) || isblank2(c) || isendofline(c))
11797f17497SC.J. Collier		return 1;
11897f17497SC.J. Collier	return 0;
11997f17497SC.J. Collier}
12097f17497SC.J. Collier
1218b25d1adSChristian Ehrhardtint
1228b25d1adSChristian Ehrhardtcmdline_isendofcommand(char c)
1238b25d1adSChristian Ehrhardt{
1248b25d1adSChristian Ehrhardt	if (!c || iscomment(c) || isendofline(c))
1258b25d1adSChristian Ehrhardt		return 1;
1268b25d1adSChristian Ehrhardt	return 0;
1278b25d1adSChristian Ehrhardt}
1288b25d1adSChristian Ehrhardt
12997f17497SC.J. Collierstatic unsigned int
13097f17497SC.J. Colliernb_common_chars(const char * s1, const char * s2)
13197f17497SC.J. Collier{
13297f17497SC.J. Collier	unsigned int i=0;
13397f17497SC.J. Collier
13497f17497SC.J. Collier	while (*s1==*s2 && *s1) {
13597f17497SC.J. Collier		s1++;
13697f17497SC.J. Collier		s2++;
13797f17497SC.J. Collier		i++;
13897f17497SC.J. Collier	}
13997f17497SC.J. Collier	return i;
14097f17497SC.J. Collier}
14197f17497SC.J. Collier
14297f17497SC.J. Collier/**
14397f17497SC.J. Collier * try to match the buffer with an instruction (only the first
14497f17497SC.J. Collier * nb_match_token tokens if != 0). Return 0 if we match all the
14597f17497SC.J. Collier * tokens, else the number of matched tokens, else -1.
14697f17497SC.J. Collier */
14797f17497SC.J. Collierstatic int
14897f17497SC.J. Colliermatch_inst(cmdline_parse_inst_t *inst, const char *buf,
14997f17497SC.J. Collier	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
15097f17497SC.J. Collier{
15197f17497SC.J. Collier	unsigned int token_num=0;
15247d9763aSLuca Boccassi	cmdline_parse_token_hdr_t *token_p = NULL;
15397f17497SC.J. Collier	unsigned int i=0;
15497f17497SC.J. Collier	int n = 0;
15597f17497SC.J. Collier	struct cmdline_token_hdr token_hdr;
15697f17497SC.J. Collier
15797f17497SC.J. Collier	token_p = inst->tokens[token_num];
15897f17497SC.J. Collier	if (token_p)
15997f17497SC.J. Collier		memcpy(&token_hdr, token_p, sizeof(token_hdr));
16097f17497SC.J. Collier
16197f17497SC.J. Collier	/* check if we match all tokens of inst */
16297f17497SC.J. Collier	while (token_p && (!nb_match_token || i<nb_match_token)) {
16397f17497SC.J. Collier		debug_printf("TK\n");
16497f17497SC.J. Collier		/* skip spaces */
16597f17497SC.J. Collier		while (isblank2(*buf)) {
16697f17497SC.J. Collier			buf++;
16797f17497SC.J. Collier		}
16897f17497SC.J. Collier
16997f17497SC.J. Collier		/* end of buf */
17097f17497SC.J. Collier		if ( isendofline(*buf) || iscomment(*buf) )
17197f17497SC.J. Collier			break;
17297f17497SC.J. Collier
17397f17497SC.J. Collier		if (resbuf == NULL) {
17497f17497SC.J. Collier			n = token_hdr.ops->parse(token_p, buf, NULL, 0);
17597f17497SC.J. Collier		} else {
17697f17497SC.J. Collier			unsigned rb_sz;
17797f17497SC.J. Collier
17897f17497SC.J. Collier			if (token_hdr.offset > resbuf_size) {
17997f17497SC.J. Collier				printf("Parse error(%s:%d): Token offset(%u) "
18097f17497SC.J. Collier					"exceeds maximum size(%u)\n",
18197f17497SC.J. Collier					__FILE__, __LINE__,
18297f17497SC.J. Collier					token_hdr.offset, resbuf_size);
18397f17497SC.J. Collier				return -ENOBUFS;
18497f17497SC.J. Collier			}
18597f17497SC.J. Collier			rb_sz = resbuf_size - token_hdr.offset;
18697f17497SC.J. Collier
18797f17497SC.J. Collier			n = token_hdr.ops->parse(token_p, buf, (char *)resbuf +
18897f17497SC.J. Collier				token_hdr.offset, rb_sz);
18997f17497SC.J. Collier		}
19097f17497SC.J. Collier
19197f17497SC.J. Collier		if (n < 0)
19297f17497SC.J. Collier			break;
19397f17497SC.J. Collier
19497f17497SC.J. Collier		debug_printf("TK parsed (len=%d)\n", n);
19597f17497SC.J. Collier		i++;
19697f17497SC.J. Collier		buf += n;
19797f17497SC.J. Collier
19897f17497SC.J. Collier		token_num ++;
19997f17497SC.J. Collier		token_p = inst->tokens[token_num];
20097f17497SC.J. Collier		if (token_p)
20197f17497SC.J. Collier			memcpy(&token_hdr, token_p, sizeof(token_hdr));
20297f17497SC.J. Collier	}
20397f17497SC.J. Collier
20497f17497SC.J. Collier	/* does not match */
20597f17497SC.J. Collier	if (i==0)
20697f17497SC.J. Collier		return -1;
20797f17497SC.J. Collier
20897f17497SC.J. Collier	/* in case we want to match a specific num of token */
20997f17497SC.J. Collier	if (nb_match_token) {
21097f17497SC.J. Collier		if (i == nb_match_token) {
21197f17497SC.J. Collier			return 0;
21297f17497SC.J. Collier		}
21397f17497SC.J. Collier		return i;
21497f17497SC.J. Collier	}
21597f17497SC.J. Collier
21697f17497SC.J. Collier	/* we don't match all the tokens */
21797f17497SC.J. Collier	if (token_p) {
21897f17497SC.J. Collier		return i;
21997f17497SC.J. Collier	}
22097f17497SC.J. Collier
22197f17497SC.J. Collier	/* are there are some tokens more */
22297f17497SC.J. Collier	while (isblank2(*buf)) {
22397f17497SC.J. Collier		buf++;
22497f17497SC.J. Collier	}
22597f17497SC.J. Collier
22697f17497SC.J. Collier	/* end of buf */
22797f17497SC.J. Collier	if ( isendofline(*buf) || iscomment(*buf) )
22897f17497SC.J. Collier		return 0;
22997f17497SC.J. Collier
23097f17497SC.J. Collier	/* garbage after inst */
23197f17497SC.J. Collier	return i;
23297f17497SC.J. Collier}
23397f17497SC.J. Collier
23497f17497SC.J. Collier
23597f17497SC.J. Collierint
23697f17497SC.J. Colliercmdline_parse(struct cmdline *cl, const char * buf)
23797f17497SC.J. Collier{
23897f17497SC.J. Collier	unsigned int inst_num=0;
23997f17497SC.J. Collier	cmdline_parse_inst_t *inst;
24097f17497SC.J. Collier	const char *curbuf;
24197f17497SC.J. Collier	char result_buf[CMDLINE_PARSE_RESULT_BUFSIZE];
24297f17497SC.J. Collier	void (*f)(void *, struct cmdline *, void *) = NULL;
24397f17497SC.J. Collier	void *data = NULL;
24497f17497SC.J. Collier	int comment = 0;
24597f17497SC.J. Collier	int linelen = 0;
24697f17497SC.J. Collier	int parse_it = 0;
24797f17497SC.J. Collier	int err = CMDLINE_PARSE_NOMATCH;
24897f17497SC.J. Collier	int tok;
24997f17497SC.J. Collier	cmdline_parse_ctx_t *ctx;
25097f17497SC.J. Collier#ifdef RTE_LIBRTE_CMDLINE_DEBUG
25197f17497SC.J. Collier	char debug_buf[BUFSIZ];
25297f17497SC.J. Collier#endif
25397f17497SC.J. Collier
25497f17497SC.J. Collier	if (!cl || !buf)
25597f17497SC.J. Collier		return CMDLINE_PARSE_BAD_ARGS;
25697f17497SC.J. Collier
25797f17497SC.J. Collier	ctx = cl->ctx;
25897f17497SC.J. Collier
25997f17497SC.J. Collier	/*
26097f17497SC.J. Collier	 * - look if the buffer contains at least one line
26197f17497SC.J. Collier	 * - look if line contains only spaces or comments
26297f17497SC.J. Collier	 * - count line length
26397f17497SC.J. Collier	 */
26497f17497SC.J. Collier	curbuf = buf;
26597f17497SC.J. Collier	while (! isendofline(*curbuf)) {
26697f17497SC.J. Collier		if ( *curbuf == '\0' ) {
26797f17497SC.J. Collier			debug_printf("Incomplete buf (len=%d)\n", linelen);
26897f17497SC.J. Collier			return 0;
26997f17497SC.J. Collier		}
27097f17497SC.J. Collier		if ( iscomment(*curbuf) ) {
27197f17497SC.J. Collier			comment = 1;
27297f17497SC.J. Collier		}
27397f17497SC.J. Collier		if ( ! isblank2(*curbuf) && ! comment) {
27497f17497SC.J. Collier			parse_it = 1;
27597f17497SC.J. Collier		}
27697f17497SC.J. Collier		curbuf++;
27797f17497SC.J. Collier		linelen++;
27897f17497SC.J. Collier	}
27997f17497SC.J. Collier
28097f17497SC.J. Collier	/* skip all endofline chars */
28197f17497SC.J. Collier	while (isendofline(buf[linelen])) {
28297f17497SC.J. Collier		linelen++;
28397f17497SC.J. Collier	}
28497f17497SC.J. Collier
28597f17497SC.J. Collier	/* empty line */
28697f17497SC.J. Collier	if ( parse_it == 0 ) {
28797f17497SC.J. Collier		debug_printf("Empty line (len=%d)\n", linelen);
28897f17497SC.J. Collier		return linelen;
28997f17497SC.J. Collier	}
29097f17497SC.J. Collier
29197f17497SC.J. Collier#ifdef RTE_LIBRTE_CMDLINE_DEBUG
29297f17497SC.J. Collier	snprintf(debug_buf, (linelen>64 ? 64 : linelen), "%s", buf);
29397f17497SC.J. Collier	debug_printf("Parse line : len=%d, <%s>\n", linelen, debug_buf);
29497f17497SC.J. Collier#endif
29597f17497SC.J. Collier
29697f17497SC.J. Collier	/* parse it !! */
29797f17497SC.J. Collier	inst = ctx[inst_num];
29897f17497SC.J. Collier	while (inst) {
29997f17497SC.J. Collier		debug_printf("INST %d\n", inst_num);
30097f17497SC.J. Collier
30197f17497SC.J. Collier		/* fully parsed */
30297f17497SC.J. Collier		tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf));
30397f17497SC.J. Collier
30497f17497SC.J. Collier		if (tok > 0) /* we matched at least one token */
30597f17497SC.J. Collier			err = CMDLINE_PARSE_BAD_ARGS;
30697f17497SC.J. Collier
30797f17497SC.J. Collier		else if (!tok) {
30897f17497SC.J. Collier			debug_printf("INST fully parsed\n");
30997f17497SC.J. Collier			/* skip spaces */
31097f17497SC.J. Collier			while (isblank2(*curbuf)) {
31197f17497SC.J. Collier				curbuf++;
31297f17497SC.J. Collier			}
31397f17497SC.J. Collier
31497f17497SC.J. Collier			/* if end of buf -> there is no garbage after inst */
31597f17497SC.J. Collier			if (isendofline(*curbuf) || iscomment(*curbuf)) {
31697f17497SC.J. Collier				if (!f) {
31797f17497SC.J. Collier					memcpy(&f, &inst->f, sizeof(f));
31897f17497SC.J. Collier					memcpy(&data, &inst->data, sizeof(data));
31997f17497SC.J. Collier				}
32097f17497SC.J. Collier				else {
32197f17497SC.J. Collier					/* more than 1 inst matches */
32297f17497SC.J. Collier					err = CMDLINE_PARSE_AMBIGUOUS;
32397f17497SC.J. Collier					f=NULL;
32497f17497SC.J. Collier					debug_printf("Ambiguous cmd\n");
32597f17497SC.J. Collier					break;
32697f17497SC.J. Collier				}
32797f17497SC.J. Collier			}
32897f17497SC.J. Collier		}
32997f17497SC.J. Collier
33097f17497SC.J. Collier		inst_num ++;
33197f17497SC.J. Collier		inst = ctx[inst_num];
33297f17497SC.J. Collier	}
33397f17497SC.J. Collier
33497f17497SC.J. Collier	/* call func */
33597f17497SC.J. Collier	if (f) {
33697f17497SC.J. Collier		f(result_buf, cl, data);
33797f17497SC.J. Collier	}
33897f17497SC.J. Collier
33997f17497SC.J. Collier	/* no match */
34097f17497SC.J. Collier	else {
34197f17497SC.J. Collier		debug_printf("No match err=%d\n", err);
34297f17497SC.J. Collier		return err;
34397f17497SC.J. Collier	}
34497f17497SC.J. Collier
34597f17497SC.J. Collier	return linelen;
34697f17497SC.J. Collier}
34797f17497SC.J. Collier
34897f17497SC.J. Collierint
34997f17497SC.J. Colliercmdline_complete(struct cmdline *cl, const char *buf, int *state,
35097f17497SC.J. Collier		 char *dst, unsigned int size)
35197f17497SC.J. Collier{
35297f17497SC.J. Collier	const char *partial_tok = buf;
35397f17497SC.J. Collier	unsigned int inst_num = 0;
35497f17497SC.J. Collier	cmdline_parse_inst_t *inst;
35597f17497SC.J. Collier	cmdline_parse_token_hdr_t *token_p;
35697f17497SC.J. Collier	struct cmdline_token_hdr token_hdr;
35797f17497SC.J. Collier	char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
35897f17497SC.J. Collier	unsigned int partial_tok_len;
35997f17497SC.J. Collier	int comp_len = -1;
36097f17497SC.J. Collier	int tmp_len = -1;
36197f17497SC.J. Collier	int nb_token = 0;
36297f17497SC.J. Collier	unsigned int i, n;
36397f17497SC.J. Collier	int l;
36497f17497SC.J. Collier	unsigned int nb_completable;
36597f17497SC.J. Collier	unsigned int nb_non_completable;
36697f17497SC.J. Collier	int local_state = 0;
36797f17497SC.J. Collier	const char *help_str;
36897f17497SC.J. Collier	cmdline_parse_ctx_t *ctx;
36997f17497SC.J. Collier
37097f17497SC.J. Collier	if (!cl || !buf || !state || !dst)
37197f17497SC.J. Collier		return -1;
37297f17497SC.J. Collier
37397f17497SC.J. Collier	ctx = cl->ctx;
37497f17497SC.J. Collier
37597f17497SC.J. Collier	debug_printf("%s called\n", __func__);
37697f17497SC.J. Collier	memset(&token_hdr, 0, sizeof(token_hdr));
37797f17497SC.J. Collier
37897f17497SC.J. Collier	/* count the number of complete token to parse */
37997f17497SC.J. Collier	for (i=0 ; buf[i] ; i++) {
38097f17497SC.J. Collier		if (!isblank2(buf[i]) && isblank2(buf[i+1]))
38197f17497SC.J. Collier			nb_token++;
38297f17497SC.J. Collier		if (isblank2(buf[i]) && !isblank2(buf[i+1]))
38397f17497SC.J. Collier			partial_tok = buf+i+1;
38497f17497SC.J. Collier	}
38597f17497SC.J. Collier	partial_tok_len = strnlen(partial_tok, RDLINE_BUF_SIZE);
38697f17497SC.J. Collier
38797f17497SC.J. Collier	/* first call -> do a first pass */
38897f17497SC.J. Collier	if (*state <= 0) {
38997f17497SC.J. Collier		debug_printf("try complete <%s>\n", buf);
39097f17497SC.J. Collier		debug_printf("there is %d complete tokens, <%s> is incomplete\n",
39197f17497SC.J. Collier			     nb_token, partial_tok);
39297f17497SC.J. Collier
39397f17497SC.J. Collier		nb_completable = 0;
39497f17497SC.J. Collier		nb_non_completable = 0;
39597f17497SC.J. Collier
39697f17497SC.J. Collier		inst = ctx[inst_num];
39797f17497SC.J. Collier		while (inst) {
39897f17497SC.J. Collier			/* parse the first tokens of the inst */
39997f17497SC.J. Collier			if (nb_token && match_inst(inst, buf, nb_token, NULL, 0))
40097f17497SC.J. Collier				goto next;
40197f17497SC.J. Collier
40297f17497SC.J. Collier			debug_printf("instruction match\n");
40397f17497SC.J. Collier			token_p = inst->tokens[nb_token];
40497f17497SC.J. Collier			if (token_p)
40597f17497SC.J. Collier				memcpy(&token_hdr, token_p, sizeof(token_hdr));
40697f17497SC.J. Collier
40797f17497SC.J. Collier			/* non completable */
40897f17497SC.J. Collier			if (!token_p ||
40997f17497SC.J. Collier			    !token_hdr.ops->complete_get_nb ||
41097f17497SC.J. Collier			    !token_hdr.ops->complete_get_elt ||
41197f17497SC.J. Collier			    (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
41297f17497SC.J. Collier				nb_non_completable++;
41397f17497SC.J. Collier				goto next;
41497f17497SC.J. Collier			}
41597f17497SC.J. Collier
41697f17497SC.J. Collier			debug_printf("%d choices for this token\n", n);
41797f17497SC.J. Collier			for (i=0 ; i<n ; i++) {
41897f17497SC.J. Collier				if (token_hdr.ops->complete_get_elt(token_p, i,
41997f17497SC.J. Collier								    tmpbuf,
42097f17497SC.J. Collier								    sizeof(tmpbuf)) < 0)
42197f17497SC.J. Collier					continue;
42297f17497SC.J. Collier
42397f17497SC.J. Collier				/* we have at least room for one char */
42497f17497SC.J. Collier				tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
42597f17497SC.J. Collier				if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
42697f17497SC.J. Collier					tmpbuf[tmp_len] = ' ';
42797f17497SC.J. Collier					tmpbuf[tmp_len+1] = 0;
42897f17497SC.J. Collier				}
42997f17497SC.J. Collier
43097f17497SC.J. Collier				debug_printf("   choice <%s>\n", tmpbuf);
43197f17497SC.J. Collier
43297f17497SC.J. Collier				/* does the completion match the
43397f17497SC.J. Collier				 * beginning of the word ? */
43497f17497SC.J. Collier				if (!strncmp(partial_tok, tmpbuf,
43597f17497SC.J. Collier					     partial_tok_len)) {
43697f17497SC.J. Collier					if (comp_len == -1) {
43797f17497SC.J. Collier						snprintf(comp_buf, sizeof(comp_buf),
43897f17497SC.J. Collier							 "%s", tmpbuf + partial_tok_len);
43997f17497SC.J. Collier						comp_len =
44097f17497SC.J. Collier							strnlen(tmpbuf + partial_tok_len,
44197f17497SC.J. Collier									sizeof(tmpbuf) - partial_tok_len);
44297f17497SC.J. Collier
44397f17497SC.J. Collier					}
44497f17497SC.J. Collier					else {
44597f17497SC.J. Collier						comp_len =
44697f17497SC.J. Collier							nb_common_chars(comp_buf,
44797f17497SC.J. Collier									tmpbuf+partial_tok_len);
44897f17497SC.J. Collier						comp_buf[comp_len] = 0;
44997f17497SC.J. Collier					}
45097f17497SC.J. Collier					nb_completable++;
45197f17497SC.J. Collier				}
45297f17497SC.J. Collier			}
45397f17497SC.J. Collier		next:
45497f17497SC.J. Collier			debug_printf("next\n");
45597f17497SC.J. Collier			inst_num ++;
45697f17497SC.J. Collier			inst = ctx[inst_num];
45797f17497SC.J. Collier		}
45897f17497SC.J. Collier
45997f17497SC.J. Collier		debug_printf("total choices %d for this completion\n",
46097f17497SC.J. Collier			     nb_completable);
46197f17497SC.J. Collier
46297f17497SC.J. Collier		/* no possible completion */
46397f17497SC.J. Collier		if (nb_completable == 0 && nb_non_completable == 0)
46497f17497SC.J. Collier			return 0;
46597f17497SC.J. Collier
46697f17497SC.J. Collier		/* if multichoice is not required */
46797f17497SC.J. Collier		if (*state == 0 && partial_tok_len > 0) {
46897f17497SC.J. Collier			/* one or several choices starting with the
46997f17497SC.J. Collier			   same chars */
47097f17497SC.J. Collier			if (comp_len > 0) {
47197f17497SC.J. Collier				if ((unsigned)(comp_len + 1) > size)
47297f17497SC.J. Collier					return 0;
47397f17497SC.J. Collier
47497f17497SC.J. Collier				snprintf(dst, size, "%s", comp_buf);
47597f17497SC.J. Collier				dst[comp_len] = 0;
47697f17497SC.J. Collier				return 2;
47797f17497SC.J. Collier			}
47897f17497SC.J. Collier		}
47997f17497SC.J. Collier	}
48097f17497SC.J. Collier
48197f17497SC.J. Collier	/* init state correctly */
48297f17497SC.J. Collier	if (*state == -1)
48397f17497SC.J. Collier		*state = 0;
48497f17497SC.J. Collier
48597f17497SC.J. Collier	debug_printf("Multiple choice STATE=%d\n", *state);
48697f17497SC.J. Collier
48797f17497SC.J. Collier	inst_num = 0;
48897f17497SC.J. Collier	inst = ctx[inst_num];
48997f17497SC.J. Collier	while (inst) {
49097f17497SC.J. Collier		/* we need to redo it */
49197f17497SC.J. Collier		inst = ctx[inst_num];
49297f17497SC.J. Collier
49397f17497SC.J. Collier		if (nb_token && match_inst(inst, buf, nb_token, NULL, 0))
49497f17497SC.J. Collier			goto next2;
49597f17497SC.J. Collier
49697f17497SC.J. Collier		token_p = inst->tokens[nb_token];
49797f17497SC.J. Collier		if (token_p)
49897f17497SC.J. Collier			memcpy(&token_hdr, token_p, sizeof(token_hdr));
49997f17497SC.J. Collier
50097f17497SC.J. Collier		/* one choice for this token */
50197f17497SC.J. Collier		if (!token_p ||
50297f17497SC.J. Collier		    !token_hdr.ops->complete_get_nb ||
50397f17497SC.J. Collier		    !token_hdr.ops->complete_get_elt ||
50497f17497SC.J. Collier		    (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
50597f17497SC.J. Collier			if (local_state < *state) {
50697f17497SC.J. Collier				local_state++;
50797f17497SC.J. Collier				goto next2;
50897f17497SC.J. Collier			}
50997f17497SC.J. Collier			(*state)++;
51097f17497SC.J. Collier			if (token_p && token_hdr.ops->get_help) {
51197f17497SC.J. Collier				token_hdr.ops->get_help(token_p, tmpbuf,
51297f17497SC.J. Collier							sizeof(tmpbuf));
51397f17497SC.J. Collier				help_str = inst->help_str;
51497f17497SC.J. Collier				if (help_str)
51597f17497SC.J. Collier					snprintf(dst, size, "[%s]: %s", tmpbuf,
51697f17497SC.J. Collier						 help_str);
51797f17497SC.J. Collier				else
51897f17497SC.J. Collier					snprintf(dst, size, "[%s]: No help",
51997f17497SC.J. Collier						 tmpbuf);
52097f17497SC.J. Collier			}
52197f17497SC.J. Collier			else {
52297f17497SC.J. Collier				snprintf(dst, size, "[RETURN]");
52397f17497SC.J. Collier			}
52497f17497SC.J. Collier			return 1;
52597f17497SC.J. Collier		}
52697f17497SC.J. Collier
52797f17497SC.J. Collier		/* several choices */
52897f17497SC.J. Collier		for (i=0 ; i<n ; i++) {
52997f17497SC.J. Collier			if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf,
53097f17497SC.J. Collier							    sizeof(tmpbuf)) < 0)
53197f17497SC.J. Collier				continue;
53297f17497SC.J. Collier			/* we have at least room for one char */
53397f17497SC.J. Collier			tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
53497f17497SC.J. Collier			if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
53597f17497SC.J. Collier				tmpbuf[tmp_len] = ' ';
53697f17497SC.J. Collier				tmpbuf[tmp_len + 1] = 0;
53797f17497SC.J. Collier			}
53897f17497SC.J. Collier
53997f17497SC.J. Collier			debug_printf("   choice <%s>\n", tmpbuf);
54097f17497SC.J. Collier
54197f17497SC.J. Collier			/* does the completion match the beginning of
54297f17497SC.J. Collier			 * the word ? */
54397f17497SC.J. Collier			if (!strncmp(partial_tok, tmpbuf,
54497f17497SC.J. Collier				     partial_tok_len)) {
54597f17497SC.J. Collier				if (local_state < *state) {
54697f17497SC.J. Collier					local_state++;
54797f17497SC.J. Collier					continue;
54897f17497SC.J. Collier				}
54997f17497SC.J. Collier				(*state)++;
55097f17497SC.J. Collier				l=snprintf(dst, size, "%s", tmpbuf);
55197f17497SC.J. Collier				if (l>=0 && token_hdr.ops->get_help) {
55297f17497SC.J. Collier					token_hdr.ops->get_help(token_p, tmpbuf,
55397f17497SC.J. Collier								sizeof(tmpbuf));
55497f17497SC.J. Collier					help_str = inst->help_str;
55597f17497SC.J. Collier					if (help_str)
55697f17497SC.J. Collier						snprintf(dst+l, size-l, "[%s]: %s",
55797f17497SC.J. Collier							 tmpbuf, help_str);
55897f17497SC.J. Collier					else
55997f17497SC.J. Collier						snprintf(dst+l, size-l,
56097f17497SC.J. Collier							 "[%s]: No help", tmpbuf);
56197f17497SC.J. Collier				}
56297f17497SC.J. Collier
56397f17497SC.J. Collier				return 1;
56497f17497SC.J. Collier			}
56597f17497SC.J. Collier		}
56697f17497SC.J. Collier	next2:
56797f17497SC.J. Collier		inst_num ++;
56897f17497SC.J. Collier		inst = ctx[inst_num];
56997f17497SC.J. Collier	}
57097f17497SC.J. Collier	return 0;
57197f17497SC.J. Collier}
572