]> git.sur5r.net Git - u-boot/blob - common/main.c
Merge with git+ssh://sr@pollux.denx.org/home/sr/git/u-boot/denx/.git
[u-boot] / common / main.c
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * Add to readline cmdline-editing by
6  * (C) Copyright 2005
7  * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
8  *
9  * 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
28 /* #define      DEBUG   */
29
30 #include <common.h>
31 #include <watchdog.h>
32 #include <command.h>
33 #ifdef CONFIG_MODEM_SUPPORT
34 #include <malloc.h>             /* for free() prototype */
35 #endif
36
37 #ifdef CFG_HUSH_PARSER
38 #include <hush.h>
39 #endif
40
41 #include <post.h>
42
43 #ifdef CONFIG_SILENT_CONSOLE
44 DECLARE_GLOBAL_DATA_PTR;
45 #endif
46
47 #if defined(CONFIG_BOOT_RETRY_TIME) && defined(CONFIG_RESET_TO_RETRY)
48 extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);               /* for do_reset() prototype */
49 #endif
50
51 extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
52
53
54 #define MAX_DELAY_STOP_STR 32
55
56 static int parse_line (char *, char *[]);
57 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
58 static int abortboot(int);
59 #endif
60
61 #undef DEBUG_PARSER
62
63 char        console_buffer[CFG_CBSIZE];         /* console I/O buffer   */
64
65 #ifndef CONFIG_CMDLINE_EDITING
66 static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen);
67 static char erase_seq[] = "\b \b";              /* erase sequence       */
68 static char   tab_seq[] = "        ";           /* used to expand TABs  */
69 #endif /* CONFIG_CMDLINE_EDITING */
70
71 #ifdef CONFIG_BOOT_RETRY_TIME
72 static uint64_t endtime = 0;  /* must be set, default is instant timeout */
73 static 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
83 int do_mdm_init = 0;
84 extern 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)
94 static __inline__ int abortboot(int bootdelay)
95 {
96         int abort = 0;
97         uint64_t etime = endtick(bootdelay);
98         struct {
99                 char* str;
100                 u_int len;
101                 int retry;
102         }
103         delaykey [] = {
104                 { 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_SILENT_CONSOLE
116         if (gd->flags & GD_FLG_SILENT) {
117                 /* Restore serial console */
118                 console_assign (stdout, "serial");
119                 console_assign (stderr, "serial");
120         }
121 #endif
122
123 #  ifdef CONFIG_AUTOBOOT_PROMPT
124         printf (CONFIG_AUTOBOOT_PROMPT, bootdelay);
125 #  endif
126
127 #  ifdef CONFIG_AUTOBOOT_DELAY_STR
128         if (delaykey[0].str == NULL)
129                 delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
130 #  endif
131 #  ifdef CONFIG_AUTOBOOT_DELAY_STR2
132         if (delaykey[1].str == NULL)
133                 delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2;
134 #  endif
135 #  ifdef CONFIG_AUTOBOOT_STOP_STR
136         if (delaykey[2].str == NULL)
137                 delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR;
138 #  endif
139 #  ifdef CONFIG_AUTOBOOT_STOP_STR2
140         if (delaykey[3].str == NULL)
141                 delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2;
142 #  endif
143
144         for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
145                 delaykey[i].len = delaykey[i].str == NULL ?
146                                     0 : strlen (delaykey[i].str);
147                 delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
148                                     MAX_DELAY_STOP_STR : delaykey[i].len;
149
150                 presskey_max = presskey_max > delaykey[i].len ?
151                                     presskey_max : delaykey[i].len;
152
153 #  if DEBUG_BOOTKEYS
154                 printf("%s key:<%s>\n",
155                        delaykey[i].retry ? "delay" : "stop",
156                        delaykey[i].str ? delaykey[i].str : "NULL");
157 #  endif
158         }
159
160         /* In order to keep up with incoming data, check timeout only
161          * when catch up.
162          */
163         while (!abort && get_ticks() <= etime) {
164                 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) {
165                         if (delaykey[i].len > 0 &&
166                             presskey_len >= delaykey[i].len &&
167                             memcmp (presskey + presskey_len - delaykey[i].len,
168                                     delaykey[i].str,
169                                     delaykey[i].len) == 0) {
170 #  if DEBUG_BOOTKEYS
171                                 printf("got %skey\n",
172                                        delaykey[i].retry ? "delay" : "stop");
173 #  endif
174
175 #  ifdef CONFIG_BOOT_RETRY_TIME
176                                 /* don't retry auto boot */
177                                 if (! delaykey[i].retry)
178                                         retry_time = -1;
179 #  endif
180                                 abort = 1;
181                         }
182                 }
183
184                 if (tstc()) {
185                         if (presskey_len < presskey_max) {
186                                 presskey [presskey_len ++] = getc();
187                         }
188                         else {
189                                 for (i = 0; i < presskey_max - 1; i ++)
190                                         presskey [i] = presskey [i + 1];
191
192                                 presskey [i] = getc();
193                         }
194                 }
195         }
196 #  if DEBUG_BOOTKEYS
197         if (!abort)
198                 puts ("key timeout\n");
199 #  endif
200
201 #ifdef CONFIG_SILENT_CONSOLE
202         if (abort) {
203                 /* permanently enable normal console output */
204                 gd->flags &= ~(GD_FLG_SILENT);
205         } else if (gd->flags & GD_FLG_SILENT) {
206                 /* Restore silent console */
207                 console_assign (stdout, "nulldev");
208                 console_assign (stderr, "nulldev");
209         }
210 #endif
211
212         return abort;
213 }
214
215 # else  /* !defined(CONFIG_AUTOBOOT_KEYED) */
216
217 #ifdef CONFIG_MENUKEY
218 static int menukey = 0;
219 #endif
220
221 static __inline__ int abortboot(int bootdelay)
222 {
223         int abort = 0;
224
225 #ifdef CONFIG_SILENT_CONSOLE
226         if (gd->flags & GD_FLG_SILENT) {
227                 /* Restore serial console */
228                 console_assign (stdout, "serial");
229                 console_assign (stderr, "serial");
230         }
231 #endif
232
233 #ifdef CONFIG_MENUPROMPT
234         printf(CONFIG_MENUPROMPT, bootdelay);
235 #else
236         printf("Hit any key to stop autoboot: %2d ", bootdelay);
237 #endif
238
239 #if defined CONFIG_ZERO_BOOTDELAY_CHECK
240         /*
241          * Check if key already pressed
242          * Don't check if bootdelay < 0
243          */
244         if (bootdelay >= 0) {
245                 if (tstc()) {   /* we got a key press   */
246                         (void) getc();  /* consume input        */
247                         puts ("\b\b\b 0");
248                         abort = 1;      /* don't auto boot      */
249                 }
250         }
251 #endif
252
253         while ((bootdelay > 0) && (!abort)) {
254                 int i;
255
256                 --bootdelay;
257                 /* delay 100 * 10ms */
258                 for (i=0; !abort && i<100; ++i) {
259                         if (tstc()) {   /* we got a key press   */
260                                 abort  = 1;     /* don't auto boot      */
261                                 bootdelay = 0;  /* no more delay        */
262 # ifdef CONFIG_MENUKEY
263                                 menukey = getc();
264 # else
265                                 (void) getc();  /* consume input        */
266 # endif
267                                 break;
268                         }
269                         udelay (10000);
270                 }
271
272                 printf ("\b\b\b%2d ", bootdelay);
273         }
274
275         putc ('\n');
276
277 #ifdef CONFIG_SILENT_CONSOLE
278         if (abort) {
279                 /* permanently enable normal console output */
280                 gd->flags &= ~(GD_FLG_SILENT);
281         } else if (gd->flags & GD_FLG_SILENT) {
282                 /* Restore silent console */
283                 console_assign (stdout, "nulldev");
284                 console_assign (stderr, "nulldev");
285         }
286 #endif
287
288         return abort;
289 }
290 # endif /* CONFIG_AUTOBOOT_KEYED */
291 #endif  /* CONFIG_BOOTDELAY >= 0  */
292
293 /****************************************************************************/
294
295 void main_loop (void)
296 {
297 #ifndef CFG_HUSH_PARSER
298         static char lastcommand[CFG_CBSIZE] = { 0, };
299         int len;
300         int rc = 1;
301         int flag;
302 #endif
303
304 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
305         char *s;
306         int bootdelay;
307 #endif
308 #ifdef CONFIG_PREBOOT
309         char *p;
310 #endif
311 #ifdef CONFIG_BOOTCOUNT_LIMIT
312         unsigned long bootcount = 0;
313         unsigned long bootlimit = 0;
314         char *bcs;
315         char bcs_set[16];
316 #endif /* CONFIG_BOOTCOUNT_LIMIT */
317
318 #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
319         ulong bmp = 0;          /* default bitmap */
320         extern int trab_vfd (ulong bitmap);
321
322 #ifdef CONFIG_MODEM_SUPPORT
323         if (do_mdm_init)
324                 bmp = 1;        /* alternate bitmap */
325 #endif
326         trab_vfd (bmp);
327 #endif  /* CONFIG_VFD && VFD_TEST_LOGO */
328
329 #ifdef CONFIG_BOOTCOUNT_LIMIT
330         bootcount = bootcount_load();
331         bootcount++;
332         bootcount_store (bootcount);
333         sprintf (bcs_set, "%lu", bootcount);
334         setenv ("bootcount", bcs_set);
335         bcs = getenv ("bootlimit");
336         bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
337 #endif /* CONFIG_BOOTCOUNT_LIMIT */
338
339 #ifdef CONFIG_MODEM_SUPPORT
340         debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
341         if (do_mdm_init) {
342                 char *str = strdup(getenv("mdm_cmd"));
343                 setenv ("preboot", str);  /* set or delete definition */
344                 if (str != NULL)
345                         free (str);
346                 mdm_init(); /* wait for modem connection */
347         }
348 #endif  /* CONFIG_MODEM_SUPPORT */
349
350 #ifdef CONFIG_VERSION_VARIABLE
351         {
352                 extern char version_string[];
353
354                 setenv ("ver", version_string);  /* set version variable */
355         }
356 #endif /* CONFIG_VERSION_VARIABLE */
357
358 #ifdef CFG_HUSH_PARSER
359         u_boot_hush_start ();
360 #endif
361
362 #ifdef CONFIG_AUTO_COMPLETE
363         install_auto_complete();
364 #endif
365
366 #ifdef CONFIG_PREBOOT
367         if ((p = getenv ("preboot")) != NULL) {
368 # ifdef CONFIG_AUTOBOOT_KEYED
369                 int prev = disable_ctrlc(1);    /* disable Control C checking */
370 # endif
371
372 # ifndef CFG_HUSH_PARSER
373                 run_command (p, 0);
374 # else
375                 parse_string_outer(p, FLAG_PARSE_SEMICOLON |
376                                     FLAG_EXIT_FROM_LOOP);
377 # endif
378
379 # ifdef CONFIG_AUTOBOOT_KEYED
380                 disable_ctrlc(prev);    /* restore Control C checking */
381 # endif
382         }
383 #endif /* CONFIG_PREBOOT */
384
385 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
386         s = getenv ("bootdelay");
387         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
388
389         debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
390
391 # ifdef CONFIG_BOOT_RETRY_TIME
392         init_cmd_timeout ();
393 # endif /* CONFIG_BOOT_RETRY_TIME */
394
395 #ifdef CONFIG_BOOTCOUNT_LIMIT
396         if (bootlimit && (bootcount > bootlimit)) {
397                 printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
398                         (unsigned)bootlimit);
399                 s = getenv ("altbootcmd");
400         }
401         else
402 #endif /* CONFIG_BOOTCOUNT_LIMIT */
403                 s = getenv ("bootcmd");
404
405         debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
406
407         if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
408 # ifdef CONFIG_AUTOBOOT_KEYED
409                 int prev = disable_ctrlc(1);    /* disable Control C checking */
410 # endif
411
412 # ifndef CFG_HUSH_PARSER
413                 run_command (s, 0);
414 # else
415                 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
416                                     FLAG_EXIT_FROM_LOOP);
417 # endif
418
419 # ifdef CONFIG_AUTOBOOT_KEYED
420                 disable_ctrlc(prev);    /* restore Control C checking */
421 # endif
422         }
423
424 # ifdef CONFIG_MENUKEY
425         if (menukey == CONFIG_MENUKEY) {
426             s = getenv("menucmd");
427             if (s) {
428 # ifndef CFG_HUSH_PARSER
429                 run_command (s, 0);
430 # else
431                 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
432                                     FLAG_EXIT_FROM_LOOP);
433 # endif
434             }
435         }
436 #endif /* CONFIG_MENUKEY */
437 #endif  /* CONFIG_BOOTDELAY */
438
439 #ifdef CONFIG_AMIGAONEG3SE
440         {
441             extern void video_banner(void);
442             video_banner();
443         }
444 #endif
445
446         /*
447          * Main Loop for Monitor Command Processing
448          */
449 #ifdef CFG_HUSH_PARSER
450         parse_file_outer();
451         /* This point is never reached */
452         for (;;);
453 #else
454         for (;;) {
455 #ifdef CONFIG_BOOT_RETRY_TIME
456                 if (rc >= 0) {
457                         /* Saw enough of a valid command to
458                          * restart the timeout.
459                          */
460                         reset_cmd_timeout();
461                 }
462 #endif
463                 len = readline (CFG_PROMPT);
464
465                 flag = 0;       /* assume no special flags for now */
466                 if (len > 0)
467                         strcpy (lastcommand, console_buffer);
468                 else if (len == 0)
469                         flag |= CMD_FLAG_REPEAT;
470 #ifdef CONFIG_BOOT_RETRY_TIME
471                 else if (len == -2) {
472                         /* -2 means timed out, retry autoboot
473                          */
474                         puts ("\nTimed out waiting for command\n");
475 # ifdef CONFIG_RESET_TO_RETRY
476                         /* Reinit board to run initialization code again */
477                         do_reset (NULL, 0, 0, NULL);
478 # else
479                         return;         /* retry autoboot */
480 # endif
481                 }
482 #endif
483
484                 if (len == -1)
485                         puts ("<INTERRUPT>\n");
486                 else
487                         rc = run_command (lastcommand, flag);
488
489                 if (rc <= 0) {
490                         /* invalid command or not repeatable, forget it */
491                         lastcommand[0] = 0;
492                 }
493         }
494 #endif /*CFG_HUSH_PARSER*/
495 }
496
497 #ifdef CONFIG_BOOT_RETRY_TIME
498 /***************************************************************************
499  * initialize command line timeout
500  */
501 void init_cmd_timeout(void)
502 {
503         char *s = getenv ("bootretry");
504
505         if (s != NULL)
506                 retry_time = (int)simple_strtol(s, NULL, 10);
507         else
508                 retry_time =  CONFIG_BOOT_RETRY_TIME;
509
510         if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN)
511                 retry_time = CONFIG_BOOT_RETRY_MIN;
512 }
513
514 /***************************************************************************
515  * reset command line timeout to retry_time seconds
516  */
517 void reset_cmd_timeout(void)
518 {
519         endtime = endtick(retry_time);
520 }
521 #endif
522
523 #ifdef CONFIG_CMDLINE_EDITING
524
525 /*
526  * cmdline-editing related codes from vivi.
527  * Author: Janghoon Lyu <nandy@mizi.com>
528  */
529
530 #define putnstr(str,n)  do {                    \
531                 printf ("%.*s", n, str);        \
532         } while (0)
533
534 #define CTL_CH(c)               ((c) - 'a' + 1)
535
536 #define MAX_CMDBUF_SIZE         256
537
538 #define CTL_BACKSPACE           ('\b')
539 #define DEL                     ((char)255)
540 #define DEL7                    ((char)127)
541 #define CREAD_HIST_CHAR         ('!')
542
543 #define getcmd_putch(ch)        putc(ch)
544 #define getcmd_getch()          getc()
545 #define getcmd_cbeep()          getcmd_putch('\a')
546
547 #define HIST_MAX                20
548 #define HIST_SIZE               MAX_CMDBUF_SIZE
549
550 static int hist_max = 0;
551 static int hist_add_idx = 0;
552 static int hist_cur = -1;
553 unsigned hist_num = 0;
554
555 char* hist_list[HIST_MAX];
556 char hist_lines[HIST_MAX][HIST_SIZE];
557
558 #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
559
560 static void hist_init(void)
561 {
562         int i;
563
564         hist_max = 0;
565         hist_add_idx = 0;
566         hist_cur = -1;
567         hist_num = 0;
568
569         for (i = 0; i < HIST_MAX; i++) {
570                 hist_list[i] = hist_lines[i];
571                 hist_list[i][0] = '\0';
572         }
573 }
574
575 static void cread_add_to_hist(char *line)
576 {
577         strcpy(hist_list[hist_add_idx], line);
578
579         if (++hist_add_idx >= HIST_MAX)
580                 hist_add_idx = 0;
581
582         if (hist_add_idx > hist_max)
583                 hist_max = hist_add_idx;
584
585         hist_num++;
586 }
587
588 static char* hist_prev(void)
589 {
590         char *ret;
591         int old_cur;
592
593         if (hist_cur < 0)
594                 return NULL;
595
596         old_cur = hist_cur;
597         if (--hist_cur < 0)
598                 hist_cur = hist_max;
599
600         if (hist_cur == hist_add_idx) {
601                 hist_cur = old_cur;
602                 ret = NULL;
603         } else
604                 ret = hist_list[hist_cur];
605
606         return (ret);
607 }
608
609 static char* hist_next(void)
610 {
611         char *ret;
612
613         if (hist_cur < 0)
614                 return NULL;
615
616         if (hist_cur == hist_add_idx)
617                 return NULL;
618
619         if (++hist_cur > hist_max)
620                 hist_cur = 0;
621
622         if (hist_cur == hist_add_idx) {
623                 ret = "";
624         } else
625                 ret = hist_list[hist_cur];
626
627         return (ret);
628 }
629
630 #ifndef CONFIG_CMDLINE_EDITING
631 static void cread_print_hist_list(void)
632 {
633         int i;
634         unsigned long n;
635
636         n = hist_num - hist_max;
637
638         i = hist_add_idx + 1;
639         while (1) {
640                 if (i > hist_max)
641                         i = 0;
642                 if (i == hist_add_idx)
643                         break;
644                 printf("%s\n", hist_list[i]);
645                 n++;
646                 i++;
647         }
648 }
649 #endif /* CONFIG_CMDLINE_EDITING */
650
651 #define BEGINNING_OF_LINE() {                   \
652         while (num) {                           \
653                 getcmd_putch(CTL_BACKSPACE);    \
654                 num--;                          \
655         }                                       \
656 }
657
658 #define ERASE_TO_EOL() {                                \
659         if (num < eol_num) {                            \
660                 int tmp;                                \
661                 for (tmp = num; tmp < eol_num; tmp++)   \
662                         getcmd_putch(' ');              \
663                 while (tmp-- > num)                     \
664                         getcmd_putch(CTL_BACKSPACE);    \
665                 eol_num = num;                          \
666         }                                               \
667 }
668
669 #define REFRESH_TO_EOL() {                      \
670         if (num < eol_num) {                    \
671                 wlen = eol_num - num;           \
672                 putnstr(buf + num, wlen);       \
673                 num = eol_num;                  \
674         }                                       \
675 }
676
677 static void cread_add_char(char ichar, int insert, unsigned long *num,
678                unsigned long *eol_num, char *buf, unsigned long len)
679 {
680         unsigned long wlen;
681
682         /* room ??? */
683         if (insert || *num == *eol_num) {
684                 if (*eol_num > len - 1) {
685                         getcmd_cbeep();
686                         return;
687                 }
688                 (*eol_num)++;
689         }
690
691         if (insert) {
692                 wlen = *eol_num - *num;
693                 if (wlen > 1) {
694                         memmove(&buf[*num+1], &buf[*num], wlen-1);
695                 }
696
697                 buf[*num] = ichar;
698                 putnstr(buf + *num, wlen);
699                 (*num)++;
700                 while (--wlen) {
701                         getcmd_putch(CTL_BACKSPACE);
702                 }
703         } else {
704                 /* echo the character */
705                 wlen = 1;
706                 buf[*num] = ichar;
707                 putnstr(buf + *num, wlen);
708                 (*num)++;
709         }
710 }
711
712 static void cread_add_str(char *str, int strsize, int insert, unsigned long *num,
713               unsigned long *eol_num, char *buf, unsigned long len)
714 {
715         while (strsize--) {
716                 cread_add_char(*str, insert, num, eol_num, buf, len);
717                 str++;
718         }
719 }
720
721 static int cread_line(char *buf, unsigned int *len)
722 {
723         unsigned long num = 0;
724         unsigned long eol_num = 0;
725         unsigned long rlen;
726         unsigned long wlen;
727         char ichar;
728         int insert = 1;
729         int esc_len = 0;
730         int rc = 0;
731         char esc_save[8];
732
733         while (1) {
734                 rlen = 1;
735                 ichar = getcmd_getch();
736
737                 if ((ichar == '\n') || (ichar == '\r')) {
738                         putc('\n');
739                         break;
740                 }
741
742                 /*
743                  * handle standard linux xterm esc sequences for arrow key, etc.
744                  */
745                 if (esc_len != 0) {
746                         if (esc_len == 1) {
747                                 if (ichar == '[') {
748                                         esc_save[esc_len] = ichar;
749                                         esc_len = 2;
750                                 } else {
751                                         cread_add_str(esc_save, esc_len, insert,
752                                                       &num, &eol_num, buf, *len);
753                                         esc_len = 0;
754                                 }
755                                 continue;
756                         }
757
758                         switch (ichar) {
759
760                         case 'D':       /* <- key */
761                                 ichar = CTL_CH('b');
762                                 esc_len = 0;
763                                 break;
764                         case 'C':       /* -> key */
765                                 ichar = CTL_CH('f');
766                                 esc_len = 0;
767                                 break;  /* pass off to ^F handler */
768                         case 'H':       /* Home key */
769                                 ichar = CTL_CH('a');
770                                 esc_len = 0;
771                                 break;  /* pass off to ^A handler */
772                         case 'A':       /* up arrow */
773                                 ichar = CTL_CH('p');
774                                 esc_len = 0;
775                                 break;  /* pass off to ^P handler */
776                         case 'B':       /* down arrow */
777                                 ichar = CTL_CH('n');
778                                 esc_len = 0;
779                                 break;  /* pass off to ^N handler */
780                         default:
781                                 esc_save[esc_len++] = ichar;
782                                 cread_add_str(esc_save, esc_len, insert,
783                                               &num, &eol_num, buf, *len);
784                                 esc_len = 0;
785                                 continue;
786                         }
787                 }
788
789                 switch (ichar) {
790                 case 0x1b:
791                         if (esc_len == 0) {
792                                 esc_save[esc_len] = ichar;
793                                 esc_len = 1;
794                         } else {
795                                 puts("impossible condition #876\n");
796                                 esc_len = 0;
797                         }
798                         break;
799
800                 case CTL_CH('a'):
801                         BEGINNING_OF_LINE();
802                         break;
803                 case CTL_CH('c'):       /* ^C - break */
804                         *buf = '\0';    /* discard input */
805                         return (-1);
806                 case CTL_CH('f'):
807                         if (num < eol_num) {
808                                 getcmd_putch(buf[num]);
809                                 num++;
810                         }
811                         break;
812                 case CTL_CH('b'):
813                         if (num) {
814                                 getcmd_putch(CTL_BACKSPACE);
815                                 num--;
816                         }
817                         break;
818                 case CTL_CH('d'):
819                         if (num < eol_num) {
820                                 wlen = eol_num - num - 1;
821                                 if (wlen) {
822                                         memmove(&buf[num], &buf[num+1], wlen);
823                                         putnstr(buf + num, wlen);
824                                 }
825
826                                 getcmd_putch(' ');
827                                 do {
828                                         getcmd_putch(CTL_BACKSPACE);
829                                 } while (wlen--);
830                                 eol_num--;
831                         }
832                         break;
833                 case CTL_CH('k'):
834                         ERASE_TO_EOL();
835                         break;
836                 case CTL_CH('e'):
837                         REFRESH_TO_EOL();
838                         break;
839                 case CTL_CH('o'):
840                         insert = !insert;
841                         break;
842                 case CTL_CH('x'):
843                         BEGINNING_OF_LINE();
844                         ERASE_TO_EOL();
845                         break;
846                 case DEL:
847                 case DEL7:
848                 case 8:
849                         if (num) {
850                                 wlen = eol_num - num;
851                                 num--;
852                                 memmove(&buf[num], &buf[num+1], wlen);
853                                 getcmd_putch(CTL_BACKSPACE);
854                                 putnstr(buf + num, wlen);
855                                 getcmd_putch(' ');
856                                 do {
857                                         getcmd_putch(CTL_BACKSPACE);
858                                 } while (wlen--);
859                                 eol_num--;
860                         }
861                         break;
862                 case CTL_CH('p'):
863                 case CTL_CH('n'):
864                 {
865                         char * hline;
866
867                         esc_len = 0;
868
869                         if (ichar == CTL_CH('p'))
870                                 hline = hist_prev();
871                         else
872                                 hline = hist_next();
873
874                         if (!hline) {
875                                 getcmd_cbeep();
876                                 continue;
877                         }
878
879                         /* nuke the current line */
880                         /* first, go home */
881                         BEGINNING_OF_LINE();
882
883                         /* erase to end of line */
884                         ERASE_TO_EOL();
885
886                         /* copy new line into place and display */
887                         strcpy(buf, hline);
888                         eol_num = strlen(buf);
889                         REFRESH_TO_EOL();
890                         continue;
891                 }
892                 default:
893                         cread_add_char(ichar, insert, &num, &eol_num, buf, *len);
894                         break;
895                 }
896         }
897         *len = eol_num;
898         buf[eol_num] = '\0';    /* lose the newline */
899
900         if (buf[0] && buf[0] != CREAD_HIST_CHAR)
901                 cread_add_to_hist(buf);
902         hist_cur = hist_add_idx;
903
904         return (rc);
905 }
906
907 #endif /* CONFIG_CMDLINE_EDITING */
908
909 /****************************************************************************/
910
911 /*
912  * Prompt for input and read a line.
913  * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
914  * time out when time goes past endtime (timebase time in ticks).
915  * Return:      number of read characters
916  *              -1 if break
917  *              -2 if timed out
918  */
919 int readline (const char *const prompt)
920 {
921 #ifdef CONFIG_CMDLINE_EDITING
922         char *p = console_buffer;
923         unsigned int len=MAX_CMDBUF_SIZE;
924         int rc;
925         static int initted = 0;
926
927         if (!initted) {
928                 hist_init();
929                 initted = 1;
930         }
931
932         puts (prompt);
933
934         rc = cread_line(p, &len);
935         return rc < 0 ? rc : len;
936 #else
937         char   *p = console_buffer;
938         int     n = 0;                          /* buffer index         */
939         int     plen = 0;                       /* prompt length        */
940         int     col;                            /* output column cnt    */
941         char    c;
942
943         /* print prompt */
944         if (prompt) {
945                 plen = strlen (prompt);
946                 puts (prompt);
947         }
948         col = plen;
949
950         for (;;) {
951 #ifdef CONFIG_BOOT_RETRY_TIME
952                 while (!tstc()) {       /* while no incoming data */
953                         if (retry_time >= 0 && get_ticks() > endtime)
954                                 return (-2);    /* timed out */
955                 }
956 #endif
957                 WATCHDOG_RESET();               /* Trigger watchdog, if needed */
958
959 #ifdef CONFIG_SHOW_ACTIVITY
960                 while (!tstc()) {
961                         extern void show_activity(int arg);
962                         show_activity(0);
963                 }
964 #endif
965                 c = getc();
966
967                 /*
968                  * Special character handling
969                  */
970                 switch (c) {
971                 case '\r':                              /* Enter                */
972                 case '\n':
973                         *p = '\0';
974                         puts ("\r\n");
975                         return (p - console_buffer);
976
977                 case '\0':                              /* nul                  */
978                         continue;
979
980                 case 0x03:                              /* ^C - break           */
981                         console_buffer[0] = '\0';       /* discard input */
982                         return (-1);
983
984                 case 0x15:                              /* ^U - erase line      */
985                         while (col > plen) {
986                                 puts (erase_seq);
987                                 --col;
988                         }
989                         p = console_buffer;
990                         n = 0;
991                         continue;
992
993                 case 0x17:                              /* ^W - erase word      */
994                         p=delete_char(console_buffer, p, &col, &n, plen);
995                         while ((n > 0) && (*p != ' ')) {
996                                 p=delete_char(console_buffer, p, &col, &n, plen);
997                         }
998                         continue;
999
1000                 case 0x08:                              /* ^H  - backspace      */
1001                 case 0x7F:                              /* DEL - backspace      */
1002                         p=delete_char(console_buffer, p, &col, &n, plen);
1003                         continue;
1004
1005                 default:
1006                         /*
1007                          * Must be a normal character then
1008                          */
1009                         if (n < CFG_CBSIZE-2) {
1010                                 if (c == '\t') {        /* expand TABs          */
1011 #ifdef CONFIG_AUTO_COMPLETE
1012                                         /* if auto completion triggered just continue */
1013                                         *p = '\0';
1014                                         if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
1015                                                 p = console_buffer + n; /* reset */
1016                                                 continue;
1017                                         }
1018 #endif
1019                                         puts (tab_seq+(col&07));
1020                                         col += 8 - (col&07);
1021                                 } else {
1022                                         ++col;          /* echo input           */
1023                                         putc (c);
1024                                 }
1025                                 *p++ = c;
1026                                 ++n;
1027                         } else {                        /* Buffer full          */
1028                                 putc ('\a');
1029                         }
1030                 }
1031         }
1032 #endif /* CONFIG_CMDLINE_EDITING */
1033 }
1034
1035 /****************************************************************************/
1036
1037 #ifndef CONFIG_CMDLINE_EDITING
1038 static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
1039 {
1040         char *s;
1041
1042         if (*np == 0) {
1043                 return (p);
1044         }
1045
1046         if (*(--p) == '\t') {                   /* will retype the whole line   */
1047                 while (*colp > plen) {
1048                         puts (erase_seq);
1049                         (*colp)--;
1050                 }
1051                 for (s=buffer; s<p; ++s) {
1052                         if (*s == '\t') {
1053                                 puts (tab_seq+((*colp) & 07));
1054                                 *colp += 8 - ((*colp) & 07);
1055                         } else {
1056                                 ++(*colp);
1057                                 putc (*s);
1058                         }
1059                 }
1060         } else {
1061                 puts (erase_seq);
1062                 (*colp)--;
1063         }
1064         (*np)--;
1065         return (p);
1066 }
1067 #endif /* CONFIG_CMDLINE_EDITING */
1068
1069 /****************************************************************************/
1070
1071 int parse_line (char *line, char *argv[])
1072 {
1073         int nargs = 0;
1074
1075 #ifdef DEBUG_PARSER
1076         printf ("parse_line: \"%s\"\n", line);
1077 #endif
1078         while (nargs < CFG_MAXARGS) {
1079
1080                 /* skip any white space */
1081                 while ((*line == ' ') || (*line == '\t')) {
1082                         ++line;
1083                 }
1084
1085                 if (*line == '\0') {    /* end of line, no more args    */
1086                         argv[nargs] = NULL;
1087 #ifdef DEBUG_PARSER
1088                 printf ("parse_line: nargs=%d\n", nargs);
1089 #endif
1090                         return (nargs);
1091                 }
1092
1093                 argv[nargs++] = line;   /* begin of argument string     */
1094
1095                 /* find end of string */
1096                 while (*line && (*line != ' ') && (*line != '\t')) {
1097                         ++line;
1098                 }
1099
1100                 if (*line == '\0') {    /* end of line, no more args    */
1101                         argv[nargs] = NULL;
1102 #ifdef DEBUG_PARSER
1103                 printf ("parse_line: nargs=%d\n", nargs);
1104 #endif
1105                         return (nargs);
1106                 }
1107
1108                 *line++ = '\0';         /* terminate current arg         */
1109         }
1110
1111         printf ("** Too many args (max. %d) **\n", CFG_MAXARGS);
1112
1113 #ifdef DEBUG_PARSER
1114         printf ("parse_line: nargs=%d\n", nargs);
1115 #endif
1116         return (nargs);
1117 }
1118
1119 /****************************************************************************/
1120
1121 static void process_macros (const char *input, char *output)
1122 {
1123         char c, prev;
1124         const char *varname_start = NULL;
1125         int inputcnt = strlen (input);
1126         int outputcnt = CFG_CBSIZE;
1127         int state = 0;          /* 0 = waiting for '$'  */
1128
1129         /* 1 = waiting for '(' or '{' */
1130         /* 2 = waiting for ')' or '}' */
1131         /* 3 = waiting for '''  */
1132 #ifdef DEBUG_PARSER
1133         char *output_start = output;
1134
1135         printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input),
1136                 input);
1137 #endif
1138
1139         prev = '\0';            /* previous character   */
1140
1141         while (inputcnt && outputcnt) {
1142                 c = *input++;
1143                 inputcnt--;
1144
1145                 if (state != 3) {
1146                         /* remove one level of escape characters */
1147                         if ((c == '\\') && (prev != '\\')) {
1148                                 if (inputcnt-- == 0)
1149                                         break;
1150                                 prev = c;
1151                                 c = *input++;
1152                         }
1153                 }
1154
1155                 switch (state) {
1156                 case 0: /* Waiting for (unescaped) $    */
1157                         if ((c == '\'') && (prev != '\\')) {
1158                                 state = 3;
1159                                 break;
1160                         }
1161                         if ((c == '$') && (prev != '\\')) {
1162                                 state++;
1163                         } else {
1164                                 *(output++) = c;
1165                                 outputcnt--;
1166                         }
1167                         break;
1168                 case 1: /* Waiting for (        */
1169                         if (c == '(' || c == '{') {
1170                                 state++;
1171                                 varname_start = input;
1172                         } else {
1173                                 state = 0;
1174                                 *(output++) = '$';
1175                                 outputcnt--;
1176
1177                                 if (outputcnt) {
1178                                         *(output++) = c;
1179                                         outputcnt--;
1180                                 }
1181                         }
1182                         break;
1183                 case 2: /* Waiting for )        */
1184                         if (c == ')' || c == '}') {
1185                                 int i;
1186                                 char envname[CFG_CBSIZE], *envval;
1187                                 int envcnt = input - varname_start - 1; /* Varname # of chars */
1188
1189                                 /* Get the varname */
1190                                 for (i = 0; i < envcnt; i++) {
1191                                         envname[i] = varname_start[i];
1192                                 }
1193                                 envname[i] = 0;
1194
1195                                 /* Get its value */
1196                                 envval = getenv (envname);
1197
1198                                 /* Copy into the line if it exists */
1199                                 if (envval != NULL)
1200                                         while ((*envval) && outputcnt) {
1201                                                 *(output++) = *(envval++);
1202                                                 outputcnt--;
1203                                         }
1204                                 /* Look for another '$' */
1205                                 state = 0;
1206                         }
1207                         break;
1208                 case 3: /* Waiting for '        */
1209                         if ((c == '\'') && (prev != '\\')) {
1210                                 state = 0;
1211                         } else {
1212                                 *(output++) = c;
1213                                 outputcnt--;
1214                         }
1215                         break;
1216                 }
1217                 prev = c;
1218         }
1219
1220         if (outputcnt)
1221                 *output = 0;
1222
1223 #ifdef DEBUG_PARSER
1224         printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
1225                 strlen (output_start), output_start);
1226 #endif
1227 }
1228
1229 /****************************************************************************
1230  * returns:
1231  *      1  - command executed, repeatable
1232  *      0  - command executed but not repeatable, interrupted commands are
1233  *           always considered not repeatable
1234  *      -1 - not executed (unrecognized, bootd recursion or too many args)
1235  *           (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
1236  *           considered unrecognized)
1237  *
1238  * WARNING:
1239  *
1240  * We must create a temporary copy of the command since the command we get
1241  * may be the result from getenv(), which returns a pointer directly to
1242  * the environment data, which may change magicly when the command we run
1243  * creates or modifies environment variables (like "bootp" does).
1244  */
1245
1246 int run_command (const char *cmd, int flag)
1247 {
1248         cmd_tbl_t *cmdtp;
1249         char cmdbuf[CFG_CBSIZE];        /* working copy of cmd          */
1250         char *token;                    /* start of token in cmdbuf     */
1251         char *sep;                      /* end of token (separator) in cmdbuf */
1252         char finaltoken[CFG_CBSIZE];
1253         char *str = cmdbuf;
1254         char *argv[CFG_MAXARGS + 1];    /* NULL terminated      */
1255         int argc, inquotes;
1256         int repeatable = 1;
1257         int rc = 0;
1258
1259 #ifdef DEBUG_PARSER
1260         printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
1261         puts (cmd ? cmd : "NULL");      /* use puts - string may be loooong */
1262         puts ("\"\n");
1263 #endif
1264
1265         clear_ctrlc();          /* forget any previous Control C */
1266
1267         if (!cmd || !*cmd) {
1268                 return -1;      /* empty command */
1269         }
1270
1271         if (strlen(cmd) >= CFG_CBSIZE) {
1272                 puts ("## Command too long!\n");
1273                 return -1;
1274         }
1275
1276         strcpy (cmdbuf, cmd);
1277
1278         /* Process separators and check for invalid
1279          * repeatable commands
1280          */
1281
1282 #ifdef DEBUG_PARSER
1283         printf ("[PROCESS_SEPARATORS] %s\n", cmd);
1284 #endif
1285         while (*str) {
1286
1287                 /*
1288                  * Find separator, or string end
1289                  * Allow simple escape of ';' by writing "\;"
1290                  */
1291                 for (inquotes = 0, sep = str; *sep; sep++) {
1292                         if ((*sep=='\'') &&
1293                             (*(sep-1) != '\\'))
1294                                 inquotes=!inquotes;
1295
1296                         if (!inquotes &&
1297                             (*sep == ';') &&    /* separator            */
1298                             ( sep != str) &&    /* past string start    */
1299                             (*(sep-1) != '\\')) /* and NOT escaped      */
1300                                 break;
1301                 }
1302
1303                 /*
1304                  * Limit the token to data between separators
1305                  */
1306                 token = str;
1307                 if (*sep) {
1308                         str = sep + 1;  /* start of command for next pass */
1309                         *sep = '\0';
1310                 }
1311                 else
1312                         str = sep;      /* no more commands for next pass */
1313 #ifdef DEBUG_PARSER
1314                 printf ("token: \"%s\"\n", token);
1315 #endif
1316
1317                 /* find macros in this token and replace them */
1318                 process_macros (token, finaltoken);
1319
1320                 /* Extract arguments */
1321                 if ((argc = parse_line (finaltoken, argv)) == 0) {
1322                         rc = -1;        /* no command at all */
1323                         continue;
1324                 }
1325
1326                 /* Look up command in command table */
1327                 if ((cmdtp = find_cmd(argv[0])) == NULL) {
1328                         printf ("Unknown command '%s' - try 'help'\n", argv[0]);
1329                         rc = -1;        /* give up after bad command */
1330                         continue;
1331                 }
1332
1333                 /* found - check max args */
1334                 if (argc > cmdtp->maxargs) {
1335                         printf ("Usage:\n%s\n", cmdtp->usage);
1336                         rc = -1;
1337                         continue;
1338                 }
1339
1340 #if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
1341                 /* avoid "bootd" recursion */
1342                 if (cmdtp->cmd == do_bootd) {
1343 #ifdef DEBUG_PARSER
1344                         printf ("[%s]\n", finaltoken);
1345 #endif
1346                         if (flag & CMD_FLAG_BOOTD) {
1347                                 puts ("'bootd' recursion detected\n");
1348                                 rc = -1;
1349                                 continue;
1350                         } else {
1351                                 flag |= CMD_FLAG_BOOTD;
1352                         }
1353                 }
1354 #endif  /* CFG_CMD_BOOTD */
1355
1356                 /* OK - call function to do the command */
1357                 if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
1358                         rc = -1;
1359                 }
1360
1361                 repeatable &= cmdtp->repeatable;
1362
1363                 /* Did the user stop this? */
1364                 if (had_ctrlc ())
1365                         return 0;       /* if stopped then not repeatable */
1366         }
1367
1368         return rc ? rc : repeatable;
1369 }
1370
1371 /****************************************************************************/
1372
1373 #if (CONFIG_COMMANDS & CFG_CMD_RUN)
1374 int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
1375 {
1376         int i;
1377
1378         if (argc < 2) {
1379                 printf ("Usage:\n%s\n", cmdtp->usage);
1380                 return 1;
1381         }
1382
1383         for (i=1; i<argc; ++i) {
1384                 char *arg;
1385
1386                 if ((arg = getenv (argv[i])) == NULL) {
1387                         printf ("## Error: \"%s\" not defined\n", argv[i]);
1388                         return 1;
1389                 }
1390 #ifndef CFG_HUSH_PARSER
1391                 if (run_command (arg, flag) == -1)
1392                         return 1;
1393 #else
1394                 if (parse_string_outer(arg,
1395                     FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)
1396                         return 1;
1397 #endif
1398         }
1399         return 0;
1400 }
1401 #endif  /* CFG_CMD_RUN */