Adding upstream version 3.50~pre5.
[syslinux-debian/hramrach.git] / com32 / modules / menumain.c
blob32c760febd042114706501bbc0ba462a7f0b9037
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004-2007 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
14 * menumain.c
16 * Simple menu system which displays a list and allows the user to select
17 * a command line and/or edit it.
20 #define _GNU_SOURCE /* Needed for asprintf() on Linux */
21 #include <ctype.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <consoles.h>
26 #include <getkey.h>
27 #include <minmax.h>
28 #include <setjmp.h>
29 #include <limits.h>
30 #include <sha1.h>
31 #include <base64.h>
32 #include <colortbl.h>
33 #ifdef __COM32__
34 #include <com32.h>
35 #endif
37 #include "menu.h"
39 int (*draw_background)(const char *filename);
42 * The color/attribute indexes (\1#XX) are as follows
44 * 00 - screen Rest of the screen
45 * 01 - border Border area
46 * 02 - title Title bar
47 * 03 - unsel Unselected menu item
48 * 04 - hotkey Unselected hotkey
49 * 05 - sel Selection bar
50 * 06 - hotsel Selected hotkey
51 * 07 - scrollbar Scroll bar
52 * 08 - tabmsg Press [Tab] message
53 * 09 - cmdmark Command line marker
54 * 10 - cmdline Command line
55 * 11 - pwdborder Password box border
56 * 12 - pwdheader Password box header
57 * 13 - pwdentry Password box contents
58 * 14 - timeout_msg Timeout message
59 * 15 - timeout Timeout counter
62 static const struct color_table default_color_table[] = {
63 { "screen", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL },
64 { "border", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL },
65 { "title", "1;36;44", 0xc00090f0, 0x00000000, SHADOW_NORMAL },
66 { "unsel", "37;44", 0x90ffffff, 0x00000000, SHADOW_NORMAL },
67 { "hotkey", "1;37;44", 0xffffffff, 0x00000000, SHADOW_NORMAL },
68 { "sel", "7;37;40", 0xe0000000, 0x20ff8000, SHADOW_ALL },
69 { "hotsel", "1;7;37;40", 0xe0400000, 0x20ff8000, SHADOW_ALL },
70 { "scrollbar", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL },
71 { "tabmsg", "31;40", 0x90ffff00, 0x00000000, SHADOW_NORMAL },
72 { "cmdmark", "1;36;40", 0xc000ffff, 0x00000000, SHADOW_NORMAL },
73 { "cmdline", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL },
74 { "pwdborder", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL },
75 { "pwdheader", "31;47", 0x80ff8080, 0x20ffffff, SHADOW_NORMAL },
76 { "pwdentry", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL },
77 { "timeout_msg", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL },
78 { "timeout", "1;37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL },
79 { "help", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL },
82 #define NCOLORS (sizeof default_color_table/sizeof(struct color_table))
84 struct menu_parameter mparm[] = {
85 { "width", 80 },
86 { "margin", 10 },
87 { "passwordmargin", 3 },
88 { "rows", 12 },
89 { "tabmsgrow", 18 },
90 { "cmdlinerow", 18 },
91 { "endrow", -1 },
92 { "passwordrow", 11 },
93 { "timeoutrow", 20 },
94 { "helpmsgrow", 22 },
95 { "helpmsgendrow", -1 },
96 { NULL, 0 }
99 #define WIDTH mparm[0].value
100 #define MARGIN mparm[1].value
101 #define PASSWD_MARGIN mparm[2].value
102 #define MENU_ROWS mparm[3].value
103 #define TABMSG_ROW mparm[4].value
104 #define CMDLINE_ROW mparm[5].value
105 #define END_ROW mparm[6].value
106 #define PASSWD_ROW mparm[7].value
107 #define TIMEOUT_ROW mparm[8].value
108 #define HELPMSG_ROW mparm[9].value
109 #define HELPMSGEND_ROW mparm[10].value
111 static void
112 install_default_color_table(void)
114 unsigned int i;
115 const struct color_table *dp;
116 struct color_table *cp;
117 static struct color_table color_table[NCOLORS];
119 dp = default_color_table;
120 cp = color_table;
122 for (i = 0; i < NCOLORS; i++) {
123 if (cp->ansi)
124 free((void *)cp->ansi);
126 *cp = *dp;
127 cp->ansi = strdup(dp->ansi);
129 cp++;
130 dp++;
133 console_color_table = color_table;
134 console_color_table_size = NCOLORS;
137 static char *
138 pad_line(const char *text, int align, int width)
140 static char buffer[MAX_CMDLINE_LEN];
141 int n, p;
143 if ( width >= (int) sizeof buffer )
144 return NULL; /* Can't do it */
146 n = strlen(text);
147 if ( n >= width )
148 n = width;
150 memset(buffer, ' ', width);
151 buffer[width] = 0;
152 p = ((width-n)*align)>>1;
153 memcpy(buffer+p, text, n);
155 return buffer;
158 /* Display an entry, with possible hotkey highlight. Assumes
159 that the current attribute is the non-hotkey one, and will
160 guarantee that as an exit condition as well. */
161 static void
162 display_entry(const struct menu_entry *entry, const char *attrib,
163 const char *hotattrib, int width)
165 const char *p = entry->displayname;
167 while ( width ) {
168 if ( *p ) {
169 if ( *p == '^' ) {
170 p++;
171 if ( *p && ((unsigned char)*p & ~0x20) == entry->hotkey ) {
172 fputs(hotattrib, stdout);
173 putchar(*p++);
174 fputs(attrib, stdout);
175 width--;
177 } else {
178 putchar(*p++);
179 width--;
181 } else {
182 putchar(' ');
183 width--;
188 static void
189 draw_row(int y, int sel, int top, int sbtop, int sbbot)
191 int i = (y-4)+top;
193 printf("\033[%d;%dH\1#01\016x\017%s ",
194 y, MARGIN+1, (i == sel) ? "\1#05" : "\1#03");
196 if ( i >= nentries ) {
197 fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout);
198 } else {
199 display_entry(&menu_entries[i],
200 (i == sel) ? "\1#05" : "\1#03",
201 (i == sel) ? "\1#06" : "\1#04",
202 WIDTH-2*MARGIN-4);
205 if ( nentries <= MENU_ROWS ) {
206 printf(" \1#01\016x\017");
207 } else if ( sbtop > 0 ) {
208 if ( y >= sbtop && y <= sbbot )
209 printf(" \1#07\016a\017");
210 else
211 printf(" \1#01\016x\017");
212 } else {
213 putchar(' '); /* Don't modify the scrollbar */
217 static int
218 passwd_compare(const char *passwd, const char *entry)
220 const char *p;
221 SHA1_CTX ctx;
222 unsigned char sha1[20], pwdsha1[20];
224 if ( passwd[0] != '$' ) /* Plaintext passwd, yuck! */
225 return !strcmp(entry, passwd);
227 if ( strncmp(passwd, "$4$", 3) )
228 return 0; /* Only SHA-1 passwds supported */
230 SHA1Init(&ctx);
232 if ( (p = strchr(passwd+3, '$')) ) {
233 SHA1Update(&ctx, (void *)passwd+3, p-(passwd+3));
234 p++;
235 } else {
236 p = passwd+3; /* Assume no salt */
239 SHA1Update(&ctx, (void *)entry, strlen(entry));
240 SHA1Final(sha1, &ctx);
242 memset(pwdsha1, 0, 20);
243 unbase64(pwdsha1, 20, p);
245 return !memcmp(sha1, pwdsha1, 20);
248 static jmp_buf timeout_jump;
250 static int mygetkey(clock_t timeout)
252 clock_t t0, t;
253 clock_t tto, to;
254 int key;
256 if ( !totaltimeout )
257 return get_key(stdin, timeout);
259 for (;;) {
260 tto = min(totaltimeout, INT_MAX);
261 to = timeout ? min(tto, timeout) : tto;
263 t0 = times(NULL);
264 key = get_key(stdin, to);
265 t = times(NULL) - t0;
267 if ( totaltimeout <= t )
268 longjmp(timeout_jump, 1);
270 totaltimeout -= t;
272 if ( key != KEY_NONE )
273 return key;
275 if ( timeout ) {
276 if ( timeout <= t )
277 return KEY_NONE;
279 timeout -= t;
284 static int
285 ask_passwd(const char *menu_entry)
287 char user_passwd[WIDTH], *p;
288 int done;
289 int key;
290 int x;
292 printf("\033[%d;%dH\1#11\016l", PASSWD_ROW, PASSWD_MARGIN+1);
293 for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
294 putchar('q');
296 printf("k\033[%d;%dHx", PASSWD_ROW+1, PASSWD_MARGIN+1);
297 for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
298 putchar(' ');
300 printf("x\033[%d;%dHm", PASSWD_ROW+2, PASSWD_MARGIN+1);
301 for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
302 putchar('q');
304 printf("j\017\033[%d;%dH\1#12 %s \033[%d;%dH\1#13",
305 PASSWD_ROW, (WIDTH-(strlen(messages[MSG_PASSPROMPT].msg)+2))/2,
306 messages[MSG_PASSPROMPT].msg, PASSWD_ROW+1, PASSWD_MARGIN+3);
308 /* Actually allow user to type a password, then compare to the SHA1 */
309 done = 0;
310 p = user_passwd;
312 while ( !done ) {
313 key = mygetkey(0);
315 switch ( key ) {
316 case KEY_ENTER:
317 case KEY_CTRL('J'):
318 done = 1;
319 break;
321 case KEY_ESC:
322 case KEY_CTRL('C'):
323 p = user_passwd; /* No password entered */
324 done = 1;
325 break;
327 case KEY_BACKSPACE:
328 case KEY_DEL:
329 case KEY_DELETE:
330 if ( p > user_passwd ) {
331 printf("\b \b");
332 p--;
334 break;
336 case KEY_CTRL('U'):
337 while ( p > user_passwd ) {
338 printf("\b \b");
339 p--;
341 break;
343 default:
344 if ( key >= ' ' && key <= 0xFF &&
345 (p-user_passwd) < WIDTH-2*PASSWD_MARGIN-5 ) {
346 *p++ = key;
347 putchar('*');
349 break;
353 if ( p == user_passwd )
354 return 0; /* No password entered */
356 *p = '\0';
358 return (menu_master_passwd && passwd_compare(menu_master_passwd, user_passwd))
359 || (menu_entry && passwd_compare(menu_entry, user_passwd));
363 static void
364 draw_menu(int sel, int top, int edit_line)
366 int x, y;
367 int sbtop = 0, sbbot = 0;
368 const char *tabmsg;
370 if ( nentries > MENU_ROWS ) {
371 int sblen = MENU_ROWS*MENU_ROWS/nentries;
372 sbtop = (MENU_ROWS-sblen+1)*top/(nentries-MENU_ROWS+1);
373 sbbot = sbtop + sblen - 1;
375 sbtop += 4; sbbot += 4; /* Starting row of scrollbar */
378 printf("\033[1;%dH\1#01\016l", MARGIN+1);
379 for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
380 putchar('q');
382 printf("k\033[2;%dH\1#01x\017\1#02 %s \1#01\016x",
383 MARGIN+1,
384 pad_line(messages[MSG_TITLE].msg, 1, WIDTH-2*MARGIN-4));
386 printf("\033[3;%dH\1#01t", MARGIN+1);
387 for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
388 putchar('q');
389 fputs("u\017", stdout);
391 for ( y = 4 ; y < 4+MENU_ROWS ; y++ )
392 draw_row(y, sel, top, sbtop, sbbot);
394 printf("\033[%d;%dH\1#01\016m", y, MARGIN+1);
395 for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
396 putchar('q');
397 fputs("j\017", stdout);
399 if ( edit_line && allowedit && !menu_master_passwd )
400 tabmsg = messages[MSG_TAB].msg;
401 else
402 tabmsg = messages[MSG_NOTAB].msg;
404 printf("\1#08\033[%d;1H%s", TABMSG_ROW, pad_line(tabmsg, 1, WIDTH));
405 printf("\1#00\033[%d;1H", END_ROW);
408 static void
409 clear_screen(void)
411 fputs("\033e\033%@\033)0\033(B\1#00\033[?25l\033[2J", stdout);
414 static void
415 display_help(const char *text)
417 int row;
418 const char *p;
420 if (!text) {
421 text = "";
422 printf("\1#00\033[%d;1H", HELPMSG_ROW);
423 } else {
424 printf("\1#16\033[%d;1H", HELPMSG_ROW);
427 for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) {
428 switch (*p) {
429 case '\r':
430 case '\f':
431 case '\v':
432 case '\033':
433 break;
434 case '\n':
435 printf("\033[K\033[%d;1H", ++row);
436 break;
437 default:
438 putchar(*p);
442 fputs("\033[K", stdout);
444 while (row <= HELPMSGEND_ROW) {
445 printf("\033[K\033[%d;1H", ++row);
449 static const char *
450 edit_cmdline(char *input, int top)
452 static char cmdline[MAX_CMDLINE_LEN];
453 int key, len, prev_len, cursor;
454 int redraw = 1; /* We enter with the menu already drawn */
456 strncpy(cmdline, input, MAX_CMDLINE_LEN);
457 cmdline[MAX_CMDLINE_LEN-1] = '\0';
459 len = cursor = strlen(cmdline);
460 prev_len = 0;
462 for (;;) {
463 if ( redraw > 1 ) {
464 /* Clear and redraw whole screen */
465 /* Enable ASCII on G0 and DEC VT on G1; do it in this order
466 to avoid confusing the Linux console */
467 clear_screen();
468 draw_menu(-1, top, 1);
469 prev_len = 0;
472 if ( redraw > 0 ) {
473 /* Redraw the command line */
474 printf("\033[?25l\033[%d;1H\1#09> \1#10%s",
475 CMDLINE_ROW, pad_line(cmdline, 0, prev_len));
476 printf("\1#10\033[%d;3H%s\033[?25h",
477 CMDLINE_ROW, pad_line(cmdline, 0, cursor));
478 prev_len = len;
479 redraw = 0;
482 key = mygetkey(0);
484 switch( key ) {
485 case KEY_CTRL('L'):
486 redraw = 2;
487 break;
489 case KEY_ENTER:
490 case KEY_CTRL('J'):
491 return cmdline;
493 case KEY_ESC:
494 case KEY_CTRL('C'):
495 return NULL;
497 case KEY_BACKSPACE:
498 case KEY_DEL:
499 if ( cursor ) {
500 memmove(cmdline+cursor-1, cmdline+cursor, len-cursor+1);
501 len--;
502 cursor--;
503 redraw = 1;
505 break;
507 case KEY_CTRL('D'):
508 case KEY_DELETE:
509 if ( cursor < len ) {
510 memmove(cmdline+cursor, cmdline+cursor+1, len-cursor);
511 len--;
512 redraw = 1;
514 break;
516 case KEY_CTRL('U'):
517 if ( len ) {
518 len = cursor = 0;
519 cmdline[len] = '\0';
520 redraw = 1;
522 break;
524 case KEY_CTRL('W'):
525 if ( cursor ) {
526 int prevcursor = cursor;
528 while ( cursor && my_isspace(cmdline[cursor-1]) )
529 cursor--;
531 while ( cursor && !my_isspace(cmdline[cursor-1]) )
532 cursor--;
534 memmove(cmdline+cursor, cmdline+prevcursor, len-prevcursor+1);
535 len -= (cursor-prevcursor);
536 redraw = 1;
538 break;
540 case KEY_LEFT:
541 case KEY_CTRL('B'):
542 if ( cursor ) {
543 cursor--;
544 redraw = 1;
546 break;
548 case KEY_RIGHT:
549 case KEY_CTRL('F'):
550 if ( cursor < len ) {
551 putchar(cmdline[cursor++]);
553 break;
555 case KEY_CTRL('K'):
556 if ( cursor < len ) {
557 cmdline[len = cursor] = '\0';
558 redraw = 1;
560 break;
562 case KEY_HOME:
563 case KEY_CTRL('A'):
564 if ( cursor ) {
565 cursor = 0;
566 redraw = 1;
568 break;
570 case KEY_END:
571 case KEY_CTRL('E'):
572 if ( cursor != len ) {
573 cursor = len;
574 redraw = 1;
576 break;
578 default:
579 if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) {
580 if ( cursor == len ) {
581 cmdline[len] = key;
582 cmdline[++len] = '\0';
583 cursor++;
584 putchar(key);
585 prev_len++;
586 } else {
587 memmove(cmdline+cursor+1, cmdline+cursor, len-cursor+1);
588 cmdline[cursor++] = key;
589 len++;
590 redraw = 1;
593 break;
598 static inline int
599 shift_is_held(void)
601 uint8_t shift_bits = *(uint8_t *)0x417;
603 return !!(shift_bits & 0x5d); /* Caps/Scroll/Alt/Shift */
606 static const char *
607 run_menu(void)
609 int key;
610 int done = 0;
611 volatile int entry = defentry, prev_entry = -1;
612 int top = 0, prev_top = -1;
613 int clear = 1, to_clear;
614 const char *cmdline = NULL;
615 volatile clock_t key_timeout, timeout_left, this_timeout;
617 /* Note: for both key_timeout and timeout == 0 means no limit */
618 timeout_left = key_timeout = timeout;
620 /* If we're in shiftkey mode, exit immediately unless a shift key is pressed */
621 if ( shiftkey && !shift_is_held() ) {
622 return menu_entries[defentry].cmdline;
625 /* Handle both local and global timeout */
626 if ( setjmp(timeout_jump) ) {
627 entry = defentry;
629 if ( top < 0 || top < entry-MENU_ROWS+1 )
630 top = max(0, entry-MENU_ROWS+1);
631 else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
632 top = min(entry, max(0,nentries-MENU_ROWS));
634 draw_menu(ontimeout ? -1 : entry, top, 1);
635 cmdline = ontimeout ? ontimeout : menu_entries[entry].cmdline;
636 done = 1;
639 while ( !done ) {
640 if ( entry < 0 )
641 entry = 0;
642 else if ( entry >= nentries )
643 entry = nentries-1;
645 if ( top < 0 || top < entry-MENU_ROWS+1 )
646 top = max(0, entry-MENU_ROWS+1);
647 else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
648 top = min(entry, max(0,nentries-MENU_ROWS));
650 /* Start with a clear screen */
651 if ( clear ) {
652 /* Clear and redraw whole screen */
653 /* Enable ASCII on G0 and DEC VT on G1; do it in this order
654 to avoid confusing the Linux console */
655 clear_screen();
656 clear = 0;
657 prev_entry = prev_top = -1;
660 if ( top != prev_top ) {
661 draw_menu(entry, top, 1);
662 display_help(menu_entries[entry].helptext);
663 } else if ( entry != prev_entry ) {
664 draw_row(prev_entry-top+4, entry, top, 0, 0);
665 draw_row(entry-top+4, entry, top, 0, 0);
666 display_help(menu_entries[entry].helptext);
669 prev_entry = entry; prev_top = top;
671 /* Cursor movement cancels timeout */
672 if ( entry != defentry )
673 key_timeout = 0;
675 if ( key_timeout ) {
676 char buf[256];
677 int tol = timeout_left/CLK_TCK;
678 int nc = 0, nnc;
679 const char *tp = messages[MSG_AUTOBOOT].msg;
680 char tc;
681 char *tq = buf;
683 while ((size_t)(tq-buf) < (sizeof buf-16) && (tc = *tp)) {
684 if (tc == '#') {
685 nnc = sprintf(tq, "\1#15%d\1#14", tol);
686 tq += nnc;
687 nc += nnc-8; /* 8 formatting characters */
688 } else {
689 *tq++ = tc;
690 nc++;
692 tp++;
694 *tq = '\0';
696 printf("\033[%d;%dH\1#14 %s ", TIMEOUT_ROW, 1+((WIDTH-nc)>>1), buf);
697 to_clear = 1;
698 } else {
699 to_clear = 0;
702 this_timeout = min(min(key_timeout, timeout_left), CLK_TCK);
703 key = mygetkey(this_timeout);
705 if ( key != KEY_NONE ) {
706 timeout_left = key_timeout;
707 if ( to_clear )
708 printf("\033[%d;1H\1#00\033[K", TIMEOUT_ROW);
711 switch ( key ) {
712 case KEY_NONE: /* Timeout */
713 /* This is somewhat hacky, but this at least lets the user
714 know what's going on, and still deals with "phantom inputs"
715 e.g. on serial ports.
717 Warning: a timeout will boot the default entry without any
718 password! */
719 if ( key_timeout ) {
720 if ( timeout_left <= this_timeout )
721 longjmp(timeout_jump, 1);
723 timeout_left -= this_timeout;
725 break;
727 case KEY_CTRL('L'):
728 clear = 1;
729 break;
731 case KEY_ENTER:
732 case KEY_CTRL('J'):
733 key_timeout = 0; /* Cancels timeout */
734 if ( menu_entries[entry].passwd ) {
735 clear = 1;
736 done = ask_passwd(menu_entries[entry].passwd);
737 } else {
738 done = 1;
740 cmdline = menu_entries[entry].cmdline;
741 break;
743 case KEY_UP:
744 case KEY_CTRL('P'):
745 if ( entry > 0 ) {
746 entry--;
747 if ( entry < top )
748 top -= MENU_ROWS;
750 break;
752 case KEY_DOWN:
753 case KEY_CTRL('N'):
754 if ( entry < nentries-1 ) {
755 entry++;
756 if ( entry >= top+MENU_ROWS )
757 top += MENU_ROWS;
759 break;
761 case KEY_PGUP:
762 case KEY_LEFT:
763 case KEY_CTRL('B'):
764 case '<':
765 entry -= MENU_ROWS;
766 top -= MENU_ROWS;
767 break;
769 case KEY_PGDN:
770 case KEY_RIGHT:
771 case KEY_CTRL('F'):
772 case '>':
773 case ' ':
774 entry += MENU_ROWS;
775 top += MENU_ROWS;
776 break;
778 case '-':
779 entry--;
780 top--;
781 break;
783 case '+':
784 entry++;
785 top++;
786 break;
788 case KEY_CTRL('A'):
789 case KEY_HOME:
790 top = entry = 0;
791 break;
793 case KEY_CTRL('E'):
794 case KEY_END:
795 entry = nentries - 1;
796 top = max(0, nentries-MENU_ROWS);
797 break;
799 case KEY_TAB:
800 if ( allowedit ) {
801 int ok = 1;
803 key_timeout = 0; /* Cancels timeout */
804 draw_row(entry-top+4, -1, top, 0, 0);
806 if ( menu_master_passwd ) {
807 ok = ask_passwd(NULL);
808 clear_screen();
809 draw_menu(-1, top, 0);
810 } else {
811 /* Erase [Tab] message and help text*/
812 printf("\033[%d;1H\1#00\033[K", TABMSG_ROW);
813 display_help(NULL);
816 if ( ok ) {
817 cmdline = edit_cmdline(menu_entries[entry].cmdline, top);
818 done = !!cmdline;
819 clear = 1; /* In case we hit [Esc] and done is null */
820 } else {
821 draw_row(entry-top+4, entry, top, 0, 0);
824 break;
825 case KEY_CTRL('C'): /* Ctrl-C */
826 case KEY_ESC: /* Esc */
827 if ( allowedit ) {
828 done = 1;
829 clear = 1;
830 key_timeout = 0;
832 draw_row(entry-top+4, -1, top, 0, 0);
834 if ( menu_master_passwd )
835 done = ask_passwd(NULL);
837 break;
838 default:
839 if ( key > 0 && key < 0xFF ) {
840 key &= ~0x20; /* Upper case */
841 if ( menu_hotkeys[key] ) {
842 key_timeout = 0;
843 entry = menu_hotkeys[key] - menu_entries;
844 /* Should we commit at this point? */
847 break;
851 printf("\033[?25h"); /* Show cursor */
853 /* Return the label name so localboot and ipappend work */
854 return cmdline;
858 static void
859 execute(const char *cmdline, enum kernel_type type)
861 #ifdef __COM32__
862 com32sys_t ireg;
863 const char *p, **pp;
864 char *q = __com32.cs_bounce;
865 const char *kernel, *args;
867 memset(&ireg, 0, sizeof ireg);
869 kernel = q;
870 p = cmdline;
871 while ( *p && !my_isspace(*p) ) {
872 *q++ = *p++;
874 *q++ = '\0';
876 args = q;
877 while ( *p && my_isspace(*p) )
878 p++;
880 strcpy(q, p);
882 if (kernel[0] == '.' && type == KT_NONE) {
883 /* It might be a type specifier */
884 enum kernel_type type = KT_NONE;
885 for (pp = kernel_types; *pp; pp++, type++) {
886 if (!strcmp(kernel+1, *pp)) {
887 execute(p, type); /* Strip the type specifier and retry */
892 if (type == KT_LOCALBOOT) {
893 ireg.eax.w[0] = 0x0014; /* Local boot */
894 ireg.edx.w[0] = strtoul(kernel, NULL, 0);
895 } else {
896 if (type < KT_KERNEL)
897 type = KT_KERNEL;
899 ireg.eax.w[0] = 0x0016; /* Run kernel image */
900 ireg.esi.w[0] = OFFS(kernel);
901 ireg.ds = SEG(kernel);
902 ireg.ebx.w[0] = OFFS(args);
903 ireg.es = SEG(args);
904 ireg.edx.l = type-KT_KERNEL;
905 /* ireg.ecx.l = 0; */ /* We do ipappend "manually" */
908 __intcall(0x22, &ireg, NULL);
910 /* If this returns, something went bad; return to menu */
911 #else
912 /* For testing... */
913 printf("\n\033[0m>>> %s\n", cmdline);
914 exit(0);
915 #endif
918 int menu_main(int argc, char *argv[])
920 const char *cmdline;
921 int rows, cols;
922 int i;
924 (void)argc;
926 console_prepare();
928 install_default_color_table();
929 if (getscreensize(1, &rows, &cols)) {
930 /* Unknown screen size? */
931 rows = 24;
932 cols = 80;
935 WIDTH = cols;
936 parse_configs(argv+1);
938 /* If anyone has specified negative parameters, consider them
939 relative to the bottom row of the screen. */
940 for (i = 0; mparm[i].name; i++)
941 if (mparm[i].value < 0)
942 mparm[i].value = max(mparm[i].value+rows, 0);
944 if (draw_background)
945 draw_background(menu_background);
947 if ( !nentries ) {
948 fputs("No LABEL entries found in configuration file!\n", stdout);
949 return 1; /* Error! */
952 for(;;) {
953 cmdline = run_menu();
955 printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
956 console_cleanup();
958 if ( cmdline ) {
959 execute(cmdline, KT_NONE);
960 if ( onerror )
961 execute(onerror, KT_NONE);
962 } else {
963 return 0; /* Exit */
966 console_prepare(); /* If we're looping... */