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