blob: dcbacc9afc019aaddeb2e243367a3fea1f437fab [file] [log] [blame]
wdenkc6097192002-11-03 00:24:07 +00001/*
2 * (C) Copyright 2000
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4 *
Wolfgang Denk4c9ca782006-07-21 11:33:45 +02005 * Add to readline cmdline-editing by
6 * (C) Copyright 2005
7 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
8 *
wdenkc6097192002-11-03 00:24:07 +00009 * See file CREDITS for list of people who contributed to this
10 * project.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 * MA 02111-1307 USA
26 */
27
wdenk9dd2b882002-12-03 21:28:10 +000028/* #define DEBUG */
29
wdenkc6097192002-11-03 00:24:07 +000030#include <common.h>
31#include <watchdog.h>
32#include <command.h>
Wolfgang Denkb0723792005-09-30 15:18:23 +020033#ifdef CONFIG_MODEM_SUPPORT
34#include <malloc.h> /* for free() prototype */
35#endif
wdenkc6097192002-11-03 00:24:07 +000036
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020037#ifdef CONFIG_SYS_HUSH_PARSER
wdenkc6097192002-11-03 00:24:07 +000038#include <hush.h>
39#endif
40
wdenk0a658552003-08-05 17:43:17 +000041#include <post.h>
42
James Yangb83983f2008-05-05 10:22:53 -050043#if defined(CONFIG_SILENT_CONSOLE) || defined(CONFIG_POST) || defined(CONFIG_CMDLINE_EDITING)
Wolfgang Denk6405a152006-03-31 18:32:53 +020044DECLARE_GLOBAL_DATA_PTR;
45#endif
46
Heiko Schocher8a8ec532007-07-13 09:54:17 +020047/*
48 * Board-specific Platform code can reimplement show_boot_progress () if needed
49 */
50void inline __show_boot_progress (int val) {}
Emil Medveace7cc82009-05-12 13:48:32 -050051void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress")));
Heiko Schocher8a8ec532007-07-13 09:54:17 +020052
Bartlomiej Siekae273e9f2008-10-01 15:26:31 +020053#if defined(CONFIG_UPDATE_TFTP)
54void update_tftp (void);
55#endif /* CONFIG_UPDATE_TFTP */
wdenk57b2d802003-06-27 21:31:46 +000056
wdenkc6097192002-11-03 00:24:07 +000057#define MAX_DELAY_STOP_STR 32
58
wdenkc6097192002-11-03 00:24:07 +000059#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
60static int abortboot(int);
61#endif
62
63#undef DEBUG_PARSER
64
John Schmollerc01e7202010-03-12 09:49:23 -060065char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
wdenkc6097192002-11-03 00:24:07 +000066
Stefan Roese59ff5a52006-07-27 16:11:19 +020067static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen);
Mike Frysingerd5c319c2010-12-22 09:40:45 -050068static const char erase_seq[] = "\b \b"; /* erase sequence */
69static const char tab_seq[] = " "; /* used to expand TABs */
wdenkc6097192002-11-03 00:24:07 +000070
71#ifdef CONFIG_BOOT_RETRY_TIME
72static uint64_t endtime = 0; /* must be set, default is instant timeout */
73static int retry_time = -1; /* -1 so can call readline before main_loop */
74#endif
75
76#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk())
77
78#ifndef CONFIG_BOOT_RETRY_MIN
79#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME
80#endif
81
82#ifdef CONFIG_MODEM_SUPPORT
83int do_mdm_init = 0;
84extern void mdm_init(void); /* defined in board.c */
85#endif
86
87/***************************************************************************
88 * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
89 * returns: 0 - no key string, allow autoboot
90 * 1 - got key string, abort
91 */
92#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
93# if defined(CONFIG_AUTOBOOT_KEYED)
94static __inline__ int abortboot(int bootdelay)
95{
96 int abort = 0;
97 uint64_t etime = endtick(bootdelay);
Wolfgang Denk9c82c682006-10-28 00:38:39 +020098 struct {
wdenkc6097192002-11-03 00:24:07 +000099 char* str;
100 u_int len;
101 int retry;
102 }
Wolfgang Denk9c82c682006-10-28 00:38:39 +0200103 delaykey [] = {
wdenkc6097192002-11-03 00:24:07 +0000104 { str: getenv ("bootdelaykey"), retry: 1 },
105 { str: getenv ("bootdelaykey2"), retry: 1 },
106 { str: getenv ("bootstopkey"), retry: 0 },
107 { str: getenv ("bootstopkey2"), retry: 0 },
108 };
109
110 char presskey [MAX_DELAY_STOP_STR];
111 u_int presskey_len = 0;
112 u_int presskey_max = 0;
113 u_int i;
114
115# ifdef CONFIG_AUTOBOOT_PROMPT
Stefan Roese37628252008-08-06 14:05:38 +0200116 printf(CONFIG_AUTOBOOT_PROMPT);
wdenkc6097192002-11-03 00:24:07 +0000117# endif
118
119# ifdef CONFIG_AUTOBOOT_DELAY_STR
120 if (delaykey[0].str == NULL)
121 delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
122# endif
123# ifdef CONFIG_AUTOBOOT_DELAY_STR2
124 if (delaykey[1].str == NULL)
125 delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2;
126# endif
127# ifdef CONFIG_AUTOBOOT_STOP_STR
128 if (delaykey[2].str == NULL)
129 delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR;
130# endif
131# ifdef CONFIG_AUTOBOOT_STOP_STR2
132 if (delaykey[3].str == NULL)
133 delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2;
134# endif
135
136 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
137 delaykey[i].len = delaykey[i].str == NULL ?
138 0 : strlen (delaykey[i].str);
139 delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
140 MAX_DELAY_STOP_STR : delaykey[i].len;
141
142 presskey_max = presskey_max > delaykey[i].len ?
143 presskey_max : delaykey[i].len;
144
145# if DEBUG_BOOTKEYS
146 printf("%s key:<%s>\n",
147 delaykey[i].retry ? "delay" : "stop",
148 delaykey[i].str ? delaykey[i].str : "NULL");
149# endif
150 }
151
152 /* In order to keep up with incoming data, check timeout only
153 * when catch up.
154 */
Peter Korsgaard6499fd62008-12-10 16:24:16 +0100155 do {
156 if (tstc()) {
157 if (presskey_len < presskey_max) {
158 presskey [presskey_len ++] = getc();
159 }
160 else {
161 for (i = 0; i < presskey_max - 1; i ++)
162 presskey [i] = presskey [i + 1];
163
164 presskey [i] = getc();
165 }
166 }
167
wdenkc6097192002-11-03 00:24:07 +0000168 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
169 if (delaykey[i].len > 0 &&
170 presskey_len >= delaykey[i].len &&
171 memcmp (presskey + presskey_len - delaykey[i].len,
wdenk57b2d802003-06-27 21:31:46 +0000172 delaykey[i].str,
wdenkc6097192002-11-03 00:24:07 +0000173 delaykey[i].len) == 0) {
174# if DEBUG_BOOTKEYS
175 printf("got %skey\n",
176 delaykey[i].retry ? "delay" : "stop");
177# endif
178
179# ifdef CONFIG_BOOT_RETRY_TIME
180 /* don't retry auto boot */
181 if (! delaykey[i].retry)
182 retry_time = -1;
183# endif
184 abort = 1;
185 }
186 }
Peter Korsgaard6499fd62008-12-10 16:24:16 +0100187 } while (!abort && get_ticks() <= etime);
wdenkc6097192002-11-03 00:24:07 +0000188
wdenkc6097192002-11-03 00:24:07 +0000189# if DEBUG_BOOTKEYS
190 if (!abort)
Ladislav Michl83fc7e52007-04-25 16:01:26 +0200191 puts("key timeout\n");
wdenkc6097192002-11-03 00:24:07 +0000192# endif
193
dzu6605c842003-10-24 13:14:45 +0000194#ifdef CONFIG_SILENT_CONSOLE
Ladislav Michl6c6a38a2007-04-25 16:01:26 +0200195 if (abort)
196 gd->flags &= ~GD_FLG_SILENT;
dzu6605c842003-10-24 13:14:45 +0000197#endif
198
wdenkc6097192002-11-03 00:24:07 +0000199 return abort;
200}
201
202# else /* !defined(CONFIG_AUTOBOOT_KEYED) */
203
wdenk452cfd62002-11-19 11:04:11 +0000204#ifdef CONFIG_MENUKEY
205static int menukey = 0;
206#endif
207
wdenkc6097192002-11-03 00:24:07 +0000208static __inline__ int abortboot(int bootdelay)
209{
210 int abort = 0;
211
wdenk452cfd62002-11-19 11:04:11 +0000212#ifdef CONFIG_MENUPROMPT
Stefan Roese37628252008-08-06 14:05:38 +0200213 printf(CONFIG_MENUPROMPT);
wdenk452cfd62002-11-19 11:04:11 +0000214#else
wdenkc6097192002-11-03 00:24:07 +0000215 printf("Hit any key to stop autoboot: %2d ", bootdelay);
wdenk452cfd62002-11-19 11:04:11 +0000216#endif
wdenkc6097192002-11-03 00:24:07 +0000217
218#if defined CONFIG_ZERO_BOOTDELAY_CHECK
wdenk57b2d802003-06-27 21:31:46 +0000219 /*
220 * Check if key already pressed
221 * Don't check if bootdelay < 0
222 */
wdenkc6097192002-11-03 00:24:07 +0000223 if (bootdelay >= 0) {
224 if (tstc()) { /* we got a key press */
225 (void) getc(); /* consume input */
wdenk42c05472004-03-23 22:14:11 +0000226 puts ("\b\b\b 0");
Ladislav Michl6c6a38a2007-04-25 16:01:26 +0200227 abort = 1; /* don't auto boot */
wdenkc6097192002-11-03 00:24:07 +0000228 }
wdenk57b2d802003-06-27 21:31:46 +0000229 }
wdenkc6097192002-11-03 00:24:07 +0000230#endif
231
wdenk808532a2003-10-10 10:05:42 +0000232 while ((bootdelay > 0) && (!abort)) {
wdenkc6097192002-11-03 00:24:07 +0000233 int i;
234
235 --bootdelay;
236 /* delay 100 * 10ms */
237 for (i=0; !abort && i<100; ++i) {
238 if (tstc()) { /* we got a key press */
239 abort = 1; /* don't auto boot */
240 bootdelay = 0; /* no more delay */
wdenk452cfd62002-11-19 11:04:11 +0000241# ifdef CONFIG_MENUKEY
242 menukey = getc();
243# else
wdenkc6097192002-11-03 00:24:07 +0000244 (void) getc(); /* consume input */
wdenk452cfd62002-11-19 11:04:11 +0000245# endif
wdenkc6097192002-11-03 00:24:07 +0000246 break;
247 }
Ladislav Michl83fc7e52007-04-25 16:01:26 +0200248 udelay(10000);
wdenkc6097192002-11-03 00:24:07 +0000249 }
250
Ladislav Michl83fc7e52007-04-25 16:01:26 +0200251 printf("\b\b\b%2d ", bootdelay);
wdenkc6097192002-11-03 00:24:07 +0000252 }
253
Ladislav Michl83fc7e52007-04-25 16:01:26 +0200254 putc('\n');
wdenkc6097192002-11-03 00:24:07 +0000255
wdenk808532a2003-10-10 10:05:42 +0000256#ifdef CONFIG_SILENT_CONSOLE
Ladislav Michl6c6a38a2007-04-25 16:01:26 +0200257 if (abort)
258 gd->flags &= ~GD_FLG_SILENT;
wdenk808532a2003-10-10 10:05:42 +0000259#endif
260
wdenkc6097192002-11-03 00:24:07 +0000261 return abort;
262}
263# endif /* CONFIG_AUTOBOOT_KEYED */
264#endif /* CONFIG_BOOTDELAY >= 0 */
265
266/****************************************************************************/
267
268void main_loop (void)
269{
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200270#ifndef CONFIG_SYS_HUSH_PARSER
271 static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
wdenkc6097192002-11-03 00:24:07 +0000272 int len;
273 int rc = 1;
274 int flag;
275#endif
276
277#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
278 char *s;
279 int bootdelay;
280#endif
281#ifdef CONFIG_PREBOOT
282 char *p;
283#endif
wdenk0a658552003-08-05 17:43:17 +0000284#ifdef CONFIG_BOOTCOUNT_LIMIT
285 unsigned long bootcount = 0;
286 unsigned long bootlimit = 0;
287 char *bcs;
288 char bcs_set[16];
289#endif /* CONFIG_BOOTCOUNT_LIMIT */
wdenkc6097192002-11-03 00:24:07 +0000290
291#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
292 ulong bmp = 0; /* default bitmap */
293 extern int trab_vfd (ulong bitmap);
294
295#ifdef CONFIG_MODEM_SUPPORT
296 if (do_mdm_init)
297 bmp = 1; /* alternate bitmap */
298#endif
299 trab_vfd (bmp);
300#endif /* CONFIG_VFD && VFD_TEST_LOGO */
301
wdenk0a658552003-08-05 17:43:17 +0000302#ifdef CONFIG_BOOTCOUNT_LIMIT
303 bootcount = bootcount_load();
304 bootcount++;
305 bootcount_store (bootcount);
306 sprintf (bcs_set, "%lu", bootcount);
307 setenv ("bootcount", bcs_set);
308 bcs = getenv ("bootlimit");
309 bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
310#endif /* CONFIG_BOOTCOUNT_LIMIT */
311
wdenkc6097192002-11-03 00:24:07 +0000312#ifdef CONFIG_MODEM_SUPPORT
313 debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
314 if (do_mdm_init) {
Wolfgang Denk7fb52662005-10-13 16:45:02 +0200315 char *str = strdup(getenv("mdm_cmd"));
wdenkc6097192002-11-03 00:24:07 +0000316 setenv ("preboot", str); /* set or delete definition */
317 if (str != NULL)
318 free (str);
319 mdm_init(); /* wait for modem connection */
320 }
321#endif /* CONFIG_MODEM_SUPPORT */
322
stroesef8105cf2003-04-04 15:44:49 +0000323#ifdef CONFIG_VERSION_VARIABLE
324 {
325 extern char version_string[];
stroesef8105cf2003-04-04 15:44:49 +0000326
stroeseb9ab27c2003-07-11 08:00:33 +0000327 setenv ("ver", version_string); /* set version variable */
stroesef8105cf2003-04-04 15:44:49 +0000328 }
329#endif /* CONFIG_VERSION_VARIABLE */
330
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200331#ifdef CONFIG_SYS_HUSH_PARSER
wdenkc6097192002-11-03 00:24:07 +0000332 u_boot_hush_start ();
333#endif
334
Heiko Schocher49ebcb52008-10-15 09:40:28 +0200335#if defined(CONFIG_HUSH_INIT_VAR)
336 hush_init_var ();
337#endif
338
wdenkc6097192002-11-03 00:24:07 +0000339#ifdef CONFIG_PREBOOT
340 if ((p = getenv ("preboot")) != NULL) {
341# ifdef CONFIG_AUTOBOOT_KEYED
342 int prev = disable_ctrlc(1); /* disable Control C checking */
343# endif
344
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200345# ifndef CONFIG_SYS_HUSH_PARSER
wdenkc6097192002-11-03 00:24:07 +0000346 run_command (p, 0);
347# else
348 parse_string_outer(p, FLAG_PARSE_SEMICOLON |
349 FLAG_EXIT_FROM_LOOP);
350# endif
351
352# ifdef CONFIG_AUTOBOOT_KEYED
353 disable_ctrlc(prev); /* restore Control C checking */
354# endif
355 }
356#endif /* CONFIG_PREBOOT */
357
Wolfgang Denkce2dd6e2010-03-11 23:56:03 +0100358#if defined(CONFIG_UPDATE_TFTP)
359 update_tftp ();
360#endif /* CONFIG_UPDATE_TFTP */
361
wdenkc6097192002-11-03 00:24:07 +0000362#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
363 s = getenv ("bootdelay");
364 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
365
wdenk9dd2b882002-12-03 21:28:10 +0000366 debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
wdenkc6097192002-11-03 00:24:07 +0000367
368# ifdef CONFIG_BOOT_RETRY_TIME
wdenkb00ec162003-06-19 23:40:20 +0000369 init_cmd_timeout ();
wdenkc6097192002-11-03 00:24:07 +0000370# endif /* CONFIG_BOOT_RETRY_TIME */
371
Yuri Tikhonovd773efb2008-02-04 14:11:03 +0100372#ifdef CONFIG_POST
373 if (gd->flags & GD_FLG_POSTFAIL) {
374 s = getenv("failbootcmd");
375 }
376 else
377#endif /* CONFIG_POST */
wdenk0a658552003-08-05 17:43:17 +0000378#ifdef CONFIG_BOOTCOUNT_LIMIT
379 if (bootlimit && (bootcount > bootlimit)) {
380 printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
381 (unsigned)bootlimit);
382 s = getenv ("altbootcmd");
383 }
384 else
385#endif /* CONFIG_BOOTCOUNT_LIMIT */
386 s = getenv ("bootcmd");
wdenk9dd2b882002-12-03 21:28:10 +0000387
388 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
389
wdenkc6097192002-11-03 00:24:07 +0000390 if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
391# ifdef CONFIG_AUTOBOOT_KEYED
392 int prev = disable_ctrlc(1); /* disable Control C checking */
393# endif
394
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200395# ifndef CONFIG_SYS_HUSH_PARSER
wdenkc6097192002-11-03 00:24:07 +0000396 run_command (s, 0);
397# else
398 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
399 FLAG_EXIT_FROM_LOOP);
400# endif
401
402# ifdef CONFIG_AUTOBOOT_KEYED
403 disable_ctrlc(prev); /* restore Control C checking */
404# endif
405 }
wdenk452cfd62002-11-19 11:04:11 +0000406
407# ifdef CONFIG_MENUKEY
wdenk9dd2b882002-12-03 21:28:10 +0000408 if (menukey == CONFIG_MENUKEY) {
wdenk452cfd62002-11-19 11:04:11 +0000409 s = getenv("menucmd");
wdenk9dd2b882002-12-03 21:28:10 +0000410 if (s) {
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200411# ifndef CONFIG_SYS_HUSH_PARSER
wdenk91a4f362005-01-09 23:33:49 +0000412 run_command (s, 0);
wdenk452cfd62002-11-19 11:04:11 +0000413# else
414 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
415 FLAG_EXIT_FROM_LOOP);
416# endif
417 }
418 }
419#endif /* CONFIG_MENUKEY */
Wolfgang Denkb0b104a2010-06-13 18:28:54 +0200420#endif /* CONFIG_BOOTDELAY */
wdenk452cfd62002-11-19 11:04:11 +0000421
wdenkc6097192002-11-03 00:24:07 +0000422 /*
423 * Main Loop for Monitor Command Processing
424 */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200425#ifdef CONFIG_SYS_HUSH_PARSER
wdenkc6097192002-11-03 00:24:07 +0000426 parse_file_outer();
427 /* This point is never reached */
428 for (;;);
429#else
430 for (;;) {
431#ifdef CONFIG_BOOT_RETRY_TIME
432 if (rc >= 0) {
433 /* Saw enough of a valid command to
434 * restart the timeout.
435 */
436 reset_cmd_timeout();
437 }
438#endif
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200439 len = readline (CONFIG_SYS_PROMPT);
wdenkc6097192002-11-03 00:24:07 +0000440
441 flag = 0; /* assume no special flags for now */
442 if (len > 0)
443 strcpy (lastcommand, console_buffer);
444 else if (len == 0)
445 flag |= CMD_FLAG_REPEAT;
446#ifdef CONFIG_BOOT_RETRY_TIME
447 else if (len == -2) {
448 /* -2 means timed out, retry autoboot
449 */
wdenk42c05472004-03-23 22:14:11 +0000450 puts ("\nTimed out waiting for command\n");
wdenkc6097192002-11-03 00:24:07 +0000451# ifdef CONFIG_RESET_TO_RETRY
452 /* Reinit board to run initialization code again */
453 do_reset (NULL, 0, 0, NULL);
454# else
455 return; /* retry autoboot */
456# endif
457 }
458#endif
459
460 if (len == -1)
wdenk42c05472004-03-23 22:14:11 +0000461 puts ("<INTERRUPT>\n");
wdenkc6097192002-11-03 00:24:07 +0000462 else
463 rc = run_command (lastcommand, flag);
464
465 if (rc <= 0) {
466 /* invalid command or not repeatable, forget it */
467 lastcommand[0] = 0;
468 }
469 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200470#endif /*CONFIG_SYS_HUSH_PARSER*/
wdenkc6097192002-11-03 00:24:07 +0000471}
472
wdenkb00ec162003-06-19 23:40:20 +0000473#ifdef CONFIG_BOOT_RETRY_TIME
474/***************************************************************************
Wolfgang Denk9c82c682006-10-28 00:38:39 +0200475 * initialize command line timeout
wdenkb00ec162003-06-19 23:40:20 +0000476 */
477void init_cmd_timeout(void)
478{
479 char *s = getenv ("bootretry");
480
481 if (s != NULL)
wdenk444f22b2003-12-07 21:39:28 +0000482 retry_time = (int)simple_strtol(s, NULL, 10);
wdenkb00ec162003-06-19 23:40:20 +0000483 else
484 retry_time = CONFIG_BOOT_RETRY_TIME;
485
486 if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN)
487 retry_time = CONFIG_BOOT_RETRY_MIN;
488}
489
wdenkc6097192002-11-03 00:24:07 +0000490/***************************************************************************
491 * reset command line timeout to retry_time seconds
492 */
wdenkc6097192002-11-03 00:24:07 +0000493void reset_cmd_timeout(void)
494{
495 endtime = endtick(retry_time);
496}
497#endif
498
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200499#ifdef CONFIG_CMDLINE_EDITING
500
501/*
502 * cmdline-editing related codes from vivi.
503 * Author: Janghoon Lyu <nandy@mizi.com>
504 */
505
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200506#define putnstr(str,n) do { \
Andrew Klossnere4ad4542008-07-07 06:41:14 -0700507 printf ("%.*s", (int)n, str); \
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200508 } while (0)
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200509
510#define CTL_CH(c) ((c) - 'a' + 1)
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200511#define CTL_BACKSPACE ('\b')
512#define DEL ((char)255)
513#define DEL7 ((char)127)
514#define CREAD_HIST_CHAR ('!')
515
516#define getcmd_putch(ch) putc(ch)
517#define getcmd_getch() getc()
518#define getcmd_cbeep() getcmd_putch('\a')
519
520#define HIST_MAX 20
Peter Tyser7e0e5ca2010-09-29 13:30:56 -0500521#define HIST_SIZE CONFIG_SYS_CBSIZE
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200522
523static int hist_max = 0;
524static int hist_add_idx = 0;
525static int hist_cur = -1;
526unsigned hist_num = 0;
527
528char* hist_list[HIST_MAX];
John Schmollerc01e7202010-03-12 09:49:23 -0600529char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200530
531#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
532
533static void hist_init(void)
534{
535 int i;
536
537 hist_max = 0;
538 hist_add_idx = 0;
539 hist_cur = -1;
540 hist_num = 0;
541
542 for (i = 0; i < HIST_MAX; i++) {
543 hist_list[i] = hist_lines[i];
544 hist_list[i][0] = '\0';
545 }
546}
547
548static void cread_add_to_hist(char *line)
549{
550 strcpy(hist_list[hist_add_idx], line);
551
552 if (++hist_add_idx >= HIST_MAX)
553 hist_add_idx = 0;
554
555 if (hist_add_idx > hist_max)
556 hist_max = hist_add_idx;
557
558 hist_num++;
559}
560
561static char* hist_prev(void)
562{
563 char *ret;
564 int old_cur;
565
566 if (hist_cur < 0)
567 return NULL;
568
569 old_cur = hist_cur;
570 if (--hist_cur < 0)
571 hist_cur = hist_max;
572
573 if (hist_cur == hist_add_idx) {
574 hist_cur = old_cur;
575 ret = NULL;
576 } else
577 ret = hist_list[hist_cur];
578
579 return (ret);
580}
581
582static char* hist_next(void)
583{
584 char *ret;
585
586 if (hist_cur < 0)
587 return NULL;
588
589 if (hist_cur == hist_add_idx)
590 return NULL;
591
592 if (++hist_cur > hist_max)
593 hist_cur = 0;
594
595 if (hist_cur == hist_add_idx) {
596 ret = "";
597 } else
598 ret = hist_list[hist_cur];
599
600 return (ret);
601}
602
Stefan Roese59ff5a52006-07-27 16:11:19 +0200603#ifndef CONFIG_CMDLINE_EDITING
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200604static void cread_print_hist_list(void)
605{
606 int i;
607 unsigned long n;
608
609 n = hist_num - hist_max;
610
611 i = hist_add_idx + 1;
612 while (1) {
613 if (i > hist_max)
614 i = 0;
615 if (i == hist_add_idx)
616 break;
617 printf("%s\n", hist_list[i]);
618 n++;
619 i++;
620 }
621}
Stefan Roese59ff5a52006-07-27 16:11:19 +0200622#endif /* CONFIG_CMDLINE_EDITING */
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200623
624#define BEGINNING_OF_LINE() { \
625 while (num) { \
626 getcmd_putch(CTL_BACKSPACE); \
627 num--; \
628 } \
629}
630
631#define ERASE_TO_EOL() { \
632 if (num < eol_num) { \
Mike Frysinger3952bea2010-07-23 05:28:15 -0400633 printf("%*s", (int)(eol_num - num), ""); \
634 do { \
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200635 getcmd_putch(CTL_BACKSPACE); \
Mike Frysinger3952bea2010-07-23 05:28:15 -0400636 } while (--eol_num > num); \
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200637 } \
638}
639
640#define REFRESH_TO_EOL() { \
641 if (num < eol_num) { \
642 wlen = eol_num - num; \
643 putnstr(buf + num, wlen); \
644 num = eol_num; \
645 } \
646}
647
648static void cread_add_char(char ichar, int insert, unsigned long *num,
649 unsigned long *eol_num, char *buf, unsigned long len)
650{
651 unsigned long wlen;
652
653 /* room ??? */
654 if (insert || *num == *eol_num) {
655 if (*eol_num > len - 1) {
656 getcmd_cbeep();
657 return;
658 }
659 (*eol_num)++;
660 }
661
662 if (insert) {
663 wlen = *eol_num - *num;
664 if (wlen > 1) {
665 memmove(&buf[*num+1], &buf[*num], wlen-1);
666 }
667
668 buf[*num] = ichar;
669 putnstr(buf + *num, wlen);
670 (*num)++;
671 while (--wlen) {
672 getcmd_putch(CTL_BACKSPACE);
673 }
674 } else {
675 /* echo the character */
676 wlen = 1;
677 buf[*num] = ichar;
678 putnstr(buf + *num, wlen);
679 (*num)++;
680 }
681}
682
683static void cread_add_str(char *str, int strsize, int insert, unsigned long *num,
684 unsigned long *eol_num, char *buf, unsigned long len)
685{
686 while (strsize--) {
687 cread_add_char(*str, insert, num, eol_num, buf, len);
688 str++;
689 }
690}
691
Jean-Christophe PLAGNIOL-VILLARD6366f002007-12-22 15:52:58 +0100692static int cread_line(const char *const prompt, char *buf, unsigned int *len)
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200693{
694 unsigned long num = 0;
695 unsigned long eol_num = 0;
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200696 unsigned long wlen;
697 char ichar;
698 int insert = 1;
699 int esc_len = 0;
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200700 char esc_save[8];
Peter Tyser8ad15a92009-10-25 15:12:54 -0500701 int init_len = strlen(buf);
702
703 if (init_len)
704 cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200705
706 while (1) {
Andreas Engel3a0ba102008-01-09 17:10:56 +0100707#ifdef CONFIG_BOOT_RETRY_TIME
708 while (!tstc()) { /* while no incoming data */
709 if (retry_time >= 0 && get_ticks() > endtime)
710 return (-2); /* timed out */
Jens Scharsig11247552010-04-09 19:02:38 +0200711 WATCHDOG_RESET();
Andreas Engel3a0ba102008-01-09 17:10:56 +0100712 }
713#endif
714
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200715 ichar = getcmd_getch();
716
717 if ((ichar == '\n') || (ichar == '\r')) {
Wolfgang Denk92549de2006-07-21 11:34:34 +0200718 putc('\n');
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200719 break;
720 }
721
722 /*
723 * handle standard linux xterm esc sequences for arrow key, etc.
724 */
725 if (esc_len != 0) {
726 if (esc_len == 1) {
727 if (ichar == '[') {
728 esc_save[esc_len] = ichar;
729 esc_len = 2;
730 } else {
731 cread_add_str(esc_save, esc_len, insert,
732 &num, &eol_num, buf, *len);
733 esc_len = 0;
734 }
735 continue;
736 }
737
738 switch (ichar) {
739
740 case 'D': /* <- key */
741 ichar = CTL_CH('b');
742 esc_len = 0;
743 break;
744 case 'C': /* -> key */
745 ichar = CTL_CH('f');
746 esc_len = 0;
747 break; /* pass off to ^F handler */
748 case 'H': /* Home key */
749 ichar = CTL_CH('a');
750 esc_len = 0;
751 break; /* pass off to ^A handler */
752 case 'A': /* up arrow */
753 ichar = CTL_CH('p');
754 esc_len = 0;
755 break; /* pass off to ^P handler */
756 case 'B': /* down arrow */
757 ichar = CTL_CH('n');
758 esc_len = 0;
759 break; /* pass off to ^N handler */
760 default:
761 esc_save[esc_len++] = ichar;
762 cread_add_str(esc_save, esc_len, insert,
763 &num, &eol_num, buf, *len);
764 esc_len = 0;
765 continue;
766 }
767 }
768
769 switch (ichar) {
770 case 0x1b:
771 if (esc_len == 0) {
772 esc_save[esc_len] = ichar;
773 esc_len = 1;
774 } else {
Wolfgang Denk92549de2006-07-21 11:34:34 +0200775 puts("impossible condition #876\n");
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200776 esc_len = 0;
777 }
778 break;
779
780 case CTL_CH('a'):
781 BEGINNING_OF_LINE();
782 break;
783 case CTL_CH('c'): /* ^C - break */
784 *buf = '\0'; /* discard input */
785 return (-1);
786 case CTL_CH('f'):
787 if (num < eol_num) {
788 getcmd_putch(buf[num]);
789 num++;
790 }
791 break;
792 case CTL_CH('b'):
793 if (num) {
794 getcmd_putch(CTL_BACKSPACE);
795 num--;
796 }
797 break;
798 case CTL_CH('d'):
799 if (num < eol_num) {
800 wlen = eol_num - num - 1;
801 if (wlen) {
802 memmove(&buf[num], &buf[num+1], wlen);
803 putnstr(buf + num, wlen);
804 }
805
806 getcmd_putch(' ');
807 do {
808 getcmd_putch(CTL_BACKSPACE);
809 } while (wlen--);
810 eol_num--;
811 }
812 break;
813 case CTL_CH('k'):
814 ERASE_TO_EOL();
815 break;
816 case CTL_CH('e'):
817 REFRESH_TO_EOL();
818 break;
819 case CTL_CH('o'):
820 insert = !insert;
821 break;
822 case CTL_CH('x'):
Jean-Christophe PLAGNIOL-VILLARD6366f002007-12-22 15:52:58 +0100823 case CTL_CH('u'):
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200824 BEGINNING_OF_LINE();
825 ERASE_TO_EOL();
826 break;
827 case DEL:
828 case DEL7:
829 case 8:
830 if (num) {
831 wlen = eol_num - num;
832 num--;
833 memmove(&buf[num], &buf[num+1], wlen);
834 getcmd_putch(CTL_BACKSPACE);
835 putnstr(buf + num, wlen);
836 getcmd_putch(' ');
837 do {
838 getcmd_putch(CTL_BACKSPACE);
839 } while (wlen--);
840 eol_num--;
841 }
842 break;
843 case CTL_CH('p'):
844 case CTL_CH('n'):
845 {
846 char * hline;
847
848 esc_len = 0;
849
850 if (ichar == CTL_CH('p'))
851 hline = hist_prev();
852 else
853 hline = hist_next();
854
855 if (!hline) {
856 getcmd_cbeep();
857 continue;
858 }
859
860 /* nuke the current line */
861 /* first, go home */
862 BEGINNING_OF_LINE();
863
864 /* erase to end of line */
865 ERASE_TO_EOL();
866
867 /* copy new line into place and display */
868 strcpy(buf, hline);
869 eol_num = strlen(buf);
870 REFRESH_TO_EOL();
871 continue;
872 }
Jean-Christophe PLAGNIOL-VILLARD6366f002007-12-22 15:52:58 +0100873#ifdef CONFIG_AUTO_COMPLETE
874 case '\t': {
875 int num2, col;
876
877 /* do not autocomplete when in the middle */
878 if (num < eol_num) {
879 getcmd_cbeep();
880 break;
881 }
882
883 buf[num] = '\0';
884 col = strlen(prompt) + eol_num;
885 num2 = num;
886 if (cmd_auto_complete(prompt, buf, &num2, &col)) {
887 col = num2 - num;
888 num += col;
889 eol_num += col;
890 }
891 break;
892 }
893#endif
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200894 default:
895 cread_add_char(ichar, insert, &num, &eol_num, buf, *len);
896 break;
897 }
898 }
899 *len = eol_num;
900 buf[eol_num] = '\0'; /* lose the newline */
901
902 if (buf[0] && buf[0] != CREAD_HIST_CHAR)
903 cread_add_to_hist(buf);
904 hist_cur = hist_add_idx;
905
Peter Tyser03e46c42009-10-25 15:12:53 -0500906 return 0;
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200907}
908
909#endif /* CONFIG_CMDLINE_EDITING */
910
wdenkc6097192002-11-03 00:24:07 +0000911/****************************************************************************/
912
913/*
914 * Prompt for input and read a line.
915 * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
916 * time out when time goes past endtime (timebase time in ticks).
917 * Return: number of read characters
918 * -1 if break
919 * -2 if timed out
920 */
921int readline (const char *const prompt)
922{
Peter Tyser8ad15a92009-10-25 15:12:54 -0500923 /*
924 * If console_buffer isn't 0-length the user will be prompted to modify
925 * it instead of entering it from scratch as desired.
926 */
927 console_buffer[0] = '\0';
928
James Yang20ffa972008-01-09 11:17:49 -0600929 return readline_into_buffer(prompt, console_buffer);
930}
931
932
933int readline_into_buffer (const char *const prompt, char * buffer)
934{
935 char *p = buffer;
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200936#ifdef CONFIG_CMDLINE_EDITING
Peter Tyser7e0e5ca2010-09-29 13:30:56 -0500937 unsigned int len = CONFIG_SYS_CBSIZE;
Stefan Roesedc5ff702006-08-07 15:08:44 +0200938 int rc;
Wolfgang Denk4c9ca782006-07-21 11:33:45 +0200939 static int initted = 0;
940
James Yangb83983f2008-05-05 10:22:53 -0500941 /*
942 * History uses a global array which is not
943 * writable until after relocation to RAM.
944 * Revert to non-history version if still
945 * running from flash.
946 */
947 if (gd->flags & GD_FLG_RELOC) {
948 if (!initted) {
949 hist_init();
950 initted = 1;
951 }
952
Peter Tyserc144e012009-10-25 15:12:52 -0500953 if (prompt)
954 puts (prompt);
James Yangb83983f2008-05-05 10:22:53 -0500955
956 rc = cread_line(prompt, p, &len);
957 return rc < 0 ? rc : len;
958
959 } else {
960#endif /* CONFIG_CMDLINE_EDITING */
Kumar Galae225b172008-01-10 02:22:05 -0600961 char * p_buf = p;
wdenkc6097192002-11-03 00:24:07 +0000962 int n = 0; /* buffer index */
963 int plen = 0; /* prompt length */
964 int col; /* output column cnt */
965 char c;
966
967 /* print prompt */
968 if (prompt) {
969 plen = strlen (prompt);
970 puts (prompt);
971 }
972 col = plen;
973
974 for (;;) {
975#ifdef CONFIG_BOOT_RETRY_TIME
976 while (!tstc()) { /* while no incoming data */
977 if (retry_time >= 0 && get_ticks() > endtime)
978 return (-2); /* timed out */
Jens Scharsig11247552010-04-09 19:02:38 +0200979 WATCHDOG_RESET();
wdenkc6097192002-11-03 00:24:07 +0000980 }
981#endif
982 WATCHDOG_RESET(); /* Trigger watchdog, if needed */
983
984#ifdef CONFIG_SHOW_ACTIVITY
985 while (!tstc()) {
986 extern void show_activity(int arg);
987 show_activity(0);
Jens Scharsig11247552010-04-09 19:02:38 +0200988 WATCHDOG_RESET();
wdenkc6097192002-11-03 00:24:07 +0000989 }
990#endif
991 c = getc();
992
993 /*
994 * Special character handling
995 */
996 switch (c) {
997 case '\r': /* Enter */
998 case '\n':
999 *p = '\0';
1000 puts ("\r\n");
James Yang20ffa972008-01-09 11:17:49 -06001001 return (p - p_buf);
wdenkc6097192002-11-03 00:24:07 +00001002
wdenk8d4d1f62004-03-23 22:37:33 +00001003 case '\0': /* nul */
1004 continue;
1005
wdenkc6097192002-11-03 00:24:07 +00001006 case 0x03: /* ^C - break */
James Yang20ffa972008-01-09 11:17:49 -06001007 p_buf[0] = '\0'; /* discard input */
wdenkc6097192002-11-03 00:24:07 +00001008 return (-1);
1009
1010 case 0x15: /* ^U - erase line */
1011 while (col > plen) {
1012 puts (erase_seq);
1013 --col;
1014 }
James Yang20ffa972008-01-09 11:17:49 -06001015 p = p_buf;
wdenkc6097192002-11-03 00:24:07 +00001016 n = 0;
1017 continue;
1018
Wolfgang Denk70df7bc2007-06-22 23:59:00 +02001019 case 0x17: /* ^W - erase word */
James Yang20ffa972008-01-09 11:17:49 -06001020 p=delete_char(p_buf, p, &col, &n, plen);
wdenkc6097192002-11-03 00:24:07 +00001021 while ((n > 0) && (*p != ' ')) {
James Yang20ffa972008-01-09 11:17:49 -06001022 p=delete_char(p_buf, p, &col, &n, plen);
wdenkc6097192002-11-03 00:24:07 +00001023 }
1024 continue;
1025
1026 case 0x08: /* ^H - backspace */
1027 case 0x7F: /* DEL - backspace */
James Yang20ffa972008-01-09 11:17:49 -06001028 p=delete_char(p_buf, p, &col, &n, plen);
wdenkc6097192002-11-03 00:24:07 +00001029 continue;
1030
1031 default:
1032 /*
1033 * Must be a normal character then
1034 */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001035 if (n < CONFIG_SYS_CBSIZE-2) {
wdenkc6097192002-11-03 00:24:07 +00001036 if (c == '\t') { /* expand TABs */
wdenk3902d702004-04-15 18:22:41 +00001037#ifdef CONFIG_AUTO_COMPLETE
1038 /* if auto completion triggered just continue */
1039 *p = '\0';
1040 if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
James Yang20ffa972008-01-09 11:17:49 -06001041 p = p_buf + n; /* reset */
wdenk3902d702004-04-15 18:22:41 +00001042 continue;
1043 }
1044#endif
wdenkc6097192002-11-03 00:24:07 +00001045 puts (tab_seq+(col&07));
1046 col += 8 - (col&07);
1047 } else {
1048 ++col; /* echo input */
1049 putc (c);
1050 }
1051 *p++ = c;
1052 ++n;
1053 } else { /* Buffer full */
1054 putc ('\a');
1055 }
1056 }
1057 }
James Yangb83983f2008-05-05 10:22:53 -05001058#ifdef CONFIG_CMDLINE_EDITING
1059 }
1060#endif
wdenkc6097192002-11-03 00:24:07 +00001061}
1062
1063/****************************************************************************/
1064
1065static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
1066{
1067 char *s;
1068
1069 if (*np == 0) {
1070 return (p);
1071 }
1072
1073 if (*(--p) == '\t') { /* will retype the whole line */
1074 while (*colp > plen) {
1075 puts (erase_seq);
1076 (*colp)--;
1077 }
1078 for (s=buffer; s<p; ++s) {
1079 if (*s == '\t') {
1080 puts (tab_seq+((*colp) & 07));
1081 *colp += 8 - ((*colp) & 07);
1082 } else {
1083 ++(*colp);
1084 putc (*s);
1085 }
1086 }
1087 } else {
1088 puts (erase_seq);
1089 (*colp)--;
1090 }
1091 (*np)--;
1092 return (p);
1093}
1094
1095/****************************************************************************/
1096
1097int parse_line (char *line, char *argv[])
1098{
1099 int nargs = 0;
1100
1101#ifdef DEBUG_PARSER
1102 printf ("parse_line: \"%s\"\n", line);
1103#endif
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001104 while (nargs < CONFIG_SYS_MAXARGS) {
wdenkc6097192002-11-03 00:24:07 +00001105
1106 /* skip any white space */
1107 while ((*line == ' ') || (*line == '\t')) {
1108 ++line;
1109 }
1110
1111 if (*line == '\0') { /* end of line, no more args */
1112 argv[nargs] = NULL;
1113#ifdef DEBUG_PARSER
1114 printf ("parse_line: nargs=%d\n", nargs);
1115#endif
1116 return (nargs);
1117 }
1118
1119 argv[nargs++] = line; /* begin of argument string */
1120
1121 /* find end of string */
1122 while (*line && (*line != ' ') && (*line != '\t')) {
1123 ++line;
1124 }
1125
1126 if (*line == '\0') { /* end of line, no more args */
1127 argv[nargs] = NULL;
1128#ifdef DEBUG_PARSER
1129 printf ("parse_line: nargs=%d\n", nargs);
1130#endif
1131 return (nargs);
1132 }
1133
1134 *line++ = '\0'; /* terminate current arg */
1135 }
1136
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001137 printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
wdenkc6097192002-11-03 00:24:07 +00001138
1139#ifdef DEBUG_PARSER
1140 printf ("parse_line: nargs=%d\n", nargs);
1141#endif
1142 return (nargs);
1143}
1144
1145/****************************************************************************/
1146
1147static void process_macros (const char *input, char *output)
1148{
1149 char c, prev;
1150 const char *varname_start = NULL;
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001151 int inputcnt = strlen (input);
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001152 int outputcnt = CONFIG_SYS_CBSIZE;
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001153 int state = 0; /* 0 = waiting for '$' */
1154
1155 /* 1 = waiting for '(' or '{' */
1156 /* 2 = waiting for ')' or '}' */
1157 /* 3 = waiting for ''' */
wdenkc6097192002-11-03 00:24:07 +00001158#ifdef DEBUG_PARSER
1159 char *output_start = output;
1160
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001161 printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input),
1162 input);
wdenkc6097192002-11-03 00:24:07 +00001163#endif
1164
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001165 prev = '\0'; /* previous character */
wdenkc6097192002-11-03 00:24:07 +00001166
1167 while (inputcnt && outputcnt) {
wdenk57b2d802003-06-27 21:31:46 +00001168 c = *input++;
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001169 inputcnt--;
wdenkc6097192002-11-03 00:24:07 +00001170
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001171 if (state != 3) {
1172 /* remove one level of escape characters */
1173 if ((c == '\\') && (prev != '\\')) {
1174 if (inputcnt-- == 0)
1175 break;
1176 prev = c;
1177 c = *input++;
1178 }
wdenkc6097192002-11-03 00:24:07 +00001179 }
wdenkc6097192002-11-03 00:24:07 +00001180
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001181 switch (state) {
1182 case 0: /* Waiting for (unescaped) $ */
1183 if ((c == '\'') && (prev != '\\')) {
1184 state = 3;
1185 break;
1186 }
1187 if ((c == '$') && (prev != '\\')) {
1188 state++;
1189 } else {
wdenkc6097192002-11-03 00:24:07 +00001190 *(output++) = c;
1191 outputcnt--;
1192 }
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001193 break;
1194 case 1: /* Waiting for ( */
1195 if (c == '(' || c == '{') {
1196 state++;
1197 varname_start = input;
1198 } else {
1199 state = 0;
1200 *(output++) = '$';
1201 outputcnt--;
wdenkc6097192002-11-03 00:24:07 +00001202
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001203 if (outputcnt) {
1204 *(output++) = c;
1205 outputcnt--;
1206 }
wdenkc6097192002-11-03 00:24:07 +00001207 }
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001208 break;
1209 case 2: /* Waiting for ) */
1210 if (c == ')' || c == '}') {
1211 int i;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001212 char envname[CONFIG_SYS_CBSIZE], *envval;
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001213 int envcnt = input - varname_start - 1; /* Varname # of chars */
wdenkc6097192002-11-03 00:24:07 +00001214
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001215 /* Get the varname */
1216 for (i = 0; i < envcnt; i++) {
1217 envname[i] = varname_start[i];
wdenkc6097192002-11-03 00:24:07 +00001218 }
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001219 envname[i] = 0;
1220
1221 /* Get its value */
1222 envval = getenv (envname);
1223
1224 /* Copy into the line if it exists */
1225 if (envval != NULL)
1226 while ((*envval) && outputcnt) {
1227 *(output++) = *(envval++);
1228 outputcnt--;
1229 }
1230 /* Look for another '$' */
1231 state = 0;
1232 }
1233 break;
1234 case 3: /* Waiting for ' */
1235 if ((c == '\'') && (prev != '\\')) {
1236 state = 0;
1237 } else {
1238 *(output++) = c;
1239 outputcnt--;
1240 }
1241 break;
wdenkc6097192002-11-03 00:24:07 +00001242 }
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001243 prev = c;
wdenkc6097192002-11-03 00:24:07 +00001244 }
1245
1246 if (outputcnt)
1247 *output = 0;
Bartlomiej Siekacadc8e82007-05-27 17:04:18 +02001248 else
1249 *(output - 1) = 0;
wdenkc6097192002-11-03 00:24:07 +00001250
1251#ifdef DEBUG_PARSER
1252 printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
Wolfgang Denk9c82c682006-10-28 00:38:39 +02001253 strlen (output_start), output_start);
wdenkc6097192002-11-03 00:24:07 +00001254#endif
1255}
1256
1257/****************************************************************************
1258 * returns:
1259 * 1 - command executed, repeatable
1260 * 0 - command executed but not repeatable, interrupted commands are
1261 * always considered not repeatable
1262 * -1 - not executed (unrecognized, bootd recursion or too many args)
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001263 * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is
wdenkc6097192002-11-03 00:24:07 +00001264 * considered unrecognized)
1265 *
1266 * WARNING:
1267 *
1268 * We must create a temporary copy of the command since the command we get
1269 * may be the result from getenv(), which returns a pointer directly to
1270 * the environment data, which may change magicly when the command we run
1271 * creates or modifies environment variables (like "bootp" does).
1272 */
1273
1274int run_command (const char *cmd, int flag)
1275{
1276 cmd_tbl_t *cmdtp;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001277 char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
wdenkc6097192002-11-03 00:24:07 +00001278 char *token; /* start of token in cmdbuf */
1279 char *sep; /* end of token (separator) in cmdbuf */
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001280 char finaltoken[CONFIG_SYS_CBSIZE];
wdenkc6097192002-11-03 00:24:07 +00001281 char *str = cmdbuf;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001282 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
wdenkf4688a22003-05-28 08:06:31 +00001283 int argc, inquotes;
wdenkc6097192002-11-03 00:24:07 +00001284 int repeatable = 1;
wdenkf4688a22003-05-28 08:06:31 +00001285 int rc = 0;
wdenkc6097192002-11-03 00:24:07 +00001286
1287#ifdef DEBUG_PARSER
1288 printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
1289 puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */
1290 puts ("\"\n");
1291#endif
1292
1293 clear_ctrlc(); /* forget any previous Control C */
1294
1295 if (!cmd || !*cmd) {
1296 return -1; /* empty command */
1297 }
1298
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001299 if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
wdenkc6097192002-11-03 00:24:07 +00001300 puts ("## Command too long!\n");
1301 return -1;
1302 }
1303
1304 strcpy (cmdbuf, cmd);
1305
1306 /* Process separators and check for invalid
1307 * repeatable commands
1308 */
1309
1310#ifdef DEBUG_PARSER
1311 printf ("[PROCESS_SEPARATORS] %s\n", cmd);
1312#endif
1313 while (*str) {
1314
1315 /*
1316 * Find separator, or string end
1317 * Allow simple escape of ';' by writing "\;"
1318 */
wdenk654868c2003-01-02 23:57:29 +00001319 for (inquotes = 0, sep = str; *sep; sep++) {
1320 if ((*sep=='\'') &&
1321 (*(sep-1) != '\\'))
1322 inquotes=!inquotes;
1323
1324 if (!inquotes &&
1325 (*sep == ';') && /* separator */
wdenkc6097192002-11-03 00:24:07 +00001326 ( sep != str) && /* past string start */
1327 (*(sep-1) != '\\')) /* and NOT escaped */
1328 break;
1329 }
1330
1331 /*
1332 * Limit the token to data between separators
1333 */
1334 token = str;
1335 if (*sep) {
1336 str = sep + 1; /* start of command for next pass */
1337 *sep = '\0';
1338 }
1339 else
1340 str = sep; /* no more commands for next pass */
1341#ifdef DEBUG_PARSER
1342 printf ("token: \"%s\"\n", token);
1343#endif
1344
1345 /* find macros in this token and replace them */
1346 process_macros (token, finaltoken);
1347
1348 /* Extract arguments */
Wolfgang Denkd44e7bb2006-03-12 02:20:55 +01001349 if ((argc = parse_line (finaltoken, argv)) == 0) {
1350 rc = -1; /* no command at all */
1351 continue;
1352 }
wdenkc6097192002-11-03 00:24:07 +00001353
1354 /* Look up command in command table */
1355 if ((cmdtp = find_cmd(argv[0])) == NULL) {
1356 printf ("Unknown command '%s' - try 'help'\n", argv[0]);
wdenkf4688a22003-05-28 08:06:31 +00001357 rc = -1; /* give up after bad command */
1358 continue;
wdenkc6097192002-11-03 00:24:07 +00001359 }
1360
1361 /* found - check max args */
1362 if (argc > cmdtp->maxargs) {
Peter Tyserddb3af92009-01-27 18:03:10 -06001363 cmd_usage(cmdtp);
wdenkf4688a22003-05-28 08:06:31 +00001364 rc = -1;
1365 continue;
wdenkc6097192002-11-03 00:24:07 +00001366 }
1367
Jon Loeliger052fc842007-07-08 18:10:08 -05001368#if defined(CONFIG_CMD_BOOTD)
wdenkc6097192002-11-03 00:24:07 +00001369 /* avoid "bootd" recursion */
1370 if (cmdtp->cmd == do_bootd) {
1371#ifdef DEBUG_PARSER
1372 printf ("[%s]\n", finaltoken);
1373#endif
1374 if (flag & CMD_FLAG_BOOTD) {
wdenk42c05472004-03-23 22:14:11 +00001375 puts ("'bootd' recursion detected\n");
wdenkf4688a22003-05-28 08:06:31 +00001376 rc = -1;
1377 continue;
Wolfgang Denkd44e7bb2006-03-12 02:20:55 +01001378 } else {
wdenkc6097192002-11-03 00:24:07 +00001379 flag |= CMD_FLAG_BOOTD;
Wolfgang Denkd44e7bb2006-03-12 02:20:55 +01001380 }
wdenkc6097192002-11-03 00:24:07 +00001381 }
Jon Loeligerd704d912007-07-10 11:02:44 -05001382#endif
wdenkc6097192002-11-03 00:24:07 +00001383
1384 /* OK - call function to do the command */
1385 if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
wdenkf4688a22003-05-28 08:06:31 +00001386 rc = -1;
wdenkc6097192002-11-03 00:24:07 +00001387 }
1388
1389 repeatable &= cmdtp->repeatable;
1390
1391 /* Did the user stop this? */
1392 if (had_ctrlc ())
Detlev Zundeleeab5a12007-05-23 18:47:48 +02001393 return -1; /* if stopped then not repeatable */
wdenkc6097192002-11-03 00:24:07 +00001394 }
1395
wdenkf4688a22003-05-28 08:06:31 +00001396 return rc ? rc : repeatable;
wdenkc6097192002-11-03 00:24:07 +00001397}
1398
1399/****************************************************************************/
1400
Jon Loeliger052fc842007-07-08 18:10:08 -05001401#if defined(CONFIG_CMD_RUN)
Wolfgang Denk6262d0212010-06-28 22:00:46 +02001402int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
wdenkc6097192002-11-03 00:24:07 +00001403{
1404 int i;
wdenkc6097192002-11-03 00:24:07 +00001405
Wolfgang Denk3b683112010-07-17 01:06:04 +02001406 if (argc < 2)
1407 return cmd_usage(cmdtp);
wdenkc6097192002-11-03 00:24:07 +00001408
1409 for (i=1; i<argc; ++i) {
wdenkb02744a2003-04-05 00:53:31 +00001410 char *arg;
1411
1412 if ((arg = getenv (argv[i])) == NULL) {
1413 printf ("## Error: \"%s\" not defined\n", argv[i]);
1414 return 1;
1415 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001416#ifndef CONFIG_SYS_HUSH_PARSER
wdenkb02744a2003-04-05 00:53:31 +00001417 if (run_command (arg, flag) == -1)
1418 return 1;
wdenkc6097192002-11-03 00:24:07 +00001419#else
wdenkb02744a2003-04-05 00:53:31 +00001420 if (parse_string_outer(arg,
wdenk541a76d2003-05-03 15:50:43 +00001421 FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)
wdenkb02744a2003-04-05 00:53:31 +00001422 return 1;
wdenkc6097192002-11-03 00:24:07 +00001423#endif
1424 }
wdenkb02744a2003-04-05 00:53:31 +00001425 return 0;
wdenkc6097192002-11-03 00:24:07 +00001426}
Jon Loeligerd704d912007-07-10 11:02:44 -05001427#endif