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