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