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