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 22 flag, 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 if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) { 73 rcode |= cmd_usage(cmdtp); 74 } else { 75 printf ("Unknown command '%s' - try 'help'" 76 " without arguments for list of all" 77 " known commands\n\n", argv[i] 78 ); 79 rcode = 1; 80 } 81 } 82 return rcode; 83 } 84 85 /*************************************************************************** 86 * find command table entry for a command 87 */ 88 cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len) 89 { 90 cmd_tbl_t *cmdtp; 91 cmd_tbl_t *cmdtp_temp = table; /*Init value */ 92 const char *p; 93 int len; 94 int n_found = 0; 95 96 if (!cmd) 97 return NULL; 98 /* 99 * Some commands allow length modifiers (like "cp.b"); 100 * compare command name only until first dot. 101 */ 102 len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd); 103 104 for (cmdtp = table; 105 cmdtp != table + table_len; 106 cmdtp++) { 107 if (strncmp (cmd, cmdtp->name, len) == 0) { 108 if (len == strlen (cmdtp->name)) 109 return cmdtp; /* full match */ 110 111 cmdtp_temp = cmdtp; /* abbreviated command ? */ 112 n_found++; 113 } 114 } 115 if (n_found == 1) { /* exactly one match */ 116 return cmdtp_temp; 117 } 118 119 return NULL; /* not found or ambiguous command */ 120 } 121 122 cmd_tbl_t *find_cmd (const char *cmd) 123 { 124 cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd); 125 const int len = ll_entry_count(cmd_tbl_t, cmd); 126 return find_cmd_tbl(cmd, start, len); 127 } 128 129 int cmd_usage(const cmd_tbl_t *cmdtp) 130 { 131 printf("%s - %s\n\n", cmdtp->name, cmdtp->usage); 132 133 #ifdef CONFIG_SYS_LONGHELP 134 printf("Usage:\n%s ", cmdtp->name); 135 136 if (!cmdtp->help) { 137 puts ("- No additional help available.\n"); 138 return 1; 139 } 140 141 puts (cmdtp->help); 142 putc ('\n'); 143 #endif /* CONFIG_SYS_LONGHELP */ 144 return 1; 145 } 146 147 #ifdef CONFIG_AUTO_COMPLETE 148 149 int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) 150 { 151 static char tmp_buf[512]; 152 int space; 153 154 space = last_char == '\0' || isblank(last_char); 155 156 if (space && argc == 1) 157 return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf); 158 159 if (!space && argc == 2) 160 return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf); 161 162 return 0; 163 } 164 165 /*************************************************************************************/ 166 167 static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) 168 { 169 cmd_tbl_t *cmdtp = ll_entry_start(cmd_tbl_t, cmd); 170 const int count = ll_entry_count(cmd_tbl_t, cmd); 171 const cmd_tbl_t *cmdend = cmdtp + count; 172 const char *p; 173 int len, clen; 174 int n_found = 0; 175 const char *cmd; 176 177 /* sanity? */ 178 if (maxv < 2) 179 return -2; 180 181 cmdv[0] = NULL; 182 183 if (argc == 0) { 184 /* output full list of commands */ 185 for (; cmdtp != cmdend; cmdtp++) { 186 if (n_found >= maxv - 2) { 187 cmdv[n_found] = "..."; 188 break; 189 } 190 cmdv[n_found] = cmdtp->name; 191 } 192 cmdv[n_found] = NULL; 193 return n_found; 194 } 195 196 /* more than one arg or one but the start of the next */ 197 if (argc > 1 || (last_char == '\0' || isblank(last_char))) { 198 cmdtp = find_cmd(argv[0]); 199 if (cmdtp == NULL || cmdtp->complete == NULL) { 200 cmdv[0] = NULL; 201 return 0; 202 } 203 return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); 204 } 205 206 cmd = argv[0]; 207 /* 208 * Some commands allow length modifiers (like "cp.b"); 209 * compare command name only until first dot. 210 */ 211 p = strchr(cmd, '.'); 212 if (p == NULL) 213 len = strlen(cmd); 214 else 215 len = p - cmd; 216 217 /* return the partial matches */ 218 for (; cmdtp != cmdend; cmdtp++) { 219 220 clen = strlen(cmdtp->name); 221 if (clen < len) 222 continue; 223 224 if (memcmp(cmd, cmdtp->name, len) != 0) 225 continue; 226 227 /* too many! */ 228 if (n_found >= maxv - 2) { 229 cmdv[n_found++] = "..."; 230 break; 231 } 232 233 cmdv[n_found++] = cmdtp->name; 234 } 235 236 cmdv[n_found] = NULL; 237 return n_found; 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, sizeof(cmdv)/sizeof(cmdv[0]), cmdv); 349 350 /* no match; bell and out */ 351 if (i == 0) { 352 if (argc > 1) /* allow tab for non command */ 353 return 0; 354 putc('\a'); 355 return 1; 356 } 357 358 s = NULL; 359 len = 0; 360 sep = NULL; 361 seplen = 0; 362 if (i == 1) { /* one match; perfect */ 363 k = strlen(argv[argc - 1]); 364 s = cmdv[0] + k; 365 len = strlen(s); 366 sep = " "; 367 seplen = 1; 368 } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ 369 k = strlen(argv[argc - 1]); 370 j -= k; 371 if (j > 0) { 372 s = cmdv[0] + k; 373 len = j; 374 } 375 } 376 377 if (s != NULL) { 378 k = len + seplen; 379 /* make sure it fits */ 380 if (n + k >= CONFIG_SYS_CBSIZE - 2) { 381 putc('\a'); 382 return 1; 383 } 384 385 t = buf + cnt; 386 for (i = 0; i < len; i++) 387 *t++ = *s++; 388 if (sep != NULL) 389 for (i = 0; i < seplen; i++) 390 *t++ = sep[i]; 391 *t = '\0'; 392 n += k; 393 col += k; 394 puts(t - k); 395 if (sep == NULL) 396 putc('\a'); 397 *np = n; 398 *colp = col; 399 } else { 400 print_argv(NULL, " ", " ", 78, cmdv); 401 402 puts(prompt); 403 puts(buf); 404 } 405 return 1; 406 } 407 408 #endif 409 410 #ifdef CMD_DATA_SIZE 411 int cmd_get_data_size(char* arg, int default_size) 412 { 413 /* Check for a size specification .b, .w or .l. 414 */ 415 int len = strlen(arg); 416 if (len > 2 && arg[len-2] == '.') { 417 switch(arg[len-1]) { 418 case 'b': 419 return 1; 420 case 'w': 421 return 2; 422 case 'l': 423 return 4; 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