]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/io.c
775710d40a8589c9e56baa45be39c92fe53828a5
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / io.c
1 //==========================================================================
2 //
3 //      io.c
4 //
5 //      RedBoot I/O support
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
12 // Copyright (C) 2002, 2003, 2005 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas,hmt,jlarmour
46 // Date:         2000-07-14
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include "redboot.h"
57
58 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
59 // GDB interface functions
60 extern void ungetDebugChar(char c);
61 #endif
62
63 static void
64 do_channel(int argc, char *argv[]);
65
66 #ifdef CYGPKG_REDBOOT_ANY_CONSOLE
67 RedBoot_cmd("channel", 
68             "Display/switch console channel", 
69             "[-1|<channel number>]",
70             do_channel
71     );
72 #else
73 RedBoot_cmd("channel", 
74             "Display/switch console channel", 
75             "[<channel number>]",
76             do_channel
77     );
78 #endif
79
80 static void
81 do_channel(int argc, char *argv[])
82 {
83     int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
84
85     if (argc == 2) { 
86 #ifdef CYGPKG_REDBOOT_ANY_CONSOLE
87         if (strcmp( argv[1], "-1") == 0) {
88             console_selected = false;
89             console_echo = true;
90         } else 
91 #endif
92         {
93             unsigned long chan;
94             if ( !parse_num( argv[1], &chan, NULL, NULL) ) {
95                 diag_printf("** Error: invalid channel '%s'\n", argv[1]);
96             } else {
97                 if (chan < CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS) {
98                     CYGACC_CALL_IF_SET_CONSOLE_COMM(chan);
99                     CYGACC_CALL_IF_SET_DEBUG_COMM(chan);
100                     if (chan != cur)
101                         console_echo = true;
102                 }
103                 else {
104                     diag_printf("**Error: bad channel number '%s'\n", argv[1]);
105                 }
106             }
107         }
108     }
109     /* else display */ 
110     else {
111         diag_printf("Current console channel id: ");
112 #ifdef CYGPKG_REDBOOT_ANY_CONSOLE
113         if (!console_selected)
114             diag_printf("-1\n");
115         else
116 #endif
117             diag_printf("%d\n", cur);
118     }
119 }
120
121 void 
122 mon_write_char(char c)
123 {
124     hal_virtual_comm_table_t *__chan;
125
126 #ifdef CYGPKG_REDBOOT_ANY_CONSOLE
127     if (!console_selected) {
128         int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
129         int i;
130         // Send output to all channels
131         for (i = 0;  i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  i++) {
132             CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
133             __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
134             CYGACC_COMM_IF_PUTC(*__chan, c);
135         }
136         CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
137     } else 
138 #endif
139     {
140         __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
141         if (__chan)
142             CYGACC_COMM_IF_PUTC(*__chan, c);
143         else {
144             __chan = CYGACC_CALL_IF_DEBUG_PROCS();
145             CYGACC_COMM_IF_PUTC(*__chan, c);
146         }
147     }
148 }
149
150 static void 
151 mon_read_char(char *c)
152 {
153     hal_virtual_comm_table_t* __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
154     
155     if (__chan)
156         *c = CYGACC_COMM_IF_GETC(*__chan);
157     else {
158         __chan = CYGACC_CALL_IF_DEBUG_PROCS();
159         *c = CYGACC_COMM_IF_GETC(*__chan);
160     }
161 }
162
163 #ifdef CYGPKG_REDBOOT_ANY_CONSOLE
164 static int _mon_timeout;
165 #endif
166
167 bool
168 mon_read_char_with_timeout(char *c)
169 {
170     bool res = false;
171     hal_virtual_comm_table_t *__chan;
172
173 #ifdef CYGPKG_REDBOOT_ANY_CONSOLE
174     if (!console_selected) {
175         int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
176         int i, j, tot;
177         // Try input from all channels
178         tot = 0;
179         while (tot < _mon_timeout) {
180             for (i = 0;  i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  i++, tot++) {
181                 CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
182                 __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
183                 res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
184                 if (res) {
185                     // Input available on this channel, make it be the console
186                     if (*c != '\0') {
187                         // Don't chose this unless real data have arrived
188                         console_selected = true;
189                         CYGACC_CALL_IF_SET_DEBUG_COMM(i);
190                         // Disable interrupts on all channels but this one
191                         for (j = 0;  j < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  j++) {
192                             if (i != j) {
193                                 CYGACC_CALL_IF_SET_CONSOLE_COMM(j);
194                                 __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
195                                 CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_IRQ_DISABLE);
196                             }
197                         }
198                         CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
199                         return res;
200                     }
201                 }
202             }
203         }
204         CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);        
205     } else 
206 #endif
207     {
208         __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
209         if (__chan)
210             res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
211         else {
212             __chan = CYGACC_CALL_IF_DEBUG_PROCS();
213             res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
214         }
215     }
216     return res;
217 }
218
219 void
220 mon_set_read_char_timeout(int ms)
221 {
222     hal_virtual_comm_table_t *__chan;
223
224 #ifdef CYGPKG_REDBOOT_ANY_CONSOLE
225     if (!console_selected) {
226         int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
227         int i;
228         // Set timeout to minimum on each channel; total amounts to desired value
229         _mon_timeout = ms;
230         ms = 1;
231         for (i = 0;  i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  i++) {
232             CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
233             if ((__chan = CYGACC_CALL_IF_CONSOLE_PROCS()) != 0) {
234                 CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
235             }
236         }
237         CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);        
238     } else 
239 #endif
240     {
241         if ((__chan = CYGACC_CALL_IF_CONSOLE_PROCS()) != 0) {
242             CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
243         }
244         if ((__chan = CYGACC_CALL_IF_DEBUG_PROCS()) != 0) {
245             CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
246         }
247     }
248 }
249
250 //
251 // Test for ^C on the console.  CAUTION! discards all console input
252 //
253 bool
254 _rb_break(int timeout)
255 {
256     char c;
257     mon_set_read_char_timeout(timeout);
258     if (mon_read_char_with_timeout(&c)) {
259         if (c == 0x03) {  // Test for ^C
260             return true;
261         }
262     }
263     return false;
264 }
265
266 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
267 #define __STRINGIFY(x) #x
268 #define _STRINGIFY(x) __STRINGIFY(x)
269 #define _STARTUP_STR _STRINGIFY(CYG_HAL_STARTUP) "}"
270
271 //
272 // Read a character from script.
273 // Return true if script character found, false if not.
274 //
275 static int
276 getc_script(char *cp)
277 {
278     static bool newline = true;
279     bool skip;
280
281     while (script && *script) {
282         if (newline && *script == '{') {
283             skip = false;
284             ++script;
285
286             // skip if it isn't for this startup type
287             if (strncmp(script, _STARTUP_STR, strlen(_STARTUP_STR)))
288                 skip = true;
289
290             // skip past "{...}"
291             while (*script && *script++ != '}')
292                 ;
293
294             // skip script line if neccessary
295             if (skip) {
296                 while (*script && *script++ != '\n')
297                     ;
298             } else
299                 newline = false;
300
301         } else {
302             *cp = *script++;
303             if (*cp == '\n') {
304               newline = true;
305             } else {
306               newline = false;
307             }
308             return true;
309         }
310     }
311     return false;
312 }
313 #endif
314
315 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
316 #define _CL_NUM_LINES CYGNUM_REDBOOT_CMD_LINE_EDITING       // Number of lines to keep
317 static char _cl_lines[_CL_NUM_LINES][CYGPKG_REDBOOT_MAX_CMD_LINE];
318 static int  _cl_index = -1;      // Last known command line
319 static int  _cl_max_index = -1;  // Last command in buffers
320
321 #ifdef CYGBLD_REDBOOT_CMD_LINE_HISTORY
322 static void expand_history(char *);
323 #endif
324 #endif
325
326 //
327 // Read a line of input from the user
328 // Return:
329 //        _GETS_OK: 'n' valid characters received
330 //       _GETS_GDB: '$' (GDB lead-in)
331 //   _GETS_TIMEOUT: No input before timeout
332 //     _GETS_CTRLC: ^C typed
333 //
334 // if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
335 //   Command line history support
336 //    ^P - Select previous line from history
337 //    ^N - Select next line from history
338 //    ^A - Move insertion [cursor] to start of line
339 //    ^E - Move cursor to end of line
340 //    ^B - Move cursor back [previous character]
341 //    ^F - Move cursor forward [next character]
342 // "standard" arrow keys work as well
343 //   left  ^[[D      == ^B
344 //   right ^[[C      == ^F
345 //   up    ^[[A      == ^P
346 //   down  ^[[B      == ^N
347 //   home  ^[[H/^[1~ == ^A
348 //   end   ^[[F/^[OF == ^E
349 //   del   ^[3~      == ^D
350 //
351 int
352 _rb_gets_preloaded(char *buf, int buflen, int timeout)
353 {
354     char *ip = buf;   // Insertion point
355     char *eol = buf;  // End of line
356     char c;
357     bool res = false;
358     static char last_ch = '\0';
359     int _timeout;
360 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
361     int   _index = _cl_index;  // Last saved line
362     char *xp;
363 #ifdef CYGSEM_REDBOOT_CMD_LINE_ANSI_SEQUENCES
364     int   ansi_state = 0;      // Used to drive ANSI parser
365     char  ansi_char = '\0';
366 #endif
367 #endif
368
369     // Display current buffer data
370     while (*eol) {
371         mon_write_char(*eol++);
372     }
373     ip = eol;
374
375     while (true) {
376 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
377         if (getc_script(&c))
378             do_idle(false);
379         else
380 #endif
381         if ((timeout > 0) && (eol == buf)) {
382 #define MIN_TIMEOUT 50
383             _timeout = timeout > MIN_TIMEOUT ? MIN_TIMEOUT : timeout;
384             mon_set_read_char_timeout(_timeout);
385             while (timeout > 0) {
386                 res = mon_read_char_with_timeout(&c);
387                 if (res) {
388                     // Got a character
389                     do_idle(false);
390                     break;
391                 }
392                 timeout -= _timeout;
393             }
394             if (res == false) {
395                 do_idle(true);
396                 return _GETS_TIMEOUT;  // Input timed out
397             }
398         } else {
399             mon_read_char(&c);
400         }
401         *eol = '\0';
402 #define CTRL(c) ((c)&0x1F)
403 #ifdef CYGSEM_REDBOOT_CMD_LINE_ANSI_SEQUENCES
404         // Special handling of ANSI keyboard sequences (arrows, etc)
405         if (c == 0x1B) {
406             // Leadin for ANSI keyboard sequence
407             ansi_state = 1;
408             continue;
409         }
410         switch (ansi_state) {
411         case 0:
412             // No ANSI sequence in progress
413             break;
414         case 1:
415             // ESC seen, look for '['
416             if (c == '[') {
417                 ansi_state = 2;
418             } else if (c == 'O') {
419                 ansi_state = 4;
420             } else {
421                 // Handle bad sequences?
422                 ansi_state = 0;
423             }
424             continue;
425         case 2:
426             // ESC+[ seen, process key
427             ansi_state = 0;
428             switch (c) {
429             case 'A':
430                 c = CTRL('P');
431                 break;
432             case 'B':
433                 c = CTRL('N');
434                 break;
435             case 'C':
436                 c = CTRL('F');
437                 break;
438             case 'D':
439                 c = CTRL('B');
440                 break;
441             case 'F':
442                 c = CTRL('E');
443                 break;
444             case 'H':
445                 c = CTRL('A');
446                 break;
447             case '1':
448                 ansi_char = CTRL('A');
449                 ansi_state = 3;
450                 continue;
451             case '3':
452                 ansi_char = CTRL('D');
453                 ansi_state = 3;
454                 continue;
455             default:
456                 // Handle bad sequences?
457                 continue;
458             }
459             break;
460         case 3:
461             // Sequences like ^[[1~ == ^H
462             ansi_state = 0;
463             if (c == '~') {
464                 c = ansi_char;
465             } else {
466                 // Handle bad sequences?
467                 continue;
468             }
469             break;
470         case 4:
471             // Sequences like ^[OF == ^E
472             ansi_state = 0;
473             if (c == 'F') {
474                 c = CTRL('E');
475             } else {
476                 // Handle bad sequences?
477                 continue;
478             }
479             break;
480         }
481 #endif
482         switch (c) {
483 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
484         case CTRL('P'):
485             // Fetch the previous line into the buffer
486             if (_index >= 0) {
487                 // Erase the previous line [crude]
488                 while (ip != buf) {
489                     mon_write_char('\b');
490                     mon_write_char(' ');
491                     mon_write_char('\b');
492                     ip--;
493                 }
494                 strcpy(buf, _cl_lines[_index]);
495                 while (*ip) {
496                     mon_write_char(*ip++);
497                 }
498                 eol = ip;
499                 // Move to previous line
500                 _index--;
501                 if (_index < 0) {
502                     _index = _cl_max_index;
503                 }
504             } else {
505                 mon_write_char(0x07);  // Audible bell on most devices
506             }
507             break;
508         case CTRL('N'):
509             // Fetch the next line into the buffer
510             if (_index >= 0) {
511                 if (++_index > _cl_max_index) _index = 0;
512                 // Erase the previous line [crude]
513                 while (ip != buf) {
514                     mon_write_char('\b');
515                     mon_write_char(' ');
516                     mon_write_char('\b');
517                     ip--;
518                 }
519                 strcpy(buf, _cl_lines[_index]);
520                 while (*ip) {
521                     mon_write_char(*ip++);
522                 }
523                 eol = ip;
524             } else {
525                 mon_write_char(0x07);  // Audible bell on most devices
526             }
527             break;
528         case CTRL('B'): 
529             // Move insertion point backwards
530             if (ip != buf) {
531                 mon_write_char('\b');
532                 ip--;
533             }
534             break;
535         case CTRL('F'):
536             // Move insertion point forwards
537             if (ip != eol) {
538                 mon_write_char(*ip++);
539             }
540             break;
541         case CTRL('E'):
542             // Move insertion point to end of line
543             while (ip != eol) {
544                 mon_write_char(*ip++);
545             }
546             break;
547         case CTRL('A'):
548             // Move insertion point to beginning of line
549             if (ip != buf) {
550                 xp = ip;
551                 while (xp-- != buf) {
552                     mon_write_char('\b');
553                 }
554             }
555             ip = buf;
556             break;
557         case CTRL('K'):
558             // Kill to the end of line
559             if (ip != eol) {
560                 xp = ip;
561                 while (xp++ != eol) {
562                     mon_write_char(' ');
563                 }
564                 while (--xp != ip) {
565                     mon_write_char('\b');
566                 }
567                 eol = ip;
568             }
569             break;
570         case CTRL('D'):
571             // Erase the character under the cursor
572             if (ip != eol) {
573                 xp = ip;
574                 eol--;
575                 while (xp != eol) {
576                     *xp = *(xp+1);
577                     mon_write_char(*xp++);
578                 }
579                 mon_write_char(' ');  // Erases last character
580                 mon_write_char('\b');
581                 while (xp-- != ip) {
582                     mon_write_char('\b');
583                 }
584             }
585             break;
586 #endif // CYGNUM_REDBOOT_CMD_LINE_EDITING
587         case CTRL('C'): // ^C
588             // Abort current input
589             diag_printf("^C\n");
590             *buf = '\0';  // Nothing useful in buffer
591             return _GETS_CTRLC;
592         case '\n':
593         case '\r':
594             // If previous character was the "other" end-of-line, ignore this one
595             if (((c == '\n') && (last_ch == '\r')) ||
596                 ((c == '\r') && (last_ch == '\n'))) {
597                 c = '\0';
598                 break;
599             }
600             // End of line
601             if (console_echo) {
602                 mon_write_char('\r');
603                 mon_write_char('\n');
604             }
605             last_ch = c;
606 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
607             if (cmd_history) {
608                 // History handling - only when enabled
609 #ifdef CYGBLD_REDBOOT_CMD_LINE_HISTORY
610                 expand_history(buf);
611 #endif
612                 if (*buf != '\0') {
613                     if (++_cl_index == _CL_NUM_LINES) _cl_index = 0;
614                     if (_cl_index > _cl_max_index) _cl_max_index = _cl_index;
615                     strcpy(_cl_lines[_cl_index], buf);
616                 }
617             }
618 #endif
619             return _GETS_OK;
620         case '\b':
621         case 0x7F:  // DEL
622             if (ip != buf) {
623 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
624                 if (ip != eol) {
625                     ip--;
626                     mon_write_char('\b');
627                     xp = ip;
628                     while (xp != (eol-1)) {
629                         *xp = *(xp+1);
630                         mon_write_char(*xp++);
631                     }
632                     mon_write_char(' ');  // Erases last character
633                     mon_write_char('\b');
634                     while (xp-- != ip) {
635                         mon_write_char('\b');
636                     }
637                 } else {
638                     if (console_echo) {
639                         mon_write_char('\b');
640                         mon_write_char(' ');
641                         mon_write_char('\b');
642                     }
643                     ip--;
644                 }
645                 eol--;
646 #else
647                 if (console_echo) {
648                     mon_write_char('\b');
649                     mon_write_char(' ');
650                     mon_write_char('\b');
651                 }
652                 ip--;
653                 eol--;
654 #endif
655             }
656             break;
657 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
658         case '+': // fall through
659         case '$':
660             if (ip == buf || last_ch != '\\')
661             {
662                 // Give up and try GDB protocol
663                 ungetDebugChar(c);  // Push back character so stubs will see it
664                 return _GETS_GDB;
665             }
666             if (last_ch == '\\') {
667 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
668                 if (ip == eol) {
669                     // Just save \$ as $
670                     eol = --ip;
671                 } else {
672                     mon_write_char('\b');
673                     *--ip = c;
674                     mon_write_char(c);
675                     break;
676                 }
677 #else
678                 ip--;  // Save \$ as $
679 #endif
680             }
681             // else fall through
682 #endif
683         default:
684 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
685             // If the insertion point is not at the end of line, make space for it
686             if (ip != eol) {
687                 xp = eol;
688                 *++eol = '\0';
689                 while (xp != ip) {
690                     *xp = *(xp-1);
691                     xp--;
692                 }
693             }
694 #endif
695             if (console_echo) {
696                 mon_write_char(c);
697             }
698             if (ip == eol) {
699                 // Advance both pointers
700                 *ip++ = c;
701                 eol = ip;
702 #if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
703             } else {
704                 // Just insert the character
705                 *ip++ = c;
706                 xp = ip;
707                 while (xp != eol) {
708                     mon_write_char(*xp++);
709                 }
710                 while (xp-- != ip) {
711                     mon_write_char('\b');
712                 }
713 #endif
714             }
715         }
716         last_ch = c;
717         if (ip == buf + buflen - 1) { // Buffer full
718             *ip = '\0';
719             return buflen;
720         }
721     }
722 }
723
724 int
725 _rb_gets(char *buf, int buflen, int timeout)
726 {
727     *buf = '\0';  // Empty buffer
728     return _rb_gets_preloaded(buf, buflen, timeout);
729 }
730
731 static bool
732 _verify_action(int timeout, char *fmt, va_list ap)
733 {
734     char ans[8];
735     int ret;
736 #ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
737     // Don't ask if we're executing a script
738     if (script && *script)
739         return 1;
740 #endif
741
742     diag_vprintf(fmt, ap);
743     diag_printf(" - continue (y/n)? ");
744     if ((ret = _rb_gets(ans, sizeof(ans), timeout)) > 0) {
745         return ((ans[0] == 'y') || (ans[0] == 'Y'));
746     } else {
747         if (ret == _GETS_TIMEOUT) {
748             diag_printf(" ** Timed out!\n");
749         }
750         return 0;  // Timed out or ^C
751     }
752 }
753
754 bool
755 verify_action(char *fmt, ...)
756 {
757     va_list ap;
758
759     va_start(ap, fmt);
760     return _verify_action(0, fmt, ap);
761 }
762
763 bool
764 verify_action_with_timeout(int timeout, char *fmt, ...)
765 {
766     va_list ap;
767
768     va_start(ap, fmt);
769     return _verify_action(timeout, fmt, ap);
770 }
771
772
773 #ifdef CYGBLD_REDBOOT_CMD_LINE_HISTORY
774 // parse for history index number. Return number or -1 if not
775 // an index number.
776 static int
777 parse_history_index(char *s)
778 {
779     int val = 0;
780
781     while ('0' <= *s && *s <= '9')
782         val = (val * 10) + (*s++ - '0');
783
784     if (*s)
785         return -1;
786
787     return val;
788 }
789
790 // Check input line to see if it needs history expansion. If so,
791 // try to find matching command and replace buffer as appropriate.
792 static void
793 expand_history(char *buf)
794 {
795     int ncmds = _cl_max_index + 1;
796     int i, index, len;
797
798     if (buf[0] != '!' || buf[1] == '\0')
799         return;
800
801     if (ncmds > 0) {
802         if (!strcmp(buf, "!!")) {
803             strcpy(buf, _cl_lines[_cl_index]);
804             return;
805         }
806         if ((index = parse_history_index(buf + 1)) >= 0) {
807             if (index <= _cl_max_index) {
808                 strcpy(buf, _cl_lines[index]);
809                 return;
810             }
811         }
812         len = strlen(buf + 1);
813         for (i = 0, index = _cl_index; i < ncmds; i++) {
814             if (!strncmp(_cl_lines[index], buf+1, len)) {
815                 strcpy(buf, _cl_lines[index]);
816                 return;
817             }
818             if (--index < 0)
819                 index = _cl_max_index;
820         }
821     }
822
823     diag_printf("%s: event not found\n", buf);
824     *buf = '\0';
825 }
826
827 static void
828 do_history(int argc, char *argv[])
829 {
830     int ncmds = _cl_max_index + 1;
831     int i, index;
832
833     if (_cl_index == _cl_max_index) {
834         // history has not wrapped around
835         for (i = 0; i < ncmds; i++)
836             diag_printf("%3d %s\n", i, _cl_lines[i]);
837     } else {
838         for (i = 0, index = _cl_index + 1; i < ncmds; i++) {
839             diag_printf("%3d %s\n", i, _cl_lines[index++]);
840             if (index > _cl_max_index)
841                 index = 0;
842         }
843     }
844 }
845
846 RedBoot_cmd("history", 
847             "Display command history", 
848             "",
849             do_history
850     );
851 #endif  // CYGBLD_REDBOOT_CMD_LINE_HISTORY