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