Merge with 3.2x branch syslinux-3.30-pre2
authorH. Peter Anvin <hpa@zytor.com>
Tue, 5 Sep 2006 22:46:27 +0000 (15:46 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Tue, 5 Sep 2006 22:46:27 +0000 (15:46 -0700)
Conflicts:

com32/modules/Makefile
com32/modules/menu.c
com32/modules/menu.h
com32/modules/readconfig.c
version

1  2 
com32/modules/Makefile
com32/modules/menu.h
com32/modules/menumain.c
com32/modules/readconfig.c

@@@ -44,8 -44,8 +44,8 @@@ AUXDIR   = $(LIBDIR)/syslinu
  INCDIR   = /usr/include
  COM32DIR = $(AUXDIR)/com32
  
- MODULES         = chain.c32 menu.c32 vesamenu.c32 ethersel.c32 mboot.c32 dmitest.c32
 -MODULES         = chain.c32 menu.c32 ethersel.c32 mboot.c32 dmitest.c32 cpuidtest.c32
 -TESTFILES = menu.lnx
++MODULES         = chain.c32 menu.c32 vesamenu.c32 ethersel.c32 mboot.c32 dmitest.c32 cpuidtest.c32
 +TESTFILES = 
  
  all: $(MODULES) $(TESTFILES)
  
@@@ -60,12 -60,10 +60,13 @@@ extern long long totaltimeout
  
  extern char *menu_title;
  extern char *ontimeout;
+ extern char *onerror;
  extern char *menu_master_passwd;
  
 +extern char *menu_background;
 +
  void parse_config(const char *filename);
 +extern int (*draw_background)(const char *filename);
  
  static inline int my_isspace(char c)
  {
index c82a582,0000000..91ee5d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,873 -1,0 +1,876 @@@
-     if ( cmdline )
 +/* ----------------------------------------------------------------------- *
 + *
 + *   Copyright 2004-2006 H. Peter Anvin - All Rights Reserved
 + *
 + *   This program is free software; you can redistribute it and/or modify
 + *   it under the terms of the GNU General Public License as published by
 + *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
 + *   Boston MA 02111-1307, USA; either version 2 of the License, or
 + *   (at your option) any later version; incorporated herein by reference.
 + *
 + * ----------------------------------------------------------------------- */
 +
 +/*
 + * menumain.c
 + *
 + * Simple menu system which displays a list and allows the user to select
 + * a command line and/or edit it.
 + */
 +
 +#define _GNU_SOURCE           /* Needed for asprintf() on Linux */
 +#include <ctype.h>
 +#include <string.h>
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <consoles.h>
 +#include <getkey.h>
 +#include <minmax.h>
 +#include <setjmp.h>
 +#include <limits.h>
 +#include <sha1.h>
 +#include <base64.h>
 +#include <colortbl.h>
 +#ifdef __COM32__
 +#include <com32.h>
 +#endif
 +
 +#include "menu.h"
 +
 +int (*draw_background)(const char *filename);
 +
 +/*
 + * The color/attribute indexes (\1#XX) are as follows
 + *
 + * 00 - screen                Rest of the screen
 + * 01 - border                Border area
 + * 02 - title         Title bar
 + * 03 - unsel         Unselected menu item
 + * 04 - hotkey                Unselected hotkey
 + * 05 - sel           Selection bar
 + * 06 - hotsel                Selected hotkey
 + * 07 - scrollbar     Scroll bar
 + * 08 - tabmsg                Press [Tab] message
 + * 09 - cmdmark               Command line marker
 + * 10 - cmdline               Command line
 + * 11 - pwdborder     Password box border
 + * 12 - pwdheader     Password box header
 + * 13 - pwdentry      Password box contents
 + * 14 - timeout_msg   Timeout message
 + * 15 - timeout               Timeout counter
 + */
 +
 +static const struct color_table default_color_table[] = {
 +  { "screen",      "0;37;40",   0x80ffffff, 0x00000000 },
 +  { "border",      "0;30;44",   0x40000000, 0x00000000 },
 +  { "title",       "1;36;44",   0xc00090f0, 0x00000000 },
 +  { "unsel",       "0;37;44",   0x90ffffff, 0x00000000 },
 +  { "hotkey",      "1;37;44",   0xffffffff, 0x00000000 },
 +  { "sel",         "0;7;37;40", 0xcf101010, 0x20ff8000 },
 +  { "hotsel",      "1;7;37;40", 0xff353535, 0x20ff8000 },
 +  { "scrollbar",   "0;30;44",   0x40000000, 0x00000000 },
 +  { "tabmsg",      "0;31;40",   0x90ffff00, 0x00000000 },
 +  { "cmdmark",     "1;36;40",   0xc000ffff, 0x00000000 },
 +  { "cmdline",     "0;37;40",   0xc0ffffff, 0x00000000 },
 +  { "pwdborder",   "0;30;47",   0x80ffffff, 0x20ffffff },
 +  { "pwdheader",   "0;31;47",   0x80ff8080, 0x20ffffff },
 +  { "pwdentry",    "0;30;47",   0x80ffffff, 0x20ffffff },
 +  { "timeout_msg", "0;37;40",   0x80ffffff, 0x00000000 },
 +  { "timeout",     "1;37;40",   0xc0ffffff, 0x00000000 },
 +};
 +
 +#define NCOLORS (sizeof default_color_table/sizeof(struct color_table))
 +
 +struct menu_parameter mparm[] = {
 +  { "width", 80 },
 +  { "margin", 10 },
 +  { "passwordmargin", 3 },
 +  { "rows", 12 },
 +  { "tabmsgrow", 18 },
 +  { "cmdlinerow", 18 },
 +  { "endrow", 24 },
 +  { "passwordrow", 11 },
 +  { "timeoutrow", 20 },
 +  { NULL, 0 }
 +};
 +
 +#define WIDTH         mparm[0].value
 +#define MARGIN                mparm[1].value
 +#define PASSWD_MARGIN mparm[2].value
 +#define MENU_ROWS     mparm[3].value
 +#define TABMSG_ROW    mparm[4].value
 +#define CMDLINE_ROW   mparm[5].value
 +#define END_ROW               mparm[6].value
 +#define PASSWD_ROW    mparm[7].value
 +#define TIMEOUT_ROW   mparm[8].value
 +
 +static void
 +install_default_color_table(void)
 +{
 +  unsigned int i;
 +  const struct color_table *dp;
 +  struct color_table *cp;
 +  static struct color_table color_table[NCOLORS];
 +
 +  dp = default_color_table;
 +  cp = color_table;
 +
 +  for (i = 0; i < NCOLORS; i++) {
 +    if (cp->ansi)
 +      free((void *)cp->ansi);
 +
 +    cp->name = dp->name;
 +    cp->ansi = strdup(dp->ansi);
 +    cp->argb_fg = dp->argb_fg;
 +    cp->argb_bg = dp->argb_bg;
 +
 +    cp++;
 +    dp++;
 +  }
 +
 +  console_color_table = color_table;
 +  console_color_table_size = NCOLORS;
 +}
 +
 +static char *
 +pad_line(const char *text, int align, int width)
 +{
 +  static char buffer[MAX_CMDLINE_LEN];
 +  int n, p;
 +
 +  if ( width >= (int) sizeof buffer )
 +    return NULL;              /* Can't do it */
 +
 +  n = strlen(text);
 +  if ( n >= width )
 +    n = width;
 +
 +  memset(buffer, ' ', width);
 +  buffer[width] = 0;
 +  p = ((width-n)*align)>>1;
 +  memcpy(buffer+p, text, n);
 +
 +  return buffer;
 +}
 +
 +/* Display an entry, with possible hotkey highlight.  Assumes
 +   that the current attribute is the non-hotkey one, and will
 +   guarantee that as an exit condition as well. */
 +static void
 +display_entry(const struct menu_entry *entry, const char *attrib,
 +            const char *hotattrib, int width)
 +{
 +  const char *p = entry->displayname;
 +
 +  while ( width ) {
 +    if ( *p ) {
 +      if ( *p == '^' ) {
 +      p++;
 +      if ( *p && ((unsigned char)*p & ~0x20) == entry->hotkey ) {
 +        fputs(hotattrib, stdout);
 +        putchar(*p++);
 +        fputs(attrib, stdout);
 +        width--;
 +      }
 +      } else {
 +      putchar(*p++);
 +      width--;
 +      }
 +    } else {
 +      putchar(' ');
 +      width--;
 +    }
 +  }
 +}
 +
 +static void
 +draw_row(int y, int sel, int top, int sbtop, int sbbot)
 +{
 +  int i = (y-4)+top;
 +
 +  printf("\033[%d;%dH\1#01\016x\017%s ",
 +       y, MARGIN+1, (i == sel) ? "\1#05" : "\1#03");
 +
 +  if ( i >= nentries ) {
 +    fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout);
 +  } else {
 +    display_entry(&menu_entries[i],
 +                (i == sel) ? "\1#05" : "\1#03",
 +                (i == sel) ? "\1#06" : "\1#04",
 +                WIDTH-2*MARGIN-4);
 +  }
 +
 +  if ( nentries <= MENU_ROWS ) {
 +    printf(" \1#01\016x\017");
 +  } else if ( sbtop > 0 ) {
 +    if ( y >= sbtop && y <= sbbot )
 +      printf(" \1#07\016a\017");
 +    else
 +      printf(" \1#01\016x\017");
 +  } else {
 +    putchar(' ');             /* Don't modify the scrollbar */
 +  }
 +}
 +
 +static int
 +passwd_compare(const char *passwd, const char *entry)
 +{
 +  const char *p;
 +  SHA1_CTX ctx;
 +  unsigned char sha1[20], pwdsha1[20];
 +
 +  if ( passwd[0] != '$' )     /* Plaintext passwd, yuck! */
 +    return !strcmp(entry, passwd);
 +
 +  if ( strncmp(passwd, "$4$", 3) )
 +    return 0;                 /* Only SHA-1 passwds supported */
 +
 +  SHA1Init(&ctx);
 +
 +  if ( (p = strchr(passwd+3, '$')) ) {
 +    SHA1Update(&ctx, (void *)passwd+3, p-(passwd+3));
 +    p++;
 +  } else {
 +    p = passwd+3;             /* Assume no salt */
 +  }
 +
 +  SHA1Update(&ctx, (void *)entry, strlen(entry));
 +  SHA1Final(sha1, &ctx);
 +
 +  memset(pwdsha1, 0, 20);
 +  unbase64(pwdsha1, 20, p);
 +
 +  return !memcmp(sha1, pwdsha1, 20);
 +}
 +
 +static jmp_buf timeout_jump;
 +
 +static int mygetkey(clock_t timeout)
 +{
 +  clock_t t0, t;
 +  clock_t tto, to;
 +  int key;
 +
 +  if ( !totaltimeout )
 +    return get_key(stdin, timeout);
 +
 +  for (;;) {
 +    tto = min(totaltimeout, INT_MAX);
 +    to = timeout ? min(tto, timeout) : tto;
 +
 +    t0 = times(NULL);
 +    key = get_key(stdin, to);
 +    t = times(NULL) - t0;
 +
 +    if ( totaltimeout <= t )
 +      longjmp(timeout_jump, 1);
 +
 +    totaltimeout -= t;
 +
 +    if ( key != KEY_NONE )
 +      return key;
 +
 +    if ( timeout ) {
 +      if ( timeout <= t )
 +      return KEY_NONE;
 +
 +      timeout -= t;
 +    }
 +  }
 +}
 +
 +static int
 +ask_passwd(const char *menu_entry)
 +{
 +  static const char title[] = "Password required";
 +  char user_passwd[WIDTH], *p;
 +  int done;
 +  int key;
 +  int x;
 +
 +  printf("\033[%d;%dH\1#11\016l", PASSWD_ROW, PASSWD_MARGIN+1);
 +  for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
 +    putchar('q');
 +
 +  printf("k\033[%d;%dHx", PASSWD_ROW+1, PASSWD_MARGIN+1);
 +  for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
 +    putchar(' ');
 +
 +  printf("x\033[%d;%dHm", PASSWD_ROW+2, PASSWD_MARGIN+1);
 +  for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
 +    putchar('q');
 +
 +  printf("j\017\033[%d;%dH\1#12 %s \033[%d;%dH\1#13",
 +       PASSWD_ROW, (WIDTH-((int)sizeof(title)+1))/2,
 +       title, PASSWD_ROW+1, PASSWD_MARGIN+3);
 +
 +  /* Actually allow user to type a password, then compare to the SHA1 */
 +  done = 0;
 +  p = user_passwd;
 +
 +  while ( !done ) {
 +    key = mygetkey(0);
 +
 +    switch ( key ) {
 +    case KEY_ENTER:
 +    case KEY_CTRL('J'):
 +      done = 1;
 +      break;
 +
 +    case KEY_ESC:
 +    case KEY_CTRL('C'):
 +      p = user_passwd;                /* No password entered */
 +      done = 1;
 +      break;
 +
 +    case KEY_BACKSPACE:
 +    case KEY_DEL:
 +    case KEY_DELETE:
 +      if ( p > user_passwd ) {
 +      printf("\b \b");
 +      p--;
 +      }
 +      break;
 +
 +    case KEY_CTRL('U'):
 +      while ( p > user_passwd ) {
 +      printf("\b \b");
 +      p--;
 +      }
 +      break;
 +
 +    default:
 +      if ( key >= ' ' && key <= 0xFF &&
 +         (p-user_passwd) < WIDTH-2*PASSWD_MARGIN-5 ) {
 +      *p++ = key;
 +      putchar('*');
 +      }
 +      break;
 +    }
 +  }
 +
 +  if ( p == user_passwd )
 +    return 0;                 /* No password entered */
 +
 +  *p = '\0';
 +
 +  return (menu_master_passwd && passwd_compare(menu_master_passwd, user_passwd))
 +    || (menu_entry && passwd_compare(menu_entry, user_passwd));
 +}
 +
 +
 +static void
 +draw_menu(int sel, int top, int edit_line)
 +{
 +  int x, y;
 +  int sbtop = 0, sbbot = 0;
 +
 +  if ( nentries > MENU_ROWS ) {
 +    int sblen = MENU_ROWS*MENU_ROWS/nentries;
 +    sbtop = (MENU_ROWS-sblen+1)*top/(nentries-MENU_ROWS+1);
 +    sbbot = sbtop + sblen - 1;
 +
 +    sbtop += 4;  sbbot += 4;  /* Starting row of scrollbar */
 +  }
 +
 +  printf("\033[1;%dH\1#01\016l", MARGIN+1);
 +  for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
 +    putchar('q');
 +
 +  printf("k\033[2;%dH\1#01x\017\1#02 %s \1#01\016x",
 +       MARGIN+1,
 +       pad_line(menu_title, 1, WIDTH-2*MARGIN-4));
 +
 +  printf("\033[3;%dH\1#01t", MARGIN+1);
 +  for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
 +    putchar('q');
 +  fputs("u\017", stdout);
 +
 +  for ( y = 4 ; y < 4+MENU_ROWS ; y++ )
 +    draw_row(y, sel, top, sbtop, sbbot);
 +
 +  printf("\033[%d;%dH\1#01\016m", y, MARGIN+1);
 +  for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
 +    putchar('q');
 +  fputs("j\017", stdout);
 +
 +  if ( edit_line && allowedit && !menu_master_passwd )
 +    printf("\1#08\033[%d;1H%s", TABMSG_ROW,
 +         pad_line("Press [Tab] to edit options", 1, WIDTH));
 +
 +  printf("\1#00\033[%d;1H", END_ROW);
 +}
 +
 +static void
 +clear_screen(void)
 +{
 +  fputs("\033e\033%@\033)0\033(B\1#00\033[?25l\033[2J", stdout);
 +}
 +
 +static const char *
 +edit_cmdline(char *input, int top)
 +{
 +  static char cmdline[MAX_CMDLINE_LEN];
 +  int key, len, prev_len, cursor;
 +  int redraw = 1;             /* We enter with the menu already drawn */
 +
 +  strncpy(cmdline, input, MAX_CMDLINE_LEN);
 +  cmdline[MAX_CMDLINE_LEN-1] = '\0';
 +
 +  len = cursor = strlen(cmdline);
 +  prev_len = 0;
 +
 +  for (;;) {
 +    if ( redraw > 1 ) {
 +      /* Clear and redraw whole screen */
 +      /* Enable ASCII on G0 and DEC VT on G1; do it in this order
 +       to avoid confusing the Linux console */
 +      clear_screen();
 +      draw_menu(-1, top, 1);
 +      prev_len = 0;
 +    }
 +
 +    if ( redraw > 0 ) {
 +      /* Redraw the command line */
 +      printf("\033[?25l\033[%d;1H\1#09> \1#10%s",
 +           CMDLINE_ROW, pad_line(cmdline, 0, prev_len));
 +      printf("\1#10\033[%d;3H%s\033[?25h",
 +           CMDLINE_ROW, pad_line(cmdline, 0, cursor));
 +      prev_len = len;
 +      redraw = 0;
 +    }
 +
 +    key = mygetkey(0);
 +
 +    switch( key ) {
 +    case KEY_CTRL('L'):
 +      redraw = 2;
 +      break;
 +
 +    case KEY_ENTER:
 +    case KEY_CTRL('J'):
 +      return cmdline;
 +
 +    case KEY_ESC:
 +    case KEY_CTRL('C'):
 +      return NULL;
 +
 +    case KEY_BACKSPACE:
 +    case KEY_DEL:
 +      if ( cursor ) {
 +      memmove(cmdline+cursor-1, cmdline+cursor, len-cursor+1);
 +      len--;
 +      cursor--;
 +      redraw = 1;
 +      }
 +      break;
 +
 +    case KEY_CTRL('D'):
 +    case KEY_DELETE:
 +      if ( cursor < len ) {
 +      memmove(cmdline+cursor, cmdline+cursor+1, len-cursor);
 +      len--;
 +      redraw = 1;
 +      }
 +      break;
 +
 +    case KEY_CTRL('U'):
 +      if ( len ) {
 +      len = cursor = 0;
 +      cmdline[len] = '\0';
 +      redraw = 1;
 +      }
 +      break;
 +
 +    case KEY_CTRL('W'):
 +      if ( cursor ) {
 +      int prevcursor = cursor;
 +
 +      while ( cursor && my_isspace(cmdline[cursor-1]) )
 +        cursor--;
 +
 +      while ( cursor && !my_isspace(cmdline[cursor-1]) )
 +        cursor--;
 +
 +      memmove(cmdline+cursor, cmdline+prevcursor, len-prevcursor+1);
 +      len -= (cursor-prevcursor);
 +      redraw = 1;
 +      }
 +      break;
 +
 +    case KEY_LEFT:
 +    case KEY_CTRL('B'):
 +      if ( cursor ) {
 +      cursor--;
 +      redraw = 1;
 +      }
 +      break;
 +
 +    case KEY_RIGHT:
 +    case KEY_CTRL('F'):
 +      if ( cursor < len ) {
 +      putchar(cmdline[cursor++]);
 +      }
 +      break;
 +
 +    case KEY_CTRL('K'):
 +      if ( cursor < len ) {
 +      cmdline[len = cursor] = '\0';
 +      redraw = 1;
 +      }
 +      break;
 +
 +    case KEY_HOME:
 +    case KEY_CTRL('A'):
 +      if ( cursor ) {
 +      cursor = 0;
 +      redraw = 1;
 +      }
 +      break;
 +
 +    case KEY_END:
 +    case KEY_CTRL('E'):
 +      if ( cursor != len ) {
 +      cursor = len;
 +      redraw = 1;
 +      }
 +      break;
 +
 +    default:
 +      if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) {
 +      if ( cursor == len ) {
 +        cmdline[len] = key;
 +        cmdline[++len] = '\0';
 +        cursor++;
 +        putchar(key);
 +        prev_len++;
 +      } else {
 +        memmove(cmdline+cursor+1, cmdline+cursor, len-cursor+1);
 +        cmdline[cursor++] = key;
 +        len++;
 +        redraw = 1;
 +      }
 +      }
 +      break;
 +    }
 +  }
 +}
 +
 +static inline int
 +shift_is_held(void)
 +{
 +  uint8_t shift_bits = *(uint8_t *)0x417;
 +
 +  return !!(shift_bits & 0x5d);       /* Caps/Scroll/Alt/Shift */
 +}
 +
 +static const char *
 +run_menu(void)
 +{
 +  int key;
 +  int done = 0;
 +  volatile int entry = defentry, prev_entry = -1;
 +  int top = 0, prev_top = -1;
 +  int clear = 1, to_clear;
 +  const char *cmdline = NULL;
 +  volatile clock_t key_timeout, timeout_left, this_timeout;
 +
 +  /* Note: for both key_timeout and timeout == 0 means no limit */
 +  timeout_left = key_timeout = timeout;
 +
 +  /* If we're in shiftkey mode, exit immediately unless a shift key is pressed */
 +  if ( shiftkey && !shift_is_held() ) {
 +    return menu_entries[defentry].cmdline;
 +  }
 +
 +  /* Handle both local and global timeout */
 +  if ( setjmp(timeout_jump) ) {
 +    entry = defentry;
 +
 +    if ( top < 0 || top < entry-MENU_ROWS+1 )
 +      top = max(0, entry-MENU_ROWS+1);
 +    else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
 +      top = min(entry, max(0,nentries-MENU_ROWS));
 +
 +    draw_menu(ontimeout ? -1 : entry, top, 1);
 +    cmdline = ontimeout ? ontimeout : menu_entries[entry].cmdline;
 +    done = 1;
 +  }
 +
 +  while ( !done ) {
 +    if ( entry < 0 )
 +      entry = 0;
 +    else if ( entry >= nentries )
 +      entry = nentries-1;
 +
 +    if ( top < 0 || top < entry-MENU_ROWS+1 )
 +      top = max(0, entry-MENU_ROWS+1);
 +    else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
 +      top = min(entry, max(0,nentries-MENU_ROWS));
 +
 +    /* Start with a clear screen */
 +    if ( clear ) {
 +      /* Clear and redraw whole screen */
 +      /* Enable ASCII on G0 and DEC VT on G1; do it in this order
 +       to avoid confusing the Linux console */
 +      clear_screen();
 +      clear = 0;
 +      prev_entry = prev_top = -1;
 +    }
 +
 +    if ( top != prev_top ) {
 +      draw_menu(entry, top, 1);
 +    } else if ( entry != prev_entry ) {
 +      draw_row(prev_entry-top+4, entry, top, 0, 0);
 +      draw_row(entry-top+4, entry, top, 0, 0);
 +    }
 +
 +    prev_entry = entry;  prev_top = top;
 +
 +    /* Cursor movement cancels timeout */
 +    if ( entry != defentry )
 +      key_timeout = 0;
 +
 +    if ( key_timeout ) {
 +      int tol = timeout_left/CLK_TCK;
 +      int nc = snprintf(NULL, 0, " Automatic boot in %d seconds ", tol);
 +      printf("\033[%d;%dH\1#14 Automatic boot in \1#15%d\1#14 seconds ",
 +           TIMEOUT_ROW, 1+((WIDTH-nc)>>1), tol);
 +      to_clear = 1;
 +    } else {
 +      to_clear = 0;
 +    }
 +
 +    this_timeout = min(min(key_timeout, timeout_left), CLK_TCK);
 +    key = mygetkey(this_timeout);
 +
 +    if ( key != KEY_NONE ) {
 +      timeout_left = key_timeout;
 +      if ( to_clear )
 +      printf("\033[%d;1H\1#00\033[K", TIMEOUT_ROW);
 +    }
 +
 +    switch ( key ) {
 +    case KEY_NONE:            /* Timeout */
 +      /* This is somewhat hacky, but this at least lets the user
 +       know what's going on, and still deals with "phantom inputs"
 +       e.g. on serial ports.
 +
 +       Warning: a timeout will boot the default entry without any
 +       password! */
 +      if ( key_timeout ) {
 +      if ( timeout_left <= this_timeout )
 +        longjmp(timeout_jump, 1);
 +
 +      timeout_left -= this_timeout;
 +      }
 +      break;
 +
 +    case KEY_CTRL('L'):
 +      clear = 1;
 +      break;
 +
 +    case KEY_ENTER:
 +    case KEY_CTRL('J'):
 +      key_timeout = 0;                /* Cancels timeout */
 +      if ( menu_entries[entry].passwd ) {
 +      clear = 1;
 +      done = ask_passwd(menu_entries[entry].passwd);
 +      } else {
 +      done = 1;
 +      }
 +      cmdline = menu_entries[entry].cmdline;
 +      break;
 +
 +    case KEY_UP:
 +    case KEY_CTRL('P'):
 +      if ( entry > 0 ) {
 +      entry--;
 +      if ( entry < top )
 +        top -= MENU_ROWS;
 +      }
 +      break;
 +
 +    case KEY_DOWN:
 +    case KEY_CTRL('N'):
 +      if ( entry < nentries-1 ) {
 +      entry++;
 +      if ( entry >= top+MENU_ROWS )
 +        top += MENU_ROWS;
 +      }
 +      break;
 +
 +    case KEY_PGUP:
 +    case KEY_LEFT:
 +    case KEY_CTRL('B'):
 +    case '<':
 +      entry -= MENU_ROWS;
 +      top   -= MENU_ROWS;
 +      break;
 +
 +    case KEY_PGDN:
 +    case KEY_RIGHT:
 +    case KEY_CTRL('F'):
 +    case '>':
 +    case ' ':
 +      entry += MENU_ROWS;
 +      top   += MENU_ROWS;
 +      break;
 +
 +    case '-':
 +      entry--;
 +      top--;
 +      break;
 +
 +    case '+':
 +      entry++;
 +      top++;
 +      break;
 +
 +    case KEY_CTRL('A'):
 +    case KEY_HOME:
 +      top = entry = 0;
 +      break;
 +
 +    case KEY_CTRL('E'):
 +    case KEY_END:
 +      entry = nentries - 1;
 +      top = max(0, nentries-MENU_ROWS);
 +      break;
 +
 +    case KEY_TAB:
 +      if ( allowedit ) {
 +      int ok = 1;
 +
 +      key_timeout = 0;        /* Cancels timeout */
 +      draw_row(entry-top+4, -1, top, 0, 0);
 +
 +      if ( menu_master_passwd ) {
 +        ok = ask_passwd(NULL);
 +        clear_screen();
 +        draw_menu(-1, top, 0);
 +      } else {
 +        /* Erase [Tab] message */
 +        printf("\033[%d;1H\1#00\033[K", TABMSG_ROW);
 +      }
 +
 +      if ( ok ) {
 +        cmdline = edit_cmdline(menu_entries[entry].cmdline, top);
 +        done = !!cmdline;
 +        clear = 1;            /* In case we hit [Esc] and done is null */
 +      } else {
 +        draw_row(entry-top+4, entry, top, 0, 0);
 +      }
 +      }
 +      break;
 +    case KEY_CTRL('C'):               /* Ctrl-C */
 +    case KEY_ESC:             /* Esc */
 +      if ( allowedit ) {
 +      done = 1;
 +      clear = 1;
 +      key_timeout = 0;
 +
 +      draw_row(entry-top+4, -1, top, 0, 0);
 +
 +      if ( menu_master_passwd )
 +        done = ask_passwd(NULL);
 +      }
 +      break;
 +    default:
 +      if ( key > 0 && key < 0xFF ) {
 +      key &= ~0x20;           /* Upper case */
 +      if ( menu_hotkeys[key] ) {
 +        key_timeout = 0;
 +        entry = menu_hotkeys[key] - menu_entries;
 +        /* Should we commit at this point? */
 +      }
 +      }
 +      break;
 +    }
 +  }
 +
 +  printf("\033[?25h");                /* Show cursor */
 +
 +  /* Return the label name so localboot and ipappend work */
 +  return cmdline;
 +}
 +
 +
 +static void
 +execute(const char *cmdline)
 +{
 +#ifdef __COM32__
 +  com32sys_t ireg;
 +  const char *p;
 +  char *q = __com32.cs_bounce;
 +  const char *kernel, *args;
 +
 +  memset(&ireg, 0, sizeof ireg);
 +
 +  kernel = q;
 +  p = cmdline;
 +  while ( *p && !my_isspace(*p) ) {
 +    *q++ = *p++;
 +  }
 +  *q++ = '\0';
 +
 +  args = q;
 +  while ( *p && my_isspace(*p) )
 +    p++;
 +
 +  strcpy(q, p);
 +
 +  if ( !strcmp(kernel, ".localboot") ) {
 +    ireg.eax.w[0] = 0x0014;   /* Local boot */
 +    ireg.edx.w[0] = strtoul(args, NULL, 0);
 +  } else {
 +    ireg.eax.w[0] = 0x0016;   /* Run kernel image */
 +    ireg.esi.w[0] = OFFS(kernel);
 +    ireg.ds       = SEG(kernel);
 +    ireg.ebx.w[0] = OFFS(args);
 +    ireg.es       = SEG(args);
 +    /* ireg.ecx.l    = 0; */          /* We do ipappend "manually" */
 +    /* ireg.edx.l    = 0; */
 +  }
 +
 +  __intcall(0x22, &ireg, NULL);
 +
 +  /* If this returns, something went bad; return to menu */
 +#else
 +  /* For testing... */
 +  printf("\n\033[0m>>> %s\n", cmdline);
 +  exit(0);
 +#endif
 +}
 +
 +int menu_main(int argc, char *argv[])
 +{
 +  const char *cmdline;
 +
 +  (void)argc;
 +
 +  install_default_color_table();
 +  fputs("\1#00", stdout);
 +
 +  parse_config(argv[1]);
 +
 +  if (draw_background && menu_background)
 +    draw_background(menu_background);
 +
 +  if ( !nentries ) {
 +    fputs("No LABEL entries found in configuration file!\n", stdout);
 +    return 1;                 /* Error! */
 +  }
 +
 +  for(;;) {
 +    cmdline = run_menu();
 +
 +    printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
-     else
++    if ( cmdline ) {
 +      execute(cmdline);
++      if ( onerror )
++      execute(onerror);
++    } else {
 +      return 0;                       /* Exit */
++    }
 +  }
 +}
@@@ -35,9 -36,8 +37,10 @@@ char *onerror     = NULL
  
  char *menu_master_passwd = NULL;
  
 +char *menu_background = NULL;
 +
  struct menu_entry menu_entries[MAX_ENTRIES];
+ struct menu_entry hide_entries[MAX_ENTRIES];
  struct menu_entry *menu_hotkeys[256];
  
  #define astrdup(x) ({ char *__x = (x); \