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 /** Retrieve either static or dynamic token at a given index. */ 143 static cmdline_parse_token_hdr_t * 144 get_token(cmdline_parse_inst_t *inst, unsigned int index) 145 { 146 cmdline_parse_token_hdr_t *token_p; 147 148 /* check presence of static tokens first */ 149 if (inst->tokens[0] || !inst->f) 150 return inst->tokens[index]; 151 /* generate dynamic token */ 152 token_p = NULL; 153 inst->f(&token_p, NULL, &inst->tokens[index]); 154 return token_p; 155 } 156 157 /** 158 * try to match the buffer with an instruction (only the first 159 * nb_match_token tokens if != 0). Return 0 if we match all the 160 * tokens, else the number of matched tokens, else -1. 161 */ 162 static int 163 match_inst(cmdline_parse_inst_t *inst, const char *buf, 164 unsigned int nb_match_token, void *resbuf, unsigned resbuf_size) 165 { 166 cmdline_parse_token_hdr_t *token_p = NULL; 167 unsigned int i=0; 168 int n = 0; 169 struct cmdline_token_hdr token_hdr; 170 171 if (resbuf != NULL) 172 memset(resbuf, 0, resbuf_size); 173 /* check if we match all tokens of inst */ 174 while (!nb_match_token || i < nb_match_token) { 175 token_p = get_token(inst, i); 176 if (!token_p) 177 break; 178 memcpy(&token_hdr, token_p, sizeof(token_hdr)); 179 180 debug_printf("TK\n"); 181 /* skip spaces */ 182 while (isblank2(*buf)) { 183 buf++; 184 } 185 186 /* end of buf */ 187 if ( isendofline(*buf) || iscomment(*buf) ) 188 break; 189 190 if (resbuf == NULL) { 191 n = token_hdr.ops->parse(token_p, buf, NULL, 0); 192 } else { 193 unsigned rb_sz; 194 195 if (token_hdr.offset > resbuf_size) { 196 printf("Parse error(%s:%d): Token offset(%u) " 197 "exceeds maximum size(%u)\n", 198 __FILE__, __LINE__, 199 token_hdr.offset, resbuf_size); 200 return -ENOBUFS; 201 } 202 rb_sz = resbuf_size - token_hdr.offset; 203 204 n = token_hdr.ops->parse(token_p, buf, (char *)resbuf + 205 token_hdr.offset, rb_sz); 206 } 207 208 if (n < 0) 209 break; 210 211 debug_printf("TK parsed (len=%d)\n", n); 212 i++; 213 buf += n; 214 } 215 216 /* does not match */ 217 if (i==0) 218 return -1; 219 220 /* in case we want to match a specific num of token */ 221 if (nb_match_token) { 222 if (i == nb_match_token) { 223 return 0; 224 } 225 return i; 226 } 227 228 /* we don't match all the tokens */ 229 if (token_p) { 230 return i; 231 } 232 233 /* are there are some tokens more */ 234 while (isblank2(*buf)) { 235 buf++; 236 } 237 238 /* end of buf */ 239 if ( isendofline(*buf) || iscomment(*buf) ) 240 return 0; 241 242 /* garbage after inst */ 243 return i; 244 } 245 246 247 int 248 cmdline_parse(struct cmdline *cl, const char * buf) 249 { 250 unsigned int inst_num=0; 251 cmdline_parse_inst_t *inst; 252 const char *curbuf; 253 union { 254 char buf[CMDLINE_PARSE_RESULT_BUFSIZE]; 255 long double align; /* strong alignment constraint for buf */ 256 } result, tmp_result; 257 void (*f)(void *, struct cmdline *, void *) = NULL; 258 void *data = NULL; 259 int comment = 0; 260 int linelen = 0; 261 int parse_it = 0; 262 int err = CMDLINE_PARSE_NOMATCH; 263 int tok; 264 cmdline_parse_ctx_t *ctx; 265 #ifdef RTE_LIBRTE_CMDLINE_DEBUG 266 char debug_buf[BUFSIZ]; 267 #endif 268 char *result_buf = result.buf; 269 270 if (!cl || !buf) 271 return CMDLINE_PARSE_BAD_ARGS; 272 273 ctx = cl->ctx; 274 275 /* 276 * - look if the buffer contains at least one line 277 * - look if line contains only spaces or comments 278 * - count line length 279 */ 280 curbuf = buf; 281 while (! isendofline(*curbuf)) { 282 if ( *curbuf == '\0' ) { 283 debug_printf("Incomplete buf (len=%d)\n", linelen); 284 return 0; 285 } 286 if ( iscomment(*curbuf) ) { 287 comment = 1; 288 } 289 if ( ! isblank2(*curbuf) && ! comment) { 290 parse_it = 1; 291 } 292 curbuf++; 293 linelen++; 294 } 295 296 /* skip all endofline chars */ 297 while (isendofline(buf[linelen])) { 298 linelen++; 299 } 300 301 /* empty line */ 302 if ( parse_it == 0 ) { 303 debug_printf("Empty line (len=%d)\n", linelen); 304 return linelen; 305 } 306 307 #ifdef RTE_LIBRTE_CMDLINE_DEBUG 308 snprintf(debug_buf, (linelen>64 ? 64 : linelen), "%s", buf); 309 debug_printf("Parse line : len=%d, <%s>\n", linelen, debug_buf); 310 #endif 311 312 /* parse it !! */ 313 inst = ctx[inst_num]; 314 while (inst) { 315 debug_printf("INST %d\n", inst_num); 316 317 /* fully parsed */ 318 tok = match_inst(inst, buf, 0, result_buf, 319 CMDLINE_PARSE_RESULT_BUFSIZE); 320 321 if (tok > 0) /* we matched at least one token */ 322 err = CMDLINE_PARSE_BAD_ARGS; 323 324 else if (!tok) { 325 debug_printf("INST fully parsed\n"); 326 /* skip spaces */ 327 while (isblank2(*curbuf)) { 328 curbuf++; 329 } 330 331 /* if end of buf -> there is no garbage after inst */ 332 if (isendofline(*curbuf) || iscomment(*curbuf)) { 333 if (!f) { 334 memcpy(&f, &inst->f, sizeof(f)); 335 memcpy(&data, &inst->data, sizeof(data)); 336 result_buf = tmp_result.buf; 337 } 338 else { 339 /* more than 1 inst matches */ 340 err = CMDLINE_PARSE_AMBIGUOUS; 341 f=NULL; 342 debug_printf("Ambiguous cmd\n"); 343 break; 344 } 345 } 346 } 347 348 inst_num ++; 349 inst = ctx[inst_num]; 350 } 351 352 /* call func */ 353 if (f) { 354 f(result.buf, cl, data); 355 } 356 357 /* no match */ 358 else { 359 debug_printf("No match err=%d\n", err); 360 return err; 361 } 362 363 return linelen; 364 } 365 366 int 367 cmdline_complete(struct cmdline *cl, const char *buf, int *state, 368 char *dst, unsigned int size) 369 { 370 const char *partial_tok = buf; 371 unsigned int inst_num = 0; 372 cmdline_parse_inst_t *inst; 373 cmdline_parse_token_hdr_t *token_p; 374 struct cmdline_token_hdr token_hdr; 375 char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE]; 376 unsigned int partial_tok_len; 377 int comp_len = -1; 378 int tmp_len = -1; 379 int nb_token = 0; 380 unsigned int i, n; 381 int l; 382 unsigned int nb_completable; 383 unsigned int nb_non_completable; 384 int local_state = 0; 385 const char *help_str; 386 cmdline_parse_ctx_t *ctx; 387 388 if (!cl || !buf || !state || !dst) 389 return -1; 390 391 ctx = cl->ctx; 392 393 debug_printf("%s called\n", __func__); 394 memset(&token_hdr, 0, sizeof(token_hdr)); 395 396 /* count the number of complete token to parse */ 397 for (i=0 ; buf[i] ; i++) { 398 if (!isblank2(buf[i]) && isblank2(buf[i+1])) 399 nb_token++; 400 if (isblank2(buf[i]) && !isblank2(buf[i+1])) 401 partial_tok = buf+i+1; 402 } 403 partial_tok_len = strnlen(partial_tok, RDLINE_BUF_SIZE); 404 405 /* first call -> do a first pass */ 406 if (*state <= 0) { 407 debug_printf("try complete <%s>\n", buf); 408 debug_printf("there is %d complete tokens, <%s> is incomplete\n", 409 nb_token, partial_tok); 410 411 nb_completable = 0; 412 nb_non_completable = 0; 413 414 inst = ctx[inst_num]; 415 while (inst) { 416 /* parse the first tokens of the inst */ 417 if (nb_token && 418 match_inst(inst, buf, nb_token, NULL, 0)) 419 goto next; 420 421 debug_printf("instruction match\n"); 422 token_p = get_token(inst, nb_token); 423 if (token_p) 424 memcpy(&token_hdr, token_p, sizeof(token_hdr)); 425 426 /* non completable */ 427 if (!token_p || 428 !token_hdr.ops->complete_get_nb || 429 !token_hdr.ops->complete_get_elt || 430 (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { 431 nb_non_completable++; 432 goto next; 433 } 434 435 debug_printf("%d choices for this token\n", n); 436 for (i=0 ; i<n ; i++) { 437 if (token_hdr.ops->complete_get_elt(token_p, i, 438 tmpbuf, 439 sizeof(tmpbuf)) < 0) 440 continue; 441 442 /* we have at least room for one char */ 443 tmp_len = strnlen(tmpbuf, sizeof(tmpbuf)); 444 if (tmp_len < CMDLINE_BUFFER_SIZE - 1) { 445 tmpbuf[tmp_len] = ' '; 446 tmpbuf[tmp_len+1] = 0; 447 } 448 449 debug_printf(" choice <%s>\n", tmpbuf); 450 451 /* does the completion match the 452 * beginning of the word ? */ 453 if (!strncmp(partial_tok, tmpbuf, 454 partial_tok_len)) { 455 if (comp_len == -1) { 456 snprintf(comp_buf, sizeof(comp_buf), 457 "%s", tmpbuf + partial_tok_len); 458 comp_len = 459 strnlen(tmpbuf + partial_tok_len, 460 sizeof(tmpbuf) - partial_tok_len); 461 462 } 463 else { 464 comp_len = 465 nb_common_chars(comp_buf, 466 tmpbuf+partial_tok_len); 467 comp_buf[comp_len] = 0; 468 } 469 nb_completable++; 470 } 471 } 472 next: 473 debug_printf("next\n"); 474 inst_num ++; 475 inst = ctx[inst_num]; 476 } 477 478 debug_printf("total choices %d for this completion\n", 479 nb_completable); 480 481 /* no possible completion */ 482 if (nb_completable == 0 && nb_non_completable == 0) 483 return 0; 484 485 /* if multichoice is not required */ 486 if (*state == 0 && partial_tok_len > 0) { 487 /* one or several choices starting with the 488 same chars */ 489 if (comp_len > 0) { 490 if ((unsigned)(comp_len + 1) > size) 491 return 0; 492 493 snprintf(dst, size, "%s", comp_buf); 494 dst[comp_len] = 0; 495 return 2; 496 } 497 } 498 } 499 500 /* init state correctly */ 501 if (*state == -1) 502 *state = 0; 503 504 debug_printf("Multiple choice STATE=%d\n", *state); 505 506 inst_num = 0; 507 inst = ctx[inst_num]; 508 while (inst) { 509 /* we need to redo it */ 510 inst = ctx[inst_num]; 511 512 if (nb_token && 513 match_inst(inst, buf, nb_token, NULL, 0)) 514 goto next2; 515 516 token_p = get_token(inst, nb_token); 517 if (token_p) 518 memcpy(&token_hdr, token_p, sizeof(token_hdr)); 519 520 /* one choice for this token */ 521 if (!token_p || 522 !token_hdr.ops->complete_get_nb || 523 !token_hdr.ops->complete_get_elt || 524 (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { 525 if (local_state < *state) { 526 local_state++; 527 goto next2; 528 } 529 (*state)++; 530 if (token_p && token_hdr.ops->get_help) { 531 token_hdr.ops->get_help(token_p, tmpbuf, 532 sizeof(tmpbuf)); 533 help_str = inst->help_str; 534 if (help_str) 535 snprintf(dst, size, "[%s]: %s", tmpbuf, 536 help_str); 537 else 538 snprintf(dst, size, "[%s]: No help", 539 tmpbuf); 540 } 541 else { 542 snprintf(dst, size, "[RETURN]"); 543 } 544 return 1; 545 } 546 547 /* several choices */ 548 for (i=0 ; i<n ; i++) { 549 if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf, 550 sizeof(tmpbuf)) < 0) 551 continue; 552 /* we have at least room for one char */ 553 tmp_len = strnlen(tmpbuf, sizeof(tmpbuf)); 554 if (tmp_len < CMDLINE_BUFFER_SIZE - 1) { 555 tmpbuf[tmp_len] = ' '; 556 tmpbuf[tmp_len + 1] = 0; 557 } 558 559 debug_printf(" choice <%s>\n", tmpbuf); 560 561 /* does the completion match the beginning of 562 * the word ? */ 563 if (!strncmp(partial_tok, tmpbuf, 564 partial_tok_len)) { 565 if (local_state < *state) { 566 local_state++; 567 continue; 568 } 569 (*state)++; 570 l=snprintf(dst, size, "%s", tmpbuf); 571 if (l>=0 && token_hdr.ops->get_help) { 572 token_hdr.ops->get_help(token_p, tmpbuf, 573 sizeof(tmpbuf)); 574 help_str = inst->help_str; 575 if (help_str) 576 snprintf(dst+l, size-l, "[%s]: %s", 577 tmpbuf, help_str); 578 else 579 snprintf(dst+l, size-l, 580 "[%s]: No help", tmpbuf); 581 } 582 583 return 1; 584 } 585 } 586 next2: 587 inst_num ++; 588 inst = ctx[inst_num]; 589 } 590 return 0; 591 } 592