net/mlx5: add E-Switch mode flag
[dpdk.git] / lib / cmdline / cmdline_parse.c
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
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
26 isblank2(char c)
27 {
28         if (c == ' ' ||
29             c == '\t' )
30                 return 1;
31         return 0;
32 }
33
34 static int
35 isendofline(char c)
36 {
37         if (c == '\n' ||
38             c == '\r' )
39                 return 1;
40         return 0;
41 }
42
43 static int
44 iscomment(char c)
45 {
46         if (c == '#')
47                 return 1;
48         return 0;
49 }
50
51 int
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
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
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 *
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
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
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
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 }