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