command.c 14.3 KB
Newer Older
wdenk's avatar
wdenk committed
1
/*
2
 * (C) Copyright 2000-2009
wdenk's avatar
wdenk committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

/*
 *  Command Processor Table
 */

#include <common.h>
#include <command.h>

int
do_version (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	extern char version_string[];
	printf ("\n%s\n", version_string);
	return 0;
}

39 40
U_BOOT_CMD(
	version,	1,		1,	do_version,
Peter Tyser's avatar
Peter Tyser committed
41
	"print monitor version",
42 43 44
	NULL
);

45
#if defined(CONFIG_CMD_ECHO)
46

wdenk's avatar
wdenk committed
47 48 49 50 51 52 53 54 55 56
int
do_echo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int i, putnl = 1;

	for (i = 1; i < argc; i++) {
		char *p = argv[i], c;

		if (i > 1)
			putc(' ');
57
		while ((c = *p++) != '\0') {
wdenk's avatar
wdenk committed
58 59 60
			if (c == '\\' && *p == 'c') {
				putnl = 0;
				p++;
61
			} else {
wdenk's avatar
wdenk committed
62
				putc(c);
63 64
			}
		}
wdenk's avatar
wdenk committed
65 66 67 68 69 70 71
	}

	if (putnl)
		putc('\n');
	return 0;
}

72
U_BOOT_CMD(
73
	echo,	CONFIG_SYS_MAXARGS,	1,	do_echo,
Peter Tyser's avatar
Peter Tyser committed
74
	"echo args to console",
Wolfgang Denk's avatar
Wolfgang Denk committed
75
	"[args..]\n"
76 77 78
	"    - echo args to console; \\c suppresses newline\n"
);

79
#endif
80

81
#ifdef CONFIG_SYS_HUSH_PARSER
82 83 84 85 86 87 88 89 90 91 92 93 94 95

int
do_test (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	char **ap;
	int left, adv, expr, last_expr, neg, last_cmp;

	/* args? */
	if (argc < 3)
		return 1;

#if 0
	{
		printf("test:");
wdenk's avatar
wdenk committed
96
		left = 1;
97 98 99 100
		while (argv[left])
			printf(" %s", argv[left++]);
	}
#endif
wdenk's avatar
wdenk committed
101

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	last_expr = 0;
	left = argc - 1; ap = argv + 1;
	if (left > 0 && strcmp(ap[0], "!") == 0) {
		neg = 1;
		ap++;
		left--;
	} else
		neg = 0;

	expr = -1;
	last_cmp = -1;
	last_expr = -1;
	while (left > 0) {

		if (strcmp(ap[0], "-o") == 0 || strcmp(ap[0], "-a") == 0)
			adv = 1;
		else if (strcmp(ap[0], "-z") == 0 || strcmp(ap[0], "-n") == 0)
			adv = 2;
		else
			adv = 3;

		if (left < adv) {
			expr = 1;
			break;
		}

		if (adv == 1) {
			if (strcmp(ap[0], "-o") == 0) {
				last_expr = expr;
				last_cmp = 0;
			} else if (strcmp(ap[0], "-a") == 0) {
				last_expr = expr;
				last_cmp = 1;
			} else {
				expr = 1;
				break;
			}
		}

		if (adv == 2) {
			if (strcmp(ap[0], "-z") == 0)
				expr = strlen(ap[1]) == 0 ? 1 : 0;
144 145
			else if (strcmp(ap[0], "-n") == 0)
				expr = strlen(ap[1]) == 0 ? 0 : 1;
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
			else {
				expr = 1;
				break;
			}

			if (last_cmp == 0)
				expr = last_expr || expr;
			else if (last_cmp == 1)
				expr = last_expr && expr;
			last_cmp = -1;
		}

		if (adv == 3) {
			if (strcmp(ap[1], "=") == 0)
				expr = strcmp(ap[0], ap[2]) == 0;
			else if (strcmp(ap[1], "!=") == 0)
				expr = strcmp(ap[0], ap[2]) != 0;
			else if (strcmp(ap[1], ">") == 0)
				expr = strcmp(ap[0], ap[2]) > 0;
			else if (strcmp(ap[1], "<") == 0)
				expr = strcmp(ap[0], ap[2]) < 0;
			else if (strcmp(ap[1], "-eq") == 0)
				expr = simple_strtol(ap[0], NULL, 10) == simple_strtol(ap[2], NULL, 10);
			else if (strcmp(ap[1], "-ne") == 0)
				expr = simple_strtol(ap[0], NULL, 10) != simple_strtol(ap[2], NULL, 10);
			else if (strcmp(ap[1], "-lt") == 0)
				expr = simple_strtol(ap[0], NULL, 10) < simple_strtol(ap[2], NULL, 10);
			else if (strcmp(ap[1], "-le") == 0)
				expr = simple_strtol(ap[0], NULL, 10) <= simple_strtol(ap[2], NULL, 10);
			else if (strcmp(ap[1], "-gt") == 0)
				expr = simple_strtol(ap[0], NULL, 10) > simple_strtol(ap[2], NULL, 10);
			else if (strcmp(ap[1], "-ge") == 0)
				expr = simple_strtol(ap[0], NULL, 10) >= simple_strtol(ap[2], NULL, 10);
			else {
				expr = 1;
				break;
			}

			if (last_cmp == 0)
				expr = last_expr || expr;
			else if (last_cmp == 1)
				expr = last_expr && expr;
			last_cmp = -1;
		}

		ap += adv; left -= adv;
	}

	if (neg)
		expr = !expr;

	expr = !expr;

Wolfgang Denk's avatar
Wolfgang Denk committed
199
	debug (": returns %d\n", expr);
200 201 202 203 204

	return expr;
}

U_BOOT_CMD(
205
	test,	CONFIG_SYS_MAXARGS,	1,	do_test,
Peter Tyser's avatar
Peter Tyser committed
206
	"minimal test like /bin/sh",
Wolfgang Denk's avatar
Wolfgang Denk committed
207
	"[args..]\n"
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	"    - test functionality\n"
);

int
do_exit (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int r;

	r = 0;
	if (argc > 1)
		r = simple_strtoul(argv[1], NULL, 10);

	return -r - 2;
}

U_BOOT_CMD(
	exit,	2,	1,	do_exit,
Peter Tyser's avatar
Peter Tyser committed
225
	"exit script",
226 227 228 229 230 231
	"    - exit functionality\n"
);


#endif

wdenk's avatar
wdenk committed
232 233 234 235
/*
 * Use puts() instead of printf() to avoid printf buffer overflow
 * for long help messages
 */
236 237 238

int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, int
	      flag, int argc, char *argv[])
wdenk's avatar
wdenk committed
239 240 241 242
{
	int i;
	int rcode = 0;

wdenk's avatar
wdenk committed
243
	if (argc == 1) {	/*show list of commands */
244 245 246 247
		cmd_tbl_t *cmd_array[cmd_items];
		int i, j, swaps;

		/* Make array of commands from .uboot_cmd section */
248
		cmdtp = cmd_start;
249 250
		for (i = 0; i < cmd_items; i++) {
			cmd_array[i] = cmdtp++;
wdenk's avatar
wdenk committed
251 252
		}

253 254 255 256 257 258 259 260 261 262 263 264
		/* Sort command list (trivial bubble sort) */
		for (i = cmd_items - 1; i > 0; --i) {
			swaps = 0;
			for (j = 0; j < i; ++j) {
				if (strcmp (cmd_array[j]->name,
					    cmd_array[j + 1]->name) > 0) {
					cmd_tbl_t *tmp;
					tmp = cmd_array[j];
					cmd_array[j] = cmd_array[j + 1];
					cmd_array[j + 1] = tmp;
					++swaps;
				}
wdenk's avatar
wdenk committed
265
			}
266 267
			if (!swaps)
				break;
wdenk's avatar
wdenk committed
268
		}
wdenk's avatar
wdenk committed
269

wdenk's avatar
wdenk committed
270
		/* print short help (usage) */
271 272 273
		for (i = 0; i < cmd_items; i++) {
			const char *usage = cmd_array[i]->usage;

wdenk's avatar
wdenk committed
274
			/* allow user abort */
wdenk's avatar
wdenk committed
275
			if (ctrlc ())
wdenk's avatar
wdenk committed
276
				return 1;
277
			if (usage == NULL)
wdenk's avatar
wdenk committed
278
				continue;
Peter Tyser's avatar
Peter Tyser committed
279 280
			printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH,
			       cmd_array[i]->name, usage);
wdenk's avatar
wdenk committed
281 282 283 284 285 286
		}
		return 0;
	}
	/*
	 * command help (long version)
	 */
wdenk's avatar
wdenk committed
287
	for (i = 1; i < argc; ++i) {
288
		if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) {
289
			rcode |= cmd_usage(cmdtp);
290
		} else {
wdenk's avatar
wdenk committed
291 292
			printf ("Unknown command '%s' - try 'help'"
				" without arguments for list of all"
wdenk's avatar
wdenk committed
293 294
				" known commands\n\n", argv[i]
					);
wdenk's avatar
wdenk committed
295 296 297 298 299 300
			rcode = 1;
		}
	}
	return rcode;
}

301 302 303 304 305 306 307
int do_help (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
	return _do_help(&__u_boot_cmd_start,
			&__u_boot_cmd_end - &__u_boot_cmd_start,
			cmdtp, flag, argc, argv);
}

wdenk's avatar
wdenk committed
308

309
U_BOOT_CMD(
310
	help,	CONFIG_SYS_MAXARGS,	1,	do_help,
Peter Tyser's avatar
Peter Tyser committed
311
	"print online help",
Wolfgang Denk's avatar
Wolfgang Denk committed
312 313 314 315 316
	"[command ...]\n"
	"    - show help information (for 'command')\n"
	"'help' prints online help for the monitor commands.\n\n"
	"Without arguments, it prints a short usage message for all commands.\n\n"
	"To get detailed help information for specific commands you can type\n"
wdenk's avatar
wdenk committed
317 318 319
  "'help' with one or more command names as arguments.\n"
);

320
/* This do not ust the U_BOOT_CMD macro as ? can't be used in symbol names */
321
#ifdef  CONFIG_SYS_LONGHELP
322
cmd_tbl_t __u_boot_cmd_question_mark Struct_Section = {
323
	"?",	CONFIG_SYS_MAXARGS,	1,	do_help,
Peter Tyser's avatar
Peter Tyser committed
324
	"alias for 'help'",
wdenk's avatar
wdenk committed
325
	NULL
326 327 328
};
#else
cmd_tbl_t __u_boot_cmd_question_mark Struct_Section = {
329
	"?",	CONFIG_SYS_MAXARGS,	1,	do_help,
Peter Tyser's avatar
Peter Tyser committed
330
	"alias for 'help'"
331
};
332
#endif /* CONFIG_SYS_LONGHELP */
wdenk's avatar
wdenk committed
333

wdenk's avatar
wdenk committed
334 335 336
/***************************************************************************
 * find command table entry for a command
 */
337
cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
wdenk's avatar
wdenk committed
338 339
{
	cmd_tbl_t *cmdtp;
340
	cmd_tbl_t *cmdtp_temp = table;	/*Init value */
341 342 343 344 345 346 347 348 349 350
	const char *p;
	int len;
	int n_found = 0;

	/*
	 * Some commands allow length modifiers (like "cp.b");
	 * compare command name only until first dot.
	 */
	len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);

351 352
	for (cmdtp = table;
	     cmdtp != table + table_len;
353 354 355 356 357 358 359 360
	     cmdtp++) {
		if (strncmp (cmd, cmdtp->name, len) == 0) {
			if (len == strlen (cmdtp->name))
				return cmdtp;	/* full match */

			cmdtp_temp = cmdtp;	/* abbreviated command ? */
			n_found++;
		}
wdenk's avatar
wdenk committed
361
	}
362
	if (n_found == 1) {			/* exactly one match */
wdenk's avatar
wdenk committed
363
		return cmdtp_temp;
364
	}
wdenk's avatar
wdenk committed
365

366
	return NULL;	/* not found or ambiguous command */
wdenk's avatar
wdenk committed
367
}
368

369 370 371 372 373 374
cmd_tbl_t *find_cmd (const char *cmd)
{
	int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
	return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
}

375
int cmd_usage(cmd_tbl_t *cmdtp)
376
{
377 378 379 380 381 382 383 384 385 386 387 388 389 390
	printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);

#ifdef	CONFIG_SYS_LONGHELP
	printf("Usage:\n%s ", cmdtp->name);

	if (!cmdtp->help) {
		puts ("- No additional help available.\n");
		return 1;
	}

	puts (cmdtp->help);
	putc ('\n');
#endif	/* CONFIG_SYS_LONGHELP */
	return 0;
391 392
}

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
#ifdef CONFIG_AUTO_COMPLETE

int var_complete(int argc, char *argv[], char last_char, int maxv, char *cmdv[])
{
	static char tmp_buf[512];
	int space;

	space = last_char == '\0' || last_char == ' ' || last_char == '\t';

	if (space && argc == 1)
		return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);

	if (!space && argc == 2)
		return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);

	return 0;
}

static void install_auto_complete_handler(const char *cmd,
		int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]))
{
	cmd_tbl_t *cmdtp;

	cmdtp = find_cmd(cmd);
	if (cmdtp == NULL)
		return;

	cmdtp->complete = complete;
}

void install_auto_complete(void)
{
	install_auto_complete_handler("printenv", var_complete);
	install_auto_complete_handler("setenv", var_complete);
427
#if defined(CONFIG_CMD_RUN)
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
	install_auto_complete_handler("run", var_complete);
#endif
}

/*************************************************************************************/

static int complete_cmdv(int argc, char *argv[], char last_char, int maxv, char *cmdv[])
{
	cmd_tbl_t *cmdtp;
	const char *p;
	int len, clen;
	int n_found = 0;
	const char *cmd;

	/* sanity? */
	if (maxv < 2)
		return -2;

	cmdv[0] = NULL;

	if (argc == 0) {
		/* output full list of commands */
		for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) {
			if (n_found >= maxv - 2) {
				cmdv[n_found++] = "...";
				break;
			}
			cmdv[n_found++] = cmdtp->name;
		}
		cmdv[n_found] = NULL;
		return n_found;
	}

	/* more than one arg or one but the start of the next */
	if (argc > 1 || (last_char == '\0' || last_char == ' ' || last_char == '\t')) {
		cmdtp = find_cmd(argv[0]);
		if (cmdtp == NULL || cmdtp->complete == NULL) {
			cmdv[0] = NULL;
			return 0;
		}
		return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
	}

	cmd = argv[0];
	/*
	 * Some commands allow length modifiers (like "cp.b");
	 * compare command name only until first dot.
	 */
	p = strchr(cmd, '.');
	if (p == NULL)
		len = strlen(cmd);
	else
		len = p - cmd;

	/* return the partial matches */
	for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) {

		clen = strlen(cmdtp->name);
		if (clen < len)
			continue;

		if (memcmp(cmd, cmdtp->name, len) != 0)
			continue;

		/* too many! */
		if (n_found >= maxv - 2) {
			cmdv[n_found++] = "...";
			break;
		}

		cmdv[n_found++] = cmdtp->name;
	}

	cmdv[n_found] = NULL;
	return n_found;
}

static int make_argv(char *s, int argvsz, char *argv[])
{
	int argc = 0;

	/* split into argv */
	while (argc < argvsz - 1) {

		/* skip any white space */
		while ((*s == ' ') || (*s == '\t'))
			++s;

Wolfgang Denk's avatar
Wolfgang Denk committed
516
		if (*s == '\0')	/* end of s, no more args	*/
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
			break;

		argv[argc++] = s;	/* begin of argument string	*/

		/* find end of string */
		while (*s && (*s != ' ') && (*s != '\t'))
			++s;

		if (*s == '\0')		/* end of s, no more args	*/
			break;

		*s++ = '\0';		/* terminate current arg	 */
	}
	argv[argc] = NULL;

	return argc;
}

static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char *argv[])
{
	int ll = leader != NULL ? strlen(leader) : 0;
	int sl = sep != NULL ? strlen(sep) : 0;
	int len, i;

	if (banner) {
		puts("\n");
		puts(banner);
	}

	i = linemax;	/* force leader and newline */
	while (*argv != NULL) {
		len = strlen(*argv) + sl;
		if (i + len >= linemax) {
			puts("\n");
			if (leader)
				puts(leader);
			i = ll - sl;
		} else if (sep)
			puts(sep);
		puts(*argv++);
		i += len;
	}
	printf("\n");
}

static int find_common_prefix(char *argv[])
{
	int i, len;
	char *anchor, *s, *t;

	if (*argv == NULL)
		return 0;

	/* begin with max */
	anchor = *argv++;
	len = strlen(anchor);
	while ((t = *argv++) != NULL) {
		s = anchor;
		for (i = 0; i < len; i++, t++, s++) {
			if (*t != *s)
				break;
		}
		len = s - anchor;
	}
	return len;
}

584
static char tmp_buf[CONFIG_SYS_CBSIZE];	/* copy of console I/O buffer	*/
585 586 587 588

int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
{
	int n = *np, col = *colp;
589
	char *argv[CONFIG_SYS_MAXARGS + 1];		/* NULL terminated	*/
590 591 592 593 594 595 596
	char *cmdv[20];
	char *s, *t;
	const char *sep;
	int i, j, k, len, seplen, argc;
	int cnt;
	char last_char;

597
	if (strcmp(prompt, CONFIG_SYS_PROMPT) != 0)
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
		return 0;	/* not in normal console */

	cnt = strlen(buf);
	if (cnt >= 1)
		last_char = buf[cnt - 1];
	else
		last_char = '\0';

	/* copy to secondary buffer which will be affected */
	strcpy(tmp_buf, buf);

	/* separate into argv */
	argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);

	/* do the completion and return the possible completions */
	i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv);

	/* no match; bell and out */
	if (i == 0) {
		if (argc > 1)	/* allow tab for non command */
			return 0;
		putc('\a');
		return 1;
	}

	s = NULL;
	len = 0;
	sep = NULL;
	seplen = 0;
	if (i == 1) { /* one match; perfect */
		k = strlen(argv[argc - 1]);
		s = cmdv[0] + k;
		len = strlen(s);
		sep = " ";
		seplen = 1;
	} else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) {	/* more */
		k = strlen(argv[argc - 1]);
		j -= k;
		if (j > 0) {
			s = cmdv[0] + k;
			len = j;
		}
	}

	if (s != NULL) {
		k = len + seplen;
		/* make sure it fits */
645
		if (n + k >= CONFIG_SYS_CBSIZE - 2) {
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
			putc('\a');
			return 1;
		}

		t = buf + cnt;
		for (i = 0; i < len; i++)
			*t++ = *s++;
		if (sep != NULL)
			for (i = 0; i < seplen; i++)
				*t++ = sep[i];
		*t = '\0';
		n += k;
		col += k;
		puts(t - k);
		if (sep == NULL)
			putc('\a');
		*np = n;
		*colp = col;
	} else {
		print_argv(NULL, "  ", " ", 78, cmdv);

		puts(prompt);
		puts(buf);
	}
	return 1;
}

#endif
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697

#ifdef CMD_DATA_SIZE
int cmd_get_data_size(char* arg, int default_size)
{
	/* Check for a size specification .b, .w or .l.
	 */
	int len = strlen(arg);
	if (len > 2 && arg[len-2] == '.') {
		switch(arg[len-1]) {
		case 'b':
			return 1;
		case 'w':
			return 2;
		case 'l':
			return 4;
		case 's':
			return -2;
		default:
			return -1;
		}
	}
	return default_size;
}
#endif