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