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