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