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