1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation.
3 * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
11 #include <rte_string_fns.h>
13 #include "cmdline_private.h"
15 #ifdef RTE_LIBRTE_CMDLINE_DEBUG
16 #define debug_printf printf
18 #define debug_printf(args...) do {} while(0)
21 #define CMDLINE_BUFFER_SIZE 64
23 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
52 cmdline_isendoftoken(char c)
54 if (!c || iscomment(c) || isblank2(c) || isendofline(c))
60 cmdline_isendofcommand(char c)
62 if (!c || iscomment(c) || isendofline(c))
68 nb_common_chars(const char * s1, const char * s2)
72 while (*s1==*s2 && *s1) {
80 /** Retrieve either static or dynamic token at a given index. */
81 static cmdline_parse_token_hdr_t *
82 get_token(cmdline_parse_inst_t *inst, unsigned int index)
84 cmdline_parse_token_hdr_t *token_p;
86 /* check presence of static tokens first */
87 if (inst->tokens[0] || !inst->f)
88 return inst->tokens[index];
89 /* generate dynamic token */
91 inst->f(&token_p, NULL, &inst->tokens[index]);
96 * try to match the buffer with an instruction (only the first
97 * nb_match_token tokens if != 0). Return 0 if we match all the
98 * tokens, else the number of matched tokens, else -1.
101 match_inst(cmdline_parse_inst_t *inst, const char *buf,
102 unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
104 cmdline_parse_token_hdr_t *token_p = NULL;
107 struct cmdline_token_hdr token_hdr;
110 memset(resbuf, 0, resbuf_size);
111 /* check if we match all tokens of inst */
112 while (!nb_match_token || i < nb_match_token) {
113 token_p = get_token(inst, i);
116 memcpy(&token_hdr, token_p, sizeof(token_hdr));
118 debug_printf("TK\n");
120 while (isblank2(*buf)) {
125 if ( isendofline(*buf) || iscomment(*buf) )
128 if (resbuf == NULL) {
129 n = token_hdr.ops->parse(token_p, buf, NULL, 0);
133 if (token_hdr.offset > resbuf_size) {
134 printf("Parse error(%s:%d): Token offset(%u) "
135 "exceeds maximum size(%u)\n",
137 token_hdr.offset, resbuf_size);
140 rb_sz = resbuf_size - token_hdr.offset;
142 n = token_hdr.ops->parse(token_p, buf, (char *)resbuf +
143 token_hdr.offset, rb_sz);
149 debug_printf("TK parsed (len=%d)\n", n);
158 /* in case we want to match a specific num of token */
159 if (nb_match_token) {
160 if (i == nb_match_token) {
166 /* we don't match all the tokens */
171 /* are there are some tokens more */
172 while (isblank2(*buf)) {
177 if ( isendofline(*buf) || iscomment(*buf) )
180 /* garbage after inst */
186 cmdline_parse(struct cmdline *cl, const char * buf)
188 unsigned int inst_num=0;
189 cmdline_parse_inst_t *inst;
192 char buf[CMDLINE_PARSE_RESULT_BUFSIZE];
193 long double align; /* strong alignment constraint for buf */
194 } result, tmp_result;
195 void (*f)(void *, struct cmdline *, void *) = NULL;
200 int err = CMDLINE_PARSE_NOMATCH;
202 cmdline_parse_ctx_t *ctx;
203 char *result_buf = result.buf;
206 return CMDLINE_PARSE_BAD_ARGS;
211 * - look if the buffer contains at least one line
212 * - look if line contains only spaces or comments
213 * - count line length
216 while (! isendofline(*curbuf)) {
217 if ( *curbuf == '\0' ) {
218 debug_printf("Incomplete buf (len=%d)\n", linelen);
221 if ( iscomment(*curbuf) ) {
224 if ( ! isblank2(*curbuf) && ! comment) {
231 /* skip all endofline chars */
232 while (isendofline(buf[linelen])) {
237 if ( parse_it == 0 ) {
238 debug_printf("Empty line (len=%d)\n", linelen);
242 debug_printf("Parse line : len=%d, <%.*s>\n",
243 linelen, linelen > 64 ? 64 : linelen, buf);
246 inst = ctx[inst_num];
248 debug_printf("INST %d\n", inst_num);
251 tok = match_inst(inst, buf, 0, result_buf,
252 CMDLINE_PARSE_RESULT_BUFSIZE);
254 if (tok > 0) /* we matched at least one token */
255 err = CMDLINE_PARSE_BAD_ARGS;
258 debug_printf("INST fully parsed\n");
260 while (isblank2(*curbuf)) {
264 /* if end of buf -> there is no garbage after inst */
265 if (isendofline(*curbuf) || iscomment(*curbuf)) {
267 memcpy(&f, &inst->f, sizeof(f));
268 memcpy(&data, &inst->data, sizeof(data));
269 result_buf = tmp_result.buf;
272 /* more than 1 inst matches */
273 err = CMDLINE_PARSE_AMBIGUOUS;
275 debug_printf("Ambiguous cmd\n");
282 inst = ctx[inst_num];
287 f(result.buf, cl, data);
292 debug_printf("No match err=%d\n", err);
300 cmdline_complete(struct cmdline *cl, const char *buf, int *state,
301 char *dst, unsigned int size)
303 const char *partial_tok = buf;
304 unsigned int inst_num = 0;
305 cmdline_parse_inst_t *inst;
306 cmdline_parse_token_hdr_t *token_p;
307 struct cmdline_token_hdr token_hdr;
308 char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
309 unsigned int partial_tok_len;
315 unsigned int nb_completable;
316 unsigned int nb_non_completable;
318 const char *help_str;
319 cmdline_parse_ctx_t *ctx;
321 if (!cl || !buf || !state || !dst)
326 debug_printf("%s called\n", __func__);
327 memset(&token_hdr, 0, sizeof(token_hdr));
329 /* count the number of complete token to parse */
330 for (i=0 ; buf[i] ; i++) {
331 if (!isblank2(buf[i]) && isblank2(buf[i+1]))
333 if (isblank2(buf[i]) && !isblank2(buf[i+1]))
334 partial_tok = buf+i+1;
336 partial_tok_len = strnlen(partial_tok, RDLINE_BUF_SIZE);
338 /* first call -> do a first pass */
340 debug_printf("try complete <%s>\n", buf);
341 debug_printf("there is %d complete tokens, <%s> is incomplete\n",
342 nb_token, partial_tok);
345 nb_non_completable = 0;
347 inst = ctx[inst_num];
349 /* parse the first tokens of the inst */
351 match_inst(inst, buf, nb_token, NULL, 0))
354 debug_printf("instruction match\n");
355 token_p = get_token(inst, nb_token);
357 memcpy(&token_hdr, token_p, sizeof(token_hdr));
359 /* non completable */
361 !token_hdr.ops->complete_get_nb ||
362 !token_hdr.ops->complete_get_elt ||
363 (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
364 nb_non_completable++;
368 debug_printf("%d choices for this token\n", n);
369 for (i=0 ; i<n ; i++) {
370 if (token_hdr.ops->complete_get_elt(token_p, i,
375 /* we have at least room for one char */
376 tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
377 if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
378 tmpbuf[tmp_len] = ' ';
379 tmpbuf[tmp_len+1] = 0;
382 debug_printf(" choice <%s>\n", tmpbuf);
384 /* does the completion match the
385 * beginning of the word ? */
386 if (!strncmp(partial_tok, tmpbuf,
388 if (comp_len == -1) {
390 tmpbuf + partial_tok_len,
393 strnlen(tmpbuf + partial_tok_len,
394 sizeof(tmpbuf) - partial_tok_len);
399 nb_common_chars(comp_buf,
400 tmpbuf+partial_tok_len);
401 comp_buf[comp_len] = 0;
407 debug_printf("next\n");
409 inst = ctx[inst_num];
412 debug_printf("total choices %d for this completion\n",
415 /* no possible completion */
416 if (nb_completable == 0 && nb_non_completable == 0)
419 /* if multichoice is not required */
420 if (*state == 0 && partial_tok_len > 0) {
421 /* one or several choices starting with the
424 if ((unsigned)(comp_len + 1) > size)
427 strlcpy(dst, comp_buf, size);
434 /* init state correctly */
438 debug_printf("Multiple choice STATE=%d\n", *state);
441 inst = ctx[inst_num];
443 /* we need to redo it */
444 inst = ctx[inst_num];
447 match_inst(inst, buf, nb_token, NULL, 0))
450 token_p = get_token(inst, nb_token);
452 memcpy(&token_hdr, token_p, sizeof(token_hdr));
454 /* one choice for this token */
456 !token_hdr.ops->complete_get_nb ||
457 !token_hdr.ops->complete_get_elt ||
458 (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
459 if (local_state < *state) {
464 if (token_p && token_hdr.ops->get_help) {
465 token_hdr.ops->get_help(token_p, tmpbuf,
467 help_str = inst->help_str;
469 snprintf(dst, size, "[%s]: %s", tmpbuf,
472 snprintf(dst, size, "[%s]: No help",
476 snprintf(dst, size, "[RETURN]");
481 /* several choices */
482 for (i=0 ; i<n ; i++) {
483 if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf,
486 /* we have at least room for one char */
487 tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
488 if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
489 tmpbuf[tmp_len] = ' ';
490 tmpbuf[tmp_len + 1] = 0;
493 debug_printf(" choice <%s>\n", tmpbuf);
495 /* does the completion match the beginning of
497 if (!strncmp(partial_tok, tmpbuf,
499 if (local_state < *state) {
504 l=strlcpy(dst, tmpbuf, size);
505 if (l>=0 && token_hdr.ops->get_help) {
506 token_hdr.ops->get_help(token_p, tmpbuf,
508 help_str = inst->help_str;
510 snprintf(dst+l, size-l, "[%s]: %s",
513 snprintf(dst+l, size-l,
514 "[%s]: No help", tmpbuf);
522 inst = ctx[inst_num];