]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/io.c
TX51/TX53 Release 2011-08-19
[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