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