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