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