first public release
[dpdk.git] / lib / librte_cmdline / cmdline_parse.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  * 
7  *   Redistribution and use in source and binary forms, with or without 
8  *   modification, are permitted provided that the following conditions 
9  *   are met:
10  * 
11  *     * Redistributions of source code must retain the above copyright 
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright 
14  *       notice, this list of conditions and the following disclaimer in 
15  *       the documentation and/or other materials provided with the 
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its 
18  *       contributors may be used to endorse or promote products derived 
19  *       from this software without specific prior written permission.
20  * 
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  * 
33  *  version: DPDK.L.1.2.3-3
34  */
35
36 /*
37  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
38  * All rights reserved.
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions are met:
41  *
42  *     * Redistributions of source code must retain the above copyright
43  *       notice, this list of conditions and the following disclaimer.
44  *     * Redistributions in binary form must reproduce the above copyright
45  *       notice, this list of conditions and the following disclaimer in the
46  *       documentation and/or other materials provided with the distribution.
47  *     * Neither the name of the University of California, Berkeley nor the
48  *       names of its contributors may be used to endorse or promote products
49  *       derived from this software without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
52  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
53  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
55  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
56  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
57  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
58  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61  */
62
63 #include <stdio.h>
64 #include <stdarg.h>
65 #include <errno.h>
66 #include <string.h>
67 #include <inttypes.h>
68 #include <ctype.h>
69 #include <termios.h>
70
71 #include <netinet/in.h>
72
73 #include <rte_string_fns.h>
74
75 #include "cmdline_rdline.h"
76 #include "cmdline_parse.h"
77 #include "cmdline.h"
78
79 #ifdef RTE_LIBRTE_CMDLINE_DEBUG
80 #define debug_printf printf
81 #else
82 #define debug_printf(args...) do {} while(0)
83 #endif
84
85 #define CMDLINE_BUFFER_SIZE 64
86
87 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
88  * own. */
89 static int
90 isblank2(char c)
91 {
92         if (c == ' ' ||
93             c == '\t' )
94                 return 1;
95         return 0;
96 }
97
98 static int
99 isendofline(char c)
100 {
101         if (c == '\n' ||
102             c == '\r' )
103                 return 1;
104         return 0;
105 }
106
107 static int
108 iscomment(char c)
109 {
110         if (c == '#')
111                 return 1;
112         return 0;
113 }
114
115 int
116 cmdline_isendoftoken(char c)
117 {
118         if (!c || iscomment(c) || isblank2(c) || isendofline(c))
119                 return 1;
120         return 0;
121 }
122
123 static unsigned int
124 nb_common_chars(const char * s1, const char * s2)
125 {
126         unsigned int i=0;
127
128         while (*s1==*s2 && *s1 && *s2) {
129                 s1++;
130                 s2++;
131                 i++;
132         }
133         return i;
134 }
135
136 /**
137  * try to match the buffer with an instruction (only the first
138  * nb_match_token tokens if != 0). Return 0 if we match all the
139  * tokens, else the number of matched tokens, else -1.
140  */
141 static int
142 match_inst(cmdline_parse_inst_t *inst, const char *buf,
143            unsigned int nb_match_token, void * result_buf)
144 {
145         unsigned int token_num=0;
146         cmdline_parse_token_hdr_t * token_p;
147         unsigned int i=0;
148         int n = 0;
149         struct cmdline_token_hdr token_hdr;
150
151         token_p = inst->tokens[token_num];
152         if (token_p)
153                 memcpy(&token_hdr, token_p, sizeof(token_hdr));
154
155         /* check if we match all tokens of inst */
156         while (token_p && (!nb_match_token || i<nb_match_token)) {
157                 debug_printf("TK\n");
158                 /* skip spaces */
159                 while (isblank2(*buf)) {
160                         buf++;
161                 }
162
163                 /* end of buf */
164                 if ( isendofline(*buf) || iscomment(*buf) )
165                         break;
166
167                 if (result_buf)
168                         n = token_hdr.ops->parse(token_p, buf,
169                                                  (char *)result_buf +
170                                                  token_hdr.offset);
171                 else
172                         n = token_hdr.ops->parse(token_p, buf, NULL);
173
174                 if (n < 0)
175                         break;
176
177                 debug_printf("TK parsed (len=%d)\n", n);
178                 i++;
179                 buf += n;
180
181                 token_num ++;
182                 token_p = inst->tokens[token_num];
183                 if (token_p)
184                         memcpy(&token_hdr, token_p, sizeof(token_hdr));
185         }
186
187         /* does not match */
188         if (i==0)
189                 return -1;
190
191         /* in case we want to match a specific num of token */
192         if (nb_match_token) {
193                 if (i == nb_match_token) {
194                         return 0;
195                 }
196                 return i;
197         }
198
199         /* we don't match all the tokens */
200         if (token_p) {
201                 return i;
202         }
203
204         /* are there are some tokens more */
205         while (isblank2(*buf)) {
206                 buf++;
207         }
208
209         /* end of buf */
210         if ( isendofline(*buf) || iscomment(*buf) )
211                 return 0;
212
213         /* garbage after inst */
214         return i;
215 }
216
217
218 int
219 cmdline_parse(struct cmdline *cl, const char * buf)
220 {
221         unsigned int inst_num=0;
222         cmdline_parse_inst_t *inst;
223         const char *curbuf;
224         char result_buf[BUFSIZ];
225         void (*f)(void *, struct cmdline *, void *) = NULL;
226         void *data = NULL;
227         int comment = 0;
228         int linelen = 0;
229         int parse_it = 0;
230         int err = CMDLINE_PARSE_NOMATCH;
231         int tok;
232         cmdline_parse_ctx_t *ctx = cl->ctx;
233 #ifdef RTE_LIBRTE_CMDLINE_DEBUG
234         char debug_buf[BUFSIZ];
235 #endif
236
237         /*
238          * - look if the buffer contains at least one line
239          * - look if line contains only spaces or comments
240          * - count line length
241          */
242         curbuf = buf;
243         while (! isendofline(*curbuf)) {
244                 if ( *curbuf == '\0' ) {
245                         debug_printf("Incomplete buf (len=%d)\n", linelen);
246                         return 0;
247                 }
248                 if ( iscomment(*curbuf) ) {
249                         comment = 1;
250                 }
251                 if ( ! isblank2(*curbuf) && ! comment) {
252                         parse_it = 1;
253                 }
254                 curbuf++;
255                 linelen++;
256         }
257
258         /* skip all endofline chars */
259         while (isendofline(buf[linelen])) {
260                 linelen++;
261         }
262
263         /* empty line */
264         if ( parse_it == 0 ) {
265                 debug_printf("Empty line (len=%d)\n", linelen);
266                 return linelen;
267         }
268
269 #ifdef RTE_LIBRTE_CMDLINE_DEBUG
270         rte_snprintf(debug_buf, (linelen>64 ? 64 : linelen), "%s", buf);
271         debug_printf("Parse line : len=%d, <%s>\n", linelen, debug_buf);
272 #endif
273
274         /* parse it !! */
275         inst = ctx[inst_num];
276         while (inst) {
277                 debug_printf("INST %d\n", inst_num);
278
279                 /* fully parsed */
280                 tok = match_inst(inst, buf, 0, result_buf);
281
282                 if (tok > 0) /* we matched at least one token */
283                         err = CMDLINE_PARSE_BAD_ARGS;
284
285                 else if (!tok) {
286                         debug_printf("INST fully parsed\n");
287                         /* skip spaces */
288                         while (isblank2(*curbuf)) {
289                                 curbuf++;
290                         }
291
292                         /* if end of buf -> there is no garbage after inst */
293                         if (isendofline(*curbuf) || iscomment(*curbuf)) {
294                                 if (!f) {
295                                         memcpy(&f, &inst->f, sizeof(f));
296                                         memcpy(&data, &inst->data, sizeof(data));
297                                 }
298                                 else {
299                                         /* more than 1 inst matches */
300                                         err = CMDLINE_PARSE_AMBIGUOUS;
301                                         f=NULL;
302                                         debug_printf("Ambiguous cmd\n");
303                                         break;
304                                 }
305                         }
306                 }
307
308                 inst_num ++;
309                 inst = ctx[inst_num];
310         }
311
312         /* call func */
313         if (f) {
314                 f(result_buf, cl, data);
315         }
316
317         /* no match */
318         else {
319                 debug_printf("No match err=%d\n", err);
320                 return err;
321         }
322
323         return linelen;
324 }
325
326 int
327 cmdline_complete(struct cmdline *cl, const char *buf, int *state,
328                  char *dst, unsigned int size)
329 {
330         const char *partial_tok = buf;
331         unsigned int inst_num = 0;
332         cmdline_parse_inst_t *inst;
333         cmdline_parse_token_hdr_t *token_p;
334         struct cmdline_token_hdr token_hdr;
335         char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
336         unsigned int partial_tok_len;
337         int comp_len = -1;
338         int tmp_len = -1;
339         int nb_token = 0;
340         unsigned int i, n;
341         int l;
342         unsigned int nb_completable;
343         unsigned int nb_non_completable;
344         int local_state = 0;
345         const char *help_str;
346         cmdline_parse_ctx_t *ctx = cl->ctx;
347
348         debug_printf("%s called\n", __func__);
349         memset(&token_hdr, 0, sizeof(token_hdr));
350
351         /* count the number of complete token to parse */
352         for (i=0 ; buf[i] ; i++) {
353                 if (!isblank2(buf[i]) && isblank2(buf[i+1]))
354                         nb_token++;
355                 if (isblank2(buf[i]) && !isblank2(buf[i+1]))
356                         partial_tok = buf+i+1;
357         }
358         partial_tok_len = strnlen(partial_tok, RDLINE_BUF_SIZE);
359
360         /* first call -> do a first pass */
361         if (*state <= 0) {
362                 debug_printf("try complete <%s>\n", buf);
363                 debug_printf("there is %d complete tokens, <%s> is incomplete\n",
364                              nb_token, partial_tok);
365
366                 nb_completable = 0;
367                 nb_non_completable = 0;
368
369                 inst = ctx[inst_num];
370                 while (inst) {
371                         /* parse the first tokens of the inst */
372                         if (nb_token && match_inst(inst, buf, nb_token, NULL))
373                                 goto next;
374
375                         debug_printf("instruction match \n");
376                         token_p = inst->tokens[nb_token];
377                         if (token_p)
378                                 memcpy(&token_hdr, token_p, sizeof(token_hdr));
379
380                         /* non completable */
381                         if (!token_p ||
382                             !token_hdr.ops->complete_get_nb ||
383                             !token_hdr.ops->complete_get_elt ||
384                             (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
385                                 nb_non_completable++;
386                                 goto next;
387                         }
388
389                         debug_printf("%d choices for this token\n", n);
390                         for (i=0 ; i<n ; i++) {
391                                 if (token_hdr.ops->complete_get_elt(token_p, i,
392                                                                     tmpbuf,
393                                                                     sizeof(tmpbuf)) < 0)
394                                         continue;
395
396                                 /* we have at least room for one char */
397                                 tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
398                                 if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
399                                         tmpbuf[tmp_len] = ' ';
400                                         tmpbuf[tmp_len+1] = 0;
401                                 }
402
403                                 debug_printf("   choice <%s>\n", tmpbuf);
404
405                                 /* does the completion match the
406                                  * beginning of the word ? */
407                                 if (!strncmp(partial_tok, tmpbuf,
408                                              partial_tok_len)) {
409                                         if (comp_len == -1) {
410                                                 rte_snprintf(comp_buf, sizeof(comp_buf),
411                                                          "%s", tmpbuf + partial_tok_len);
412                                                 comp_len =
413                                                         strnlen(tmpbuf + partial_tok_len,
414                                                                         sizeof(tmpbuf) - partial_tok_len);
415
416                                         }
417                                         else {
418                                                 comp_len =
419                                                         nb_common_chars(comp_buf,
420                                                                         tmpbuf+partial_tok_len);
421                                                 comp_buf[comp_len] = 0;
422                                         }
423                                         nb_completable++;
424                                 }
425                         }
426                 next:
427                         inst_num ++;
428                         inst = ctx[inst_num];
429                 }
430
431                 debug_printf("total choices %d for this completion\n",
432                              nb_completable);
433
434                 /* no possible completion */
435                 if (nb_completable == 0 && nb_non_completable == 0)
436                         return 0;
437
438                 /* if multichoice is not required */
439                 if (*state == 0 && partial_tok_len > 0) {
440                         /* one or several choices starting with the
441                            same chars */
442                         if (comp_len > 0) {
443                                 if ((unsigned)(comp_len + 1) > size)
444                                         return 0;
445
446                                 rte_snprintf(dst, size, "%s", comp_buf);
447                                 dst[comp_len] = 0;
448                                 return 2;
449                         }
450                 }
451         }
452
453         /* init state correctly */
454         if (*state == -1)
455                 *state = 0;
456
457         debug_printf("Multiple choice STATE=%d\n", *state);
458
459         inst_num = 0;
460         inst = ctx[inst_num];
461         while (inst) {
462                 /* we need to redo it */
463                 inst = ctx[inst_num];
464
465                 if (nb_token && match_inst(inst, buf, nb_token, NULL))
466                         goto next2;
467
468                 token_p = inst->tokens[nb_token];
469                 if (token_p)
470                         memcpy(&token_hdr, token_p, sizeof(token_hdr));
471
472                 /* one choice for this token */
473                 if (!token_p ||
474                     !token_hdr.ops->complete_get_nb ||
475                     !token_hdr.ops->complete_get_elt ||
476                     (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
477                         if (local_state < *state) {
478                                 local_state++;
479                                 goto next2;
480                         }
481                         (*state)++;
482                         if (token_p && token_hdr.ops->get_help) {
483                                 token_hdr.ops->get_help(token_p, tmpbuf,
484                                                         sizeof(tmpbuf));
485                                 help_str = inst->help_str;
486                                 if (help_str)
487                                         rte_snprintf(dst, size, "[%s]: %s", tmpbuf,
488                                                  help_str);
489                                 else
490                                         rte_snprintf(dst, size, "[%s]: No help",
491                                                  tmpbuf);
492                         }
493                         else {
494                                 rte_snprintf(dst, size, "[RETURN]");
495                         }
496                         return 1;
497                 }
498
499                 /* several choices */
500                 for (i=0 ; i<n ; i++) {
501                         if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf,
502                                                             sizeof(tmpbuf)) < 0)
503                                 continue;
504                         /* we have at least room for one char */
505                         tmp_len = strnlen(tmpbuf, sizeof(tmpbuf));
506                         if (tmp_len < CMDLINE_BUFFER_SIZE - 1) {
507                                 tmpbuf[tmp_len] = ' ';
508                                 tmpbuf[tmp_len + 1] = 0;
509                         }
510
511                         debug_printf("   choice <%s>\n", tmpbuf);
512
513                         /* does the completion match the beginning of
514                          * the word ? */
515                         if (!strncmp(partial_tok, tmpbuf,
516                                      partial_tok_len)) {
517                                 if (local_state < *state) {
518                                         local_state++;
519                                         continue;
520                                 }
521                                 (*state)++;
522                                 l=rte_snprintf(dst, size, "%s", tmpbuf);
523                                 if (l>=0 && token_hdr.ops->get_help) {
524                                         token_hdr.ops->get_help(token_p, tmpbuf,
525                                                                 sizeof(tmpbuf));
526                                         help_str = inst->help_str;
527                                         if (help_str)
528                                                 rte_snprintf(dst+l, size-l, "[%s]: %s",
529                                                          tmpbuf, help_str);
530                                         else
531                                                 rte_snprintf(dst+l, size-l,
532                                                          "[%s]: No help", tmpbuf);
533                                 }
534
535                                 return 1;
536                         }
537                 }
538         next2:
539                 inst_num ++;
540                 inst = ctx[inst_num];
541         }
542         return 0;
543 }
544