xref: /openbmc/u-boot/common/command.c (revision 77ea071f)
1 /*
2  * (C) Copyright 2000-2009
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23 
24 /*
25  *  Command Processor Table
26  */
27 
28 #include <common.h>
29 #include <command.h>
30 #include <linux/ctype.h>
31 
32 /*
33  * Use puts() instead of printf() to avoid printf buffer overflow
34  * for long help messages
35  */
36 
37 int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, int
38 	      flag, int argc, char * const argv[])
39 {
40 	int i;
41 	int rcode = 0;
42 
43 	if (argc == 1) {	/*show list of commands */
44 		cmd_tbl_t *cmd_array[cmd_items];
45 		int i, j, swaps;
46 
47 		/* Make array of commands from .uboot_cmd section */
48 		cmdtp = cmd_start;
49 		for (i = 0; i < cmd_items; i++) {
50 			cmd_array[i] = cmdtp++;
51 		}
52 
53 		/* Sort command list (trivial bubble sort) */
54 		for (i = cmd_items - 1; i > 0; --i) {
55 			swaps = 0;
56 			for (j = 0; j < i; ++j) {
57 				if (strcmp (cmd_array[j]->name,
58 					    cmd_array[j + 1]->name) > 0) {
59 					cmd_tbl_t *tmp;
60 					tmp = cmd_array[j];
61 					cmd_array[j] = cmd_array[j + 1];
62 					cmd_array[j + 1] = tmp;
63 					++swaps;
64 				}
65 			}
66 			if (!swaps)
67 				break;
68 		}
69 
70 		/* print short help (usage) */
71 		for (i = 0; i < cmd_items; i++) {
72 			const char *usage = cmd_array[i]->usage;
73 
74 			/* allow user abort */
75 			if (ctrlc ())
76 				return 1;
77 			if (usage == NULL)
78 				continue;
79 			printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH,
80 			       cmd_array[i]->name, usage);
81 		}
82 		return 0;
83 	}
84 	/*
85 	 * command help (long version)
86 	 */
87 	for (i = 1; i < argc; ++i) {
88 		if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) {
89 			rcode |= cmd_usage(cmdtp);
90 		} else {
91 			printf ("Unknown command '%s' - try 'help'"
92 				" without arguments for list of all"
93 				" known commands\n\n", argv[i]
94 					);
95 			rcode = 1;
96 		}
97 	}
98 	return rcode;
99 }
100 
101 /***************************************************************************
102  * find command table entry for a command
103  */
104 cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
105 {
106 	cmd_tbl_t *cmdtp;
107 	cmd_tbl_t *cmdtp_temp = table;	/*Init value */
108 	const char *p;
109 	int len;
110 	int n_found = 0;
111 
112 	if (!cmd)
113 		return NULL;
114 	/*
115 	 * Some commands allow length modifiers (like "cp.b");
116 	 * compare command name only until first dot.
117 	 */
118 	len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
119 
120 	for (cmdtp = table;
121 	     cmdtp != table + table_len;
122 	     cmdtp++) {
123 		if (strncmp (cmd, cmdtp->name, len) == 0) {
124 			if (len == strlen (cmdtp->name))
125 				return cmdtp;	/* full match */
126 
127 			cmdtp_temp = cmdtp;	/* abbreviated command ? */
128 			n_found++;
129 		}
130 	}
131 	if (n_found == 1) {			/* exactly one match */
132 		return cmdtp_temp;
133 	}
134 
135 	return NULL;	/* not found or ambiguous command */
136 }
137 
138 cmd_tbl_t *find_cmd (const char *cmd)
139 {
140 	int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
141 	return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
142 }
143 
144 int cmd_usage(const cmd_tbl_t *cmdtp)
145 {
146 	printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
147 
148 #ifdef	CONFIG_SYS_LONGHELP
149 	printf("Usage:\n%s ", cmdtp->name);
150 
151 	if (!cmdtp->help) {
152 		puts ("- No additional help available.\n");
153 		return 1;
154 	}
155 
156 	puts (cmdtp->help);
157 	putc ('\n');
158 #endif	/* CONFIG_SYS_LONGHELP */
159 	return 1;
160 }
161 
162 #ifdef CONFIG_AUTO_COMPLETE
163 
164 int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
165 {
166 	static char tmp_buf[512];
167 	int space;
168 
169 	space = last_char == '\0' || isblank(last_char);
170 
171 	if (space && argc == 1)
172 		return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);
173 
174 	if (!space && argc == 2)
175 		return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);
176 
177 	return 0;
178 }
179 
180 /*************************************************************************************/
181 
182 static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
183 {
184 	cmd_tbl_t *cmdtp;
185 	const char *p;
186 	int len, clen;
187 	int n_found = 0;
188 	const char *cmd;
189 
190 	/* sanity? */
191 	if (maxv < 2)
192 		return -2;
193 
194 	cmdv[0] = NULL;
195 
196 	if (argc == 0) {
197 		/* output full list of commands */
198 		for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) {
199 			if (n_found >= maxv - 2) {
200 				cmdv[n_found++] = "...";
201 				break;
202 			}
203 			cmdv[n_found++] = cmdtp->name;
204 		}
205 		cmdv[n_found] = NULL;
206 		return n_found;
207 	}
208 
209 	/* more than one arg or one but the start of the next */
210 	if (argc > 1 || (last_char == '\0' || isblank(last_char))) {
211 		cmdtp = find_cmd(argv[0]);
212 		if (cmdtp == NULL || cmdtp->complete == NULL) {
213 			cmdv[0] = NULL;
214 			return 0;
215 		}
216 		return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
217 	}
218 
219 	cmd = argv[0];
220 	/*
221 	 * Some commands allow length modifiers (like "cp.b");
222 	 * compare command name only until first dot.
223 	 */
224 	p = strchr(cmd, '.');
225 	if (p == NULL)
226 		len = strlen(cmd);
227 	else
228 		len = p - cmd;
229 
230 	/* return the partial matches */
231 	for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) {
232 
233 		clen = strlen(cmdtp->name);
234 		if (clen < len)
235 			continue;
236 
237 		if (memcmp(cmd, cmdtp->name, len) != 0)
238 			continue;
239 
240 		/* too many! */
241 		if (n_found >= maxv - 2) {
242 			cmdv[n_found++] = "...";
243 			break;
244 		}
245 
246 		cmdv[n_found++] = cmdtp->name;
247 	}
248 
249 	cmdv[n_found] = NULL;
250 	return n_found;
251 }
252 
253 static int make_argv(char *s, int argvsz, char *argv[])
254 {
255 	int argc = 0;
256 
257 	/* split into argv */
258 	while (argc < argvsz - 1) {
259 
260 		/* skip any white space */
261 		while (isblank(*s))
262 			++s;
263 
264 		if (*s == '\0')	/* end of s, no more args	*/
265 			break;
266 
267 		argv[argc++] = s;	/* begin of argument string	*/
268 
269 		/* find end of string */
270 		while (*s && !isblank(*s))
271 			++s;
272 
273 		if (*s == '\0')		/* end of s, no more args	*/
274 			break;
275 
276 		*s++ = '\0';		/* terminate current arg	 */
277 	}
278 	argv[argc] = NULL;
279 
280 	return argc;
281 }
282 
283 static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[])
284 {
285 	int ll = leader != NULL ? strlen(leader) : 0;
286 	int sl = sep != NULL ? strlen(sep) : 0;
287 	int len, i;
288 
289 	if (banner) {
290 		puts("\n");
291 		puts(banner);
292 	}
293 
294 	i = linemax;	/* force leader and newline */
295 	while (*argv != NULL) {
296 		len = strlen(*argv) + sl;
297 		if (i + len >= linemax) {
298 			puts("\n");
299 			if (leader)
300 				puts(leader);
301 			i = ll - sl;
302 		} else if (sep)
303 			puts(sep);
304 		puts(*argv++);
305 		i += len;
306 	}
307 	printf("\n");
308 }
309 
310 static int find_common_prefix(char * const argv[])
311 {
312 	int i, len;
313 	char *anchor, *s, *t;
314 
315 	if (*argv == NULL)
316 		return 0;
317 
318 	/* begin with max */
319 	anchor = *argv++;
320 	len = strlen(anchor);
321 	while ((t = *argv++) != NULL) {
322 		s = anchor;
323 		for (i = 0; i < len; i++, t++, s++) {
324 			if (*t != *s)
325 				break;
326 		}
327 		len = s - anchor;
328 	}
329 	return len;
330 }
331 
332 static char tmp_buf[CONFIG_SYS_CBSIZE];	/* copy of console I/O buffer	*/
333 
334 int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
335 {
336 	int n = *np, col = *colp;
337 	char *argv[CONFIG_SYS_MAXARGS + 1];		/* NULL terminated	*/
338 	char *cmdv[20];
339 	char *s, *t;
340 	const char *sep;
341 	int i, j, k, len, seplen, argc;
342 	int cnt;
343 	char last_char;
344 
345 	if (strcmp(prompt, CONFIG_SYS_PROMPT) != 0)
346 		return 0;	/* not in normal console */
347 
348 	cnt = strlen(buf);
349 	if (cnt >= 1)
350 		last_char = buf[cnt - 1];
351 	else
352 		last_char = '\0';
353 
354 	/* copy to secondary buffer which will be affected */
355 	strcpy(tmp_buf, buf);
356 
357 	/* separate into argv */
358 	argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);
359 
360 	/* do the completion and return the possible completions */
361 	i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv);
362 
363 	/* no match; bell and out */
364 	if (i == 0) {
365 		if (argc > 1)	/* allow tab for non command */
366 			return 0;
367 		putc('\a');
368 		return 1;
369 	}
370 
371 	s = NULL;
372 	len = 0;
373 	sep = NULL;
374 	seplen = 0;
375 	if (i == 1) { /* one match; perfect */
376 		k = strlen(argv[argc - 1]);
377 		s = cmdv[0] + k;
378 		len = strlen(s);
379 		sep = " ";
380 		seplen = 1;
381 	} else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) {	/* more */
382 		k = strlen(argv[argc - 1]);
383 		j -= k;
384 		if (j > 0) {
385 			s = cmdv[0] + k;
386 			len = j;
387 		}
388 	}
389 
390 	if (s != NULL) {
391 		k = len + seplen;
392 		/* make sure it fits */
393 		if (n + k >= CONFIG_SYS_CBSIZE - 2) {
394 			putc('\a');
395 			return 1;
396 		}
397 
398 		t = buf + cnt;
399 		for (i = 0; i < len; i++)
400 			*t++ = *s++;
401 		if (sep != NULL)
402 			for (i = 0; i < seplen; i++)
403 				*t++ = sep[i];
404 		*t = '\0';
405 		n += k;
406 		col += k;
407 		puts(t - k);
408 		if (sep == NULL)
409 			putc('\a');
410 		*np = n;
411 		*colp = col;
412 	} else {
413 		print_argv(NULL, "  ", " ", 78, cmdv);
414 
415 		puts(prompt);
416 		puts(buf);
417 	}
418 	return 1;
419 }
420 
421 #endif
422 
423 #ifdef CMD_DATA_SIZE
424 int cmd_get_data_size(char* arg, int default_size)
425 {
426 	/* Check for a size specification .b, .w or .l.
427 	 */
428 	int len = strlen(arg);
429 	if (len > 2 && arg[len-2] == '.') {
430 		switch(arg[len-1]) {
431 		case 'b':
432 			return 1;
433 		case 'w':
434 			return 2;
435 		case 'l':
436 			return 4;
437 		case 's':
438 			return -2;
439 		default:
440 			return -1;
441 		}
442 	}
443 	return default_size;
444 }
445 #endif
446 
447 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
448 DECLARE_GLOBAL_DATA_PTR;
449 
450 void fixup_cmdtable(cmd_tbl_t *cmdtp, int size)
451 {
452 	int	i;
453 
454 	if (gd->reloc_off == 0)
455 		return;
456 
457 	for (i = 0; i < size; i++) {
458 		ulong addr;
459 
460 		addr = (ulong) (cmdtp->cmd) + gd->reloc_off;
461 #if DEBUG_COMMANDS
462 		printf("Command \"%s\": 0x%08lx => 0x%08lx\n",
463 		       cmdtp->name, (ulong) (cmdtp->cmd), addr);
464 #endif
465 		cmdtp->cmd =
466 			(int (*)(struct cmd_tbl_s *, int, int, char * const []))addr;
467 		addr = (ulong)(cmdtp->name) + gd->reloc_off;
468 		cmdtp->name = (char *)addr;
469 		if (cmdtp->usage) {
470 			addr = (ulong)(cmdtp->usage) + gd->reloc_off;
471 			cmdtp->usage = (char *)addr;
472 		}
473 #ifdef	CONFIG_SYS_LONGHELP
474 		if (cmdtp->help) {
475 			addr = (ulong)(cmdtp->help) + gd->reloc_off;
476 			cmdtp->help = (char *)addr;
477 		}
478 #endif
479 #ifdef CONFIG_AUTO_COMPLETE
480 		if (cmdtp->complete) {
481 			addr = (ulong)(cmdtp->complete) + gd->reloc_off;
482 			cmdtp->complete =
483 				(int (*)(int, char * const [], char, int, char * []))addr;
484 		}
485 #endif
486 		cmdtp++;
487 	}
488 }
489 #endif
490