1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation.
3 * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
15 #include <netinet/in.h>
17 #include <rte_string_fns.h>
19 #include "cmdline_rdline.h"
20 #include "cmdline_parse.h"
23 #ifdef RTE_LIBRTE_CMDLINE_DEBUG
24 #define debug_printf printf
26 #define debug_printf(args...) do {} while(0)
29 #define CMDLINE_BUFFER_SIZE 64
31 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
60 cmdline_isendoftoken(char c
)
62 if (!c
|| iscomment(c
) || isblank2(c
) || isendofline(c
))
68 cmdline_isendofcommand(char c
)
70 if (!c
|| iscomment(c
) || isendofline(c
))
76 nb_common_chars(const char * s1
, const char * s2
)
80 while (*s1
==*s2
&& *s1
) {
88 /** Retrieve either static or dynamic token at a given index. */
89 static cmdline_parse_token_hdr_t
*
90 get_token(cmdline_parse_inst_t
*inst
, unsigned int index
)
92 cmdline_parse_token_hdr_t
*token_p
;
94 /* check presence of static tokens first */
95 if (inst
->tokens
[0] || !inst
->f
)
96 return inst
->tokens
[index
];
97 /* generate dynamic token */
99 inst
->f(&token_p
, NULL
, &inst
->tokens
[index
]);
104 * try to match the buffer with an instruction (only the first
105 * nb_match_token tokens if != 0). Return 0 if we match all the
106 * tokens, else the number of matched tokens, else -1.
109 match_inst(cmdline_parse_inst_t
*inst
, const char *buf
,
110 unsigned int nb_match_token
, void *resbuf
, unsigned resbuf_size
)
112 cmdline_parse_token_hdr_t
*token_p
= NULL
;
115 struct cmdline_token_hdr token_hdr
;
118 memset(resbuf
, 0, resbuf_size
);
119 /* check if we match all tokens of inst */
120 while (!nb_match_token
|| i
< nb_match_token
) {
121 token_p
= get_token(inst
, i
);
124 memcpy(&token_hdr
, token_p
, sizeof(token_hdr
));
126 debug_printf("TK\n");
128 while (isblank2(*buf
)) {
133 if ( isendofline(*buf
) || iscomment(*buf
) )
136 if (resbuf
== NULL
) {
137 n
= token_hdr
.ops
->parse(token_p
, buf
, NULL
, 0);
141 if (token_hdr
.offset
> resbuf_size
) {
142 printf("Parse error(%s:%d): Token offset(%u) "
143 "exceeds maximum size(%u)\n",
145 token_hdr
.offset
, resbuf_size
);
148 rb_sz
= resbuf_size
- token_hdr
.offset
;
150 n
= token_hdr
.ops
->parse(token_p
, buf
, (char *)resbuf
+
151 token_hdr
.offset
, rb_sz
);
157 debug_printf("TK parsed (len=%d)\n", n
);
166 /* in case we want to match a specific num of token */
167 if (nb_match_token
) {
168 if (i
== nb_match_token
) {
174 /* we don't match all the tokens */
179 /* are there are some tokens more */
180 while (isblank2(*buf
)) {
185 if ( isendofline(*buf
) || iscomment(*buf
) )
188 /* garbage after inst */
194 cmdline_parse(struct cmdline
*cl
, const char * buf
)
196 unsigned int inst_num
=0;
197 cmdline_parse_inst_t
*inst
;
200 char buf
[CMDLINE_PARSE_RESULT_BUFSIZE
];
201 long double align
; /* strong alignment constraint for buf */
202 } result
, tmp_result
;
203 void (*f
)(void *, struct cmdline
*, void *) = NULL
;
208 int err
= CMDLINE_PARSE_NOMATCH
;
210 cmdline_parse_ctx_t
*ctx
;
211 char *result_buf
= result
.buf
;
214 return CMDLINE_PARSE_BAD_ARGS
;
219 * - look if the buffer contains at least one line
220 * - look if line contains only spaces or comments
221 * - count line length
224 while (! isendofline(*curbuf
)) {
225 if ( *curbuf
== '\0' ) {
226 debug_printf("Incomplete buf (len=%d)\n", linelen
);
229 if ( iscomment(*curbuf
) ) {
232 if ( ! isblank2(*curbuf
) && ! comment
) {
239 /* skip all endofline chars */
240 while (isendofline(buf
[linelen
])) {
245 if ( parse_it
== 0 ) {
246 debug_printf("Empty line (len=%d)\n", linelen
);
250 debug_printf("Parse line : len=%d, <%.*s>\n",
251 linelen
, linelen
> 64 ? 64 : linelen
, buf
);
254 inst
= ctx
[inst_num
];
256 debug_printf("INST %d\n", inst_num
);
259 tok
= match_inst(inst
, buf
, 0, result_buf
,
260 CMDLINE_PARSE_RESULT_BUFSIZE
);
262 if (tok
> 0) /* we matched at least one token */
263 err
= CMDLINE_PARSE_BAD_ARGS
;
266 debug_printf("INST fully parsed\n");
268 while (isblank2(*curbuf
)) {
272 /* if end of buf -> there is no garbage after inst */
273 if (isendofline(*curbuf
) || iscomment(*curbuf
)) {
275 memcpy(&f
, &inst
->f
, sizeof(f
));
276 memcpy(&data
, &inst
->data
, sizeof(data
));
277 result_buf
= tmp_result
.buf
;
280 /* more than 1 inst matches */
281 err
= CMDLINE_PARSE_AMBIGUOUS
;
283 debug_printf("Ambiguous cmd\n");
290 inst
= ctx
[inst_num
];
295 f(result
.buf
, cl
, data
);
300 debug_printf("No match err=%d\n", err
);
308 cmdline_complete(struct cmdline
*cl
, const char *buf
, int *state
,
309 char *dst
, unsigned int size
)
311 const char *partial_tok
= buf
;
312 unsigned int inst_num
= 0;
313 cmdline_parse_inst_t
*inst
;
314 cmdline_parse_token_hdr_t
*token_p
;
315 struct cmdline_token_hdr token_hdr
;
316 char tmpbuf
[CMDLINE_BUFFER_SIZE
], comp_buf
[CMDLINE_BUFFER_SIZE
];
317 unsigned int partial_tok_len
;
323 unsigned int nb_completable
;
324 unsigned int nb_non_completable
;
326 const char *help_str
;
327 cmdline_parse_ctx_t
*ctx
;
329 if (!cl
|| !buf
|| !state
|| !dst
)
334 debug_printf("%s called\n", __func__
);
335 memset(&token_hdr
, 0, sizeof(token_hdr
));
337 /* count the number of complete token to parse */
338 for (i
=0 ; buf
[i
] ; i
++) {
339 if (!isblank2(buf
[i
]) && isblank2(buf
[i
+1]))
341 if (isblank2(buf
[i
]) && !isblank2(buf
[i
+1]))
342 partial_tok
= buf
+i
+1;
344 partial_tok_len
= strnlen(partial_tok
, RDLINE_BUF_SIZE
);
346 /* first call -> do a first pass */
348 debug_printf("try complete <%s>\n", buf
);
349 debug_printf("there is %d complete tokens, <%s> is incomplete\n",
350 nb_token
, partial_tok
);
353 nb_non_completable
= 0;
355 inst
= ctx
[inst_num
];
357 /* parse the first tokens of the inst */
359 match_inst(inst
, buf
, nb_token
, NULL
, 0))
362 debug_printf("instruction match\n");
363 token_p
= get_token(inst
, nb_token
);
365 memcpy(&token_hdr
, token_p
, sizeof(token_hdr
));
367 /* non completable */
369 !token_hdr
.ops
->complete_get_nb
||
370 !token_hdr
.ops
->complete_get_elt
||
371 (n
= token_hdr
.ops
->complete_get_nb(token_p
)) == 0) {
372 nb_non_completable
++;
376 debug_printf("%d choices for this token\n", n
);
377 for (i
=0 ; i
<n
; i
++) {
378 if (token_hdr
.ops
->complete_get_elt(token_p
, i
,
383 /* we have at least room for one char */
384 tmp_len
= strnlen(tmpbuf
, sizeof(tmpbuf
));
385 if (tmp_len
< CMDLINE_BUFFER_SIZE
- 1) {
386 tmpbuf
[tmp_len
] = ' ';
387 tmpbuf
[tmp_len
+1] = 0;
390 debug_printf(" choice <%s>\n", tmpbuf
);
392 /* does the completion match the
393 * beginning of the word ? */
394 if (!strncmp(partial_tok
, tmpbuf
,
396 if (comp_len
== -1) {
398 tmpbuf
+ partial_tok_len
,
401 strnlen(tmpbuf
+ partial_tok_len
,
402 sizeof(tmpbuf
) - partial_tok_len
);
407 nb_common_chars(comp_buf
,
408 tmpbuf
+partial_tok_len
);
409 comp_buf
[comp_len
] = 0;
415 debug_printf("next\n");
417 inst
= ctx
[inst_num
];
420 debug_printf("total choices %d for this completion\n",
423 /* no possible completion */
424 if (nb_completable
== 0 && nb_non_completable
== 0)
427 /* if multichoice is not required */
428 if (*state
== 0 && partial_tok_len
> 0) {
429 /* one or several choices starting with the
432 if ((unsigned)(comp_len
+ 1) > size
)
435 strlcpy(dst
, comp_buf
, size
);
442 /* init state correctly */
446 debug_printf("Multiple choice STATE=%d\n", *state
);
449 inst
= ctx
[inst_num
];
451 /* we need to redo it */
452 inst
= ctx
[inst_num
];
455 match_inst(inst
, buf
, nb_token
, NULL
, 0))
458 token_p
= get_token(inst
, nb_token
);
460 memcpy(&token_hdr
, token_p
, sizeof(token_hdr
));
462 /* one choice for this token */
464 !token_hdr
.ops
->complete_get_nb
||
465 !token_hdr
.ops
->complete_get_elt
||
466 (n
= token_hdr
.ops
->complete_get_nb(token_p
)) == 0) {
467 if (local_state
< *state
) {
472 if (token_p
&& token_hdr
.ops
->get_help
) {
473 token_hdr
.ops
->get_help(token_p
, tmpbuf
,
475 help_str
= inst
->help_str
;
477 snprintf(dst
, size
, "[%s]: %s", tmpbuf
,
480 snprintf(dst
, size
, "[%s]: No help",
484 snprintf(dst
, size
, "[RETURN]");
489 /* several choices */
490 for (i
=0 ; i
<n
; i
++) {
491 if (token_hdr
.ops
->complete_get_elt(token_p
, i
, tmpbuf
,
494 /* we have at least room for one char */
495 tmp_len
= strnlen(tmpbuf
, sizeof(tmpbuf
));
496 if (tmp_len
< CMDLINE_BUFFER_SIZE
- 1) {
497 tmpbuf
[tmp_len
] = ' ';
498 tmpbuf
[tmp_len
+ 1] = 0;
501 debug_printf(" choice <%s>\n", tmpbuf
);
503 /* does the completion match the beginning of
505 if (!strncmp(partial_tok
, tmpbuf
,
507 if (local_state
< *state
) {
512 l
=strlcpy(dst
, tmpbuf
, size
);
513 if (l
>=0 && token_hdr
.ops
->get_help
) {
514 token_hdr
.ops
->get_help(token_p
, tmpbuf
,
516 help_str
= inst
->help_str
;
518 snprintf(dst
+l
, size
-l
, "[%s]: %s",
521 snprintf(dst
+l
, size
-l
,
522 "[%s]: No help", tmpbuf
);
530 inst
= ctx
[inst_num
];