1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2000-2009 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 */ 6 7 /* 8 * Command Processor Table 9 */ 10 11 #include <common.h> 12 #include <command.h> 13 #include <console.h> 14 #include <linux/ctype.h> 15 16 /* 17 * Use puts() instead of printf() to avoid printf buffer overflow 18 * for long help messages 19 */ 20 21 int _do_help(cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t *cmdtp, int flag, 22 int argc, char * const argv[]) 23 { 24 int i; 25 int rcode = 0; 26 27 if (argc == 1) { /* show list of commands */ 28 cmd_tbl_t *cmd_array[cmd_items]; 29 int i, j, swaps; 30 31 /* Make array of commands from .uboot_cmd section */ 32 cmdtp = cmd_start; 33 for (i = 0; i < cmd_items; i++) { 34 cmd_array[i] = cmdtp++; 35 } 36 37 /* Sort command list (trivial bubble sort) */ 38 for (i = cmd_items - 1; i > 0; --i) { 39 swaps = 0; 40 for (j = 0; j < i; ++j) { 41 if (strcmp(cmd_array[j]->name, 42 cmd_array[j + 1]->name) > 0) { 43 cmd_tbl_t *tmp; 44 tmp = cmd_array[j]; 45 cmd_array[j] = cmd_array[j + 1]; 46 cmd_array[j + 1] = tmp; 47 ++swaps; 48 } 49 } 50 if (!swaps) 51 break; 52 } 53 54 /* print short help (usage) */ 55 for (i = 0; i < cmd_items; i++) { 56 const char *usage = cmd_array[i]->usage; 57 58 /* allow user abort */ 59 if (ctrlc()) 60 return 1; 61 if (usage == NULL) 62 continue; 63 printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH, 64 cmd_array[i]->name, usage); 65 } 66 return 0; 67 } 68 /* 69 * command help (long version) 70 */ 71 for (i = 1; i < argc; ++i) { 72 cmdtp = find_cmd_tbl(argv[i], cmd_start, cmd_items); 73 if (cmdtp != NULL) { 74 rcode |= cmd_usage(cmdtp); 75 } else { 76 printf("Unknown command '%s' - try 'help' without arguments for list of all known commands\n\n", 77 argv[i]); 78 rcode = 1; 79 } 80 } 81 return rcode; 82 } 83 84 /* find command table entry for a command */ 85 cmd_tbl_t *find_cmd_tbl(const char *cmd, cmd_tbl_t *table, int table_len) 86 { 87 #ifdef CONFIG_CMDLINE 88 cmd_tbl_t *cmdtp; 89 cmd_tbl_t *cmdtp_temp = table; /* Init value */ 90 const char *p; 91 int len; 92 int n_found = 0; 93 94 if (!cmd) 95 return NULL; 96 /* 97 * Some commands allow length modifiers (like "cp.b"); 98 * compare command name only until first dot. 99 */ 100 len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd); 101 102 for (cmdtp = table; cmdtp != table + table_len; cmdtp++) { 103 if (strncmp(cmd, cmdtp->name, len) == 0) { 104 if (len == strlen(cmdtp->name)) 105 return cmdtp; /* full match */ 106 107 cmdtp_temp = cmdtp; /* abbreviated command ? */ 108 n_found++; 109 } 110 } 111 if (n_found == 1) { /* exactly one match */ 112 return cmdtp_temp; 113 } 114 #endif /* CONFIG_CMDLINE */ 115 116 return NULL; /* not found or ambiguous command */ 117 } 118 119 cmd_tbl_t *find_cmd(const char *cmd) 120 { 121 cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd); 122 const int len = ll_entry_count(cmd_tbl_t, cmd); 123 return find_cmd_tbl(cmd, start, len); 124 } 125 126 int cmd_usage(const cmd_tbl_t *cmdtp) 127 { 128 printf("%s - %s\n\n", cmdtp->name, cmdtp->usage); 129 130 #ifdef CONFIG_SYS_LONGHELP 131 printf("Usage:\n%s ", cmdtp->name); 132 133 if (!cmdtp->help) { 134 puts ("- No additional help available.\n"); 135 return 1; 136 } 137 138 puts(cmdtp->help); 139 putc('\n'); 140 #endif /* CONFIG_SYS_LONGHELP */ 141 return 1; 142 } 143 144 #ifdef CONFIG_AUTO_COMPLETE 145 static char env_complete_buf[512]; 146 147 int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) 148 { 149 int space; 150 151 space = last_char == '\0' || isblank(last_char); 152 153 if (space && argc == 1) 154 return env_complete("", maxv, cmdv, sizeof(env_complete_buf), 155 env_complete_buf, false); 156 157 if (!space && argc == 2) 158 return env_complete(argv[1], maxv, cmdv, 159 sizeof(env_complete_buf), 160 env_complete_buf, false); 161 162 return 0; 163 } 164 165 static int dollar_complete(int argc, char * const argv[], char last_char, 166 int maxv, char *cmdv[]) 167 { 168 /* Make sure the last argument starts with a $. */ 169 if (argc < 1 || argv[argc - 1][0] != '$' || 170 last_char == '\0' || isblank(last_char)) 171 return 0; 172 173 return env_complete(argv[argc - 1], maxv, cmdv, sizeof(env_complete_buf), 174 env_complete_buf, true); 175 } 176 177 /*************************************************************************************/ 178 179 int complete_subcmdv(cmd_tbl_t *cmdtp, int count, int argc, 180 char * const argv[], char last_char, 181 int maxv, char *cmdv[]) 182 { 183 #ifdef CONFIG_CMDLINE 184 const cmd_tbl_t *cmdend = cmdtp + count; 185 const char *p; 186 int len, clen; 187 int n_found = 0; 188 const char *cmd; 189 190 /* sanity? */ 191 if (maxv < 2) 192 return -2; 193 194 cmdv[0] = NULL; 195 196 if (argc == 0) { 197 /* output full list of commands */ 198 for (; cmdtp != cmdend; cmdtp++) { 199 if (n_found >= maxv - 2) { 200 cmdv[n_found++] = "..."; 201 break; 202 } 203 cmdv[n_found++] = cmdtp->name; 204 } 205 cmdv[n_found] = NULL; 206 return n_found; 207 } 208 209 /* more than one arg or one but the start of the next */ 210 if (argc > 1 || last_char == '\0' || isblank(last_char)) { 211 cmdtp = find_cmd_tbl(argv[0], cmdtp, count); 212 if (cmdtp == NULL || cmdtp->complete == NULL) { 213 cmdv[0] = NULL; 214 return 0; 215 } 216 return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); 217 } 218 219 cmd = argv[0]; 220 /* 221 * Some commands allow length modifiers (like "cp.b"); 222 * compare command name only until first dot. 223 */ 224 p = strchr(cmd, '.'); 225 if (p == NULL) 226 len = strlen(cmd); 227 else 228 len = p - cmd; 229 230 /* return the partial matches */ 231 for (; cmdtp != cmdend; cmdtp++) { 232 233 clen = strlen(cmdtp->name); 234 if (clen < len) 235 continue; 236 237 if (memcmp(cmd, cmdtp->name, len) != 0) 238 continue; 239 240 /* too many! */ 241 if (n_found >= maxv - 2) { 242 cmdv[n_found++] = "..."; 243 break; 244 } 245 246 cmdv[n_found++] = cmdtp->name; 247 } 248 249 cmdv[n_found] = NULL; 250 return n_found; 251 #else 252 return 0; 253 #endif 254 } 255 256 static int complete_cmdv(int argc, char * const argv[], char last_char, 257 int maxv, char *cmdv[]) 258 { 259 #ifdef CONFIG_CMDLINE 260 return complete_subcmdv(ll_entry_start(cmd_tbl_t, cmd), 261 ll_entry_count(cmd_tbl_t, cmd), argc, argv, 262 last_char, maxv, cmdv); 263 #else 264 return 0; 265 #endif 266 } 267 268 static int make_argv(char *s, int argvsz, char *argv[]) 269 { 270 int argc = 0; 271 272 /* split into argv */ 273 while (argc < argvsz - 1) { 274 275 /* skip any white space */ 276 while (isblank(*s)) 277 ++s; 278 279 if (*s == '\0') /* end of s, no more args */ 280 break; 281 282 argv[argc++] = s; /* begin of argument string */ 283 284 /* find end of string */ 285 while (*s && !isblank(*s)) 286 ++s; 287 288 if (*s == '\0') /* end of s, no more args */ 289 break; 290 291 *s++ = '\0'; /* terminate current arg */ 292 } 293 argv[argc] = NULL; 294 295 return argc; 296 } 297 298 static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[]) 299 { 300 int ll = leader != NULL ? strlen(leader) : 0; 301 int sl = sep != NULL ? strlen(sep) : 0; 302 int len, i; 303 304 if (banner) { 305 puts("\n"); 306 puts(banner); 307 } 308 309 i = linemax; /* force leader and newline */ 310 while (*argv != NULL) { 311 len = strlen(*argv) + sl; 312 if (i + len >= linemax) { 313 puts("\n"); 314 if (leader) 315 puts(leader); 316 i = ll - sl; 317 } else if (sep) 318 puts(sep); 319 puts(*argv++); 320 i += len; 321 } 322 printf("\n"); 323 } 324 325 static int find_common_prefix(char * const argv[]) 326 { 327 int i, len; 328 char *anchor, *s, *t; 329 330 if (*argv == NULL) 331 return 0; 332 333 /* begin with max */ 334 anchor = *argv++; 335 len = strlen(anchor); 336 while ((t = *argv++) != NULL) { 337 s = anchor; 338 for (i = 0; i < len; i++, t++, s++) { 339 if (*t != *s) 340 break; 341 } 342 len = s - anchor; 343 } 344 return len; 345 } 346 347 static char tmp_buf[CONFIG_SYS_CBSIZE + 1]; /* copy of console I/O buffer */ 348 349 int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp) 350 { 351 int n = *np, col = *colp; 352 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ 353 char *cmdv[20]; 354 char *s, *t; 355 const char *sep; 356 int i, j, k, len, seplen, argc; 357 int cnt; 358 char last_char; 359 360 if (strcmp(prompt, CONFIG_SYS_PROMPT) != 0) 361 return 0; /* not in normal console */ 362 363 cnt = strlen(buf); 364 if (cnt >= 1) 365 last_char = buf[cnt - 1]; 366 else 367 last_char = '\0'; 368 369 /* copy to secondary buffer which will be affected */ 370 strcpy(tmp_buf, buf); 371 372 /* separate into argv */ 373 argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv); 374 375 /* first try a $ completion */ 376 i = dollar_complete(argc, argv, last_char, 377 sizeof(cmdv) / sizeof(cmdv[0]), cmdv); 378 if (!i) { 379 /* do the completion and return the possible completions */ 380 i = complete_cmdv(argc, argv, last_char, 381 sizeof(cmdv) / sizeof(cmdv[0]), cmdv); 382 } 383 384 /* no match; bell and out */ 385 if (i == 0) { 386 if (argc > 1) /* allow tab for non command */ 387 return 0; 388 putc('\a'); 389 return 1; 390 } 391 392 s = NULL; 393 len = 0; 394 sep = NULL; 395 seplen = 0; 396 if (i == 1) { /* one match; perfect */ 397 if (last_char != '\0' && !isblank(last_char)) 398 k = strlen(argv[argc - 1]); 399 else 400 k = 0; 401 402 s = cmdv[0] + k; 403 len = strlen(s); 404 sep = " "; 405 seplen = 1; 406 } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ 407 if (last_char != '\0' && !isblank(last_char)) 408 k = strlen(argv[argc - 1]); 409 else 410 k = 0; 411 412 j -= k; 413 if (j > 0) { 414 s = cmdv[0] + k; 415 len = j; 416 } 417 } 418 419 if (s != NULL) { 420 k = len + seplen; 421 /* make sure it fits */ 422 if (n + k >= CONFIG_SYS_CBSIZE - 2) { 423 putc('\a'); 424 return 1; 425 } 426 427 t = buf + cnt; 428 for (i = 0; i < len; i++) 429 *t++ = *s++; 430 if (sep != NULL) 431 for (i = 0; i < seplen; i++) 432 *t++ = sep[i]; 433 *t = '\0'; 434 n += k; 435 col += k; 436 puts(t - k); 437 if (sep == NULL) 438 putc('\a'); 439 *np = n; 440 *colp = col; 441 } else { 442 print_argv(NULL, " ", " ", 78, cmdv); 443 444 puts(prompt); 445 puts(buf); 446 } 447 return 1; 448 } 449 450 #endif 451 452 #ifdef CMD_DATA_SIZE 453 int cmd_get_data_size(char* arg, int default_size) 454 { 455 /* Check for a size specification .b, .w or .l. 456 */ 457 int len = strlen(arg); 458 if (len > 2 && arg[len-2] == '.') { 459 switch (arg[len-1]) { 460 case 'b': 461 return 1; 462 case 'w': 463 return 2; 464 case 'l': 465 return 4; 466 #ifdef CONFIG_SYS_SUPPORT_64BIT_DATA 467 case 'q': 468 return 8; 469 #endif 470 case 's': 471 return -2; 472 default: 473 return -1; 474 } 475 } 476 return default_size; 477 } 478 #endif 479 480 #if defined(CONFIG_NEEDS_MANUAL_RELOC) 481 DECLARE_GLOBAL_DATA_PTR; 482 483 void fixup_cmdtable(cmd_tbl_t *cmdtp, int size) 484 { 485 int i; 486 487 if (gd->reloc_off == 0) 488 return; 489 490 for (i = 0; i < size; i++) { 491 ulong addr; 492 493 addr = (ulong)(cmdtp->cmd) + gd->reloc_off; 494 #ifdef DEBUG_COMMANDS 495 printf("Command \"%s\": 0x%08lx => 0x%08lx\n", 496 cmdtp->name, (ulong)(cmdtp->cmd), addr); 497 #endif 498 cmdtp->cmd = 499 (int (*)(struct cmd_tbl_s *, int, int, char * const []))addr; 500 addr = (ulong)(cmdtp->name) + gd->reloc_off; 501 cmdtp->name = (char *)addr; 502 if (cmdtp->usage) { 503 addr = (ulong)(cmdtp->usage) + gd->reloc_off; 504 cmdtp->usage = (char *)addr; 505 } 506 #ifdef CONFIG_SYS_LONGHELP 507 if (cmdtp->help) { 508 addr = (ulong)(cmdtp->help) + gd->reloc_off; 509 cmdtp->help = (char *)addr; 510 } 511 #endif 512 #ifdef CONFIG_AUTO_COMPLETE 513 if (cmdtp->complete) { 514 addr = (ulong)(cmdtp->complete) + gd->reloc_off; 515 cmdtp->complete = 516 (int (*)(int, char * const [], char, int, char * []))addr; 517 } 518 #endif 519 cmdtp++; 520 } 521 } 522 #endif 523 524 int cmd_always_repeatable(cmd_tbl_t *cmdtp, int flag, int argc, 525 char * const argv[], int *repeatable) 526 { 527 *repeatable = 1; 528 529 return cmdtp->cmd(cmdtp, flag, argc, argv); 530 } 531 532 int cmd_never_repeatable(cmd_tbl_t *cmdtp, int flag, int argc, 533 char * const argv[], int *repeatable) 534 { 535 *repeatable = 0; 536 537 return cmdtp->cmd(cmdtp, flag, argc, argv); 538 } 539 540 int cmd_discard_repeatable(cmd_tbl_t *cmdtp, int flag, int argc, 541 char * const argv[]) 542 { 543 int repeatable; 544 545 return cmdtp->cmd_rep(cmdtp, flag, argc, argv, &repeatable); 546 } 547 548 /** 549 * Call a command function. This should be the only route in U-Boot to call 550 * a command, so that we can track whether we are waiting for input or 551 * executing a command. 552 * 553 * @param cmdtp Pointer to the command to execute 554 * @param flag Some flags normally 0 (see CMD_FLAG_.. above) 555 * @param argc Number of arguments (arg 0 must be the command text) 556 * @param argv Arguments 557 * @param repeatable Can the command be repeated 558 * @return 0 if command succeeded, else non-zero (CMD_RET_...) 559 */ 560 static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 561 int *repeatable) 562 { 563 int result; 564 565 result = cmdtp->cmd_rep(cmdtp, flag, argc, argv, repeatable); 566 if (result) 567 debug("Command failed, result=%d\n", result); 568 return result; 569 } 570 571 enum command_ret_t cmd_process(int flag, int argc, char * const argv[], 572 int *repeatable, ulong *ticks) 573 { 574 enum command_ret_t rc = CMD_RET_SUCCESS; 575 cmd_tbl_t *cmdtp; 576 577 /* Look up command in command table */ 578 cmdtp = find_cmd(argv[0]); 579 if (cmdtp == NULL) { 580 printf("Unknown command '%s' - try 'help'\n", argv[0]); 581 return 1; 582 } 583 584 /* found - check max args */ 585 if (argc > cmdtp->maxargs) 586 rc = CMD_RET_USAGE; 587 588 #if defined(CONFIG_CMD_BOOTD) 589 /* avoid "bootd" recursion */ 590 else if (cmdtp->cmd == do_bootd) { 591 if (flag & CMD_FLAG_BOOTD) { 592 puts("'bootd' recursion detected\n"); 593 rc = CMD_RET_FAILURE; 594 } else { 595 flag |= CMD_FLAG_BOOTD; 596 } 597 } 598 #endif 599 600 /* If OK so far, then do the command */ 601 if (!rc) { 602 int newrep; 603 604 if (ticks) 605 *ticks = get_timer(0); 606 rc = cmd_call(cmdtp, flag, argc, argv, &newrep); 607 if (ticks) 608 *ticks = get_timer(*ticks); 609 *repeatable &= newrep; 610 } 611 if (rc == CMD_RET_USAGE) 612 rc = cmd_usage(cmdtp); 613 return rc; 614 } 615 616 int cmd_process_error(cmd_tbl_t *cmdtp, int err) 617 { 618 if (err == CMD_RET_USAGE) 619 return CMD_RET_USAGE; 620 621 if (err) { 622 printf("Command '%s' failed: Error %d\n", cmdtp->name, err); 623 return CMD_RET_FAILURE; 624 } 625 626 return CMD_RET_SUCCESS; 627 } 628