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