diff options
Diffstat (limited to 'contrib/syslinux-4.02/com32/menu/menumain.c')
-rw-r--r-- | contrib/syslinux-4.02/com32/menu/menumain.c | 1164 |
1 files changed, 1164 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/menu/menumain.c b/contrib/syslinux-4.02/com32/menu/menumain.c new file mode 100644 index 0000000..06725f3 --- /dev/null +++ b/contrib/syslinux-4.02/com32/menu/menumain.c @@ -0,0 +1,1164 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * + * 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., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, 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. + */ + +#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 <com32.h> +#include <syslinux/adv.h> + +#include "menu.h" + +/* The symbol "cm" always refers to the current menu across this file... */ +static struct menu *cm; + +const struct menu_parameter mparm[NPARAMS] = { + [P_WIDTH] = {"width", 0}, + [P_MARGIN] = {"margin", 10}, + [P_PASSWD_MARGIN] = {"passwordmargin", 3}, + [P_MENU_ROWS] = {"rows", 12}, + [P_TABMSG_ROW] = {"tabmsgrow", 18}, + [P_CMDLINE_ROW] = {"cmdlinerow", 18}, + [P_END_ROW] = {"endrow", -1}, + [P_PASSWD_ROW] = {"passwordrow", 11}, + [P_TIMEOUT_ROW] = {"timeoutrow", 20}, + [P_HELPMSG_ROW] = {"helpmsgrow", 22}, + [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1}, + [P_HSHIFT] = {"hshift", 0}, + [P_VSHIFT] = {"vshift", 0}, + [P_HIDDEN_ROW] = {"hiddenrow", -2}, +}; + +/* These macros assume "cm" is a pointer to the current menu */ +#define WIDTH (cm->mparm[P_WIDTH]) +#define MARGIN (cm->mparm[P_MARGIN]) +#define PASSWD_MARGIN (cm->mparm[P_PASSWD_MARGIN]) +#define MENU_ROWS (cm->mparm[P_MENU_ROWS]) +#define TABMSG_ROW (cm->mparm[P_TABMSG_ROW]+VSHIFT) +#define CMDLINE_ROW (cm->mparm[P_CMDLINE_ROW]+VSHIFT) +#define END_ROW (cm->mparm[P_END_ROW]) +#define PASSWD_ROW (cm->mparm[P_PASSWD_ROW]+VSHIFT) +#define TIMEOUT_ROW (cm->mparm[P_TIMEOUT_ROW]+VSHIFT) +#define HELPMSG_ROW (cm->mparm[P_HELPMSG_ROW]+VSHIFT) +#define HELPMSGEND_ROW (cm->mparm[P_HELPMSGEND_ROW]) +#define HSHIFT (cm->mparm[P_HSHIFT]) +#define VSHIFT (cm->mparm[P_VSHIFT]) +#define HIDDEN_ROW (cm->mparm[P_HIDDEN_ROW]) + +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; + char marker; + + if (!p) + p = ""; + + switch (entry->action) { + case MA_SUBMENU: + marker = '>'; + break; + case MA_EXIT: + marker = '<'; + break; + default: + marker = 0; + break; + } + + if (marker) + width -= 2; + + 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--; + } + } + + if (marker) { + putchar(' '); + putchar(marker); + } +} + +static void draw_row(int y, int sel, int top, int sbtop, int sbbot) +{ + int i = (y - 4 - VSHIFT) + top; + int dis = (i < cm->nentries) && is_disabled(cm->menu_entries[i]); + + printf("\033[%d;%dH\1#1\016x\017%s ", + y, MARGIN + 1 + HSHIFT, + (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3"); + + if (i >= cm->nentries) { + fputs(pad_line("", 0, WIDTH - 2 * MARGIN - 4), stdout); + } else { + display_entry(cm->menu_entries[i], + (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3", + (i == sel) ? "\1#6" : dis ? "\2#17" : "\1#4", + WIDTH - 2 * MARGIN - 4); + } + + if (cm->nentries <= MENU_ROWS) { + printf(" \1#1\016x\017"); + } else if (sbtop > 0) { + if (y >= sbtop && y <= sbbot) + printf(" \1#7\016a\017"); + else + printf(" \1#1\016x\017"); + } else { + putchar(' '); /* Don't modify the scrollbar */ + } +} + +static jmp_buf timeout_jump; + +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) +{ + char user_passwd[WIDTH], *p; + int done; + int key; + int x; + int rv; + + printf("\033[%d;%dH\2#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\2#12 %s \033[%d;%dH\2#13", + PASSWD_ROW, (WIDTH - (strlen(cm->messages[MSG_PASSPROMPT]) + 2)) / 2, + cm->messages[MSG_PASSPROMPT], PASSWD_ROW + 1, PASSWD_MARGIN + 3); + + drain_keyboard(); + + /* 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'; + + rv = (cm->menu_master_passwd && + passwd_compare(cm->menu_master_passwd, user_passwd)) + || (menu_entry && passwd_compare(menu_entry, user_passwd)); + + /* Clean up */ + memset(user_passwd, 0, WIDTH); + drain_keyboard(); + + return rv; +} + +static void draw_menu(int sel, int top, int edit_line) +{ + int x, y; + int sbtop = 0, sbbot = 0; + const char *tabmsg; + int tabmsg_len; + + if (cm->nentries > MENU_ROWS) { + int sblen = max(MENU_ROWS * MENU_ROWS / cm->nentries, 1); + sbtop = (MENU_ROWS - sblen + 1) * top / (cm->nentries - MENU_ROWS + 1); + sbbot = sbtop + sblen - 1; + sbtop += 4; + sbbot += 4; /* Starting row of scrollbar */ + } + + printf("\033[%d;%dH\1#1\016l", VSHIFT + 1, HSHIFT + MARGIN + 1); + for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++) + putchar('q'); + + printf("k\033[%d;%dH\1#1x\017\1#2 %s \1#1\016x", + VSHIFT + 2, + HSHIFT + MARGIN + 1, pad_line(cm->title, 1, WIDTH - 2 * MARGIN - 4)); + + printf("\033[%d;%dH\1#1t", VSHIFT + 3, HSHIFT + MARGIN + 1); + for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++) + putchar('q'); + fputs("u\017", stdout); + + for (y = 4 + VSHIFT; y < 4 + VSHIFT + MENU_ROWS; y++) + draw_row(y, sel, top, sbtop, sbbot); + + printf("\033[%d;%dH\1#1\016m", y, HSHIFT + MARGIN + 1); + for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++) + putchar('q'); + fputs("j\017", stdout); + + if (edit_line && cm->allowedit && !cm->menu_master_passwd) + tabmsg = cm->messages[MSG_TAB]; + else + tabmsg = cm->messages[MSG_NOTAB]; + + tabmsg_len = strlen(tabmsg); + + printf("\1#8\033[%d;%dH%s", + TABMSG_ROW, 1 + HSHIFT + ((WIDTH - tabmsg_len) >> 1), tabmsg); + printf("\1#0\033[%d;1H", END_ROW); +} + +static void clear_screen(void) +{ + fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout); +} + +static void display_help(const char *text) +{ + int row; + const char *p; + + if (!text) { + text = ""; + printf("\1#0\033[%d;1H", HELPMSG_ROW); + } else { + printf("\2#16\033[%d;1H", HELPMSG_ROW); + } + + for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) { + switch (*p) { + case '\r': + case '\f': + case '\v': + case '\033': + break; + case '\n': + printf("\033[K\033[%d;1H", ++row); + break; + default: + putchar(*p); + } + } + + fputs("\033[K", stdout); + + while (row <= HELPMSGEND_ROW) { + printf("\033[K\033[%d;1H", ++row); + } +} + +static void show_fkey(int key) +{ + int fkey; + + while (1) { + switch (key) { + case KEY_F1: + fkey = 0; + break; + case KEY_F2: + fkey = 1; + break; + case KEY_F3: + fkey = 2; + break; + case KEY_F4: + fkey = 3; + break; + case KEY_F5: + fkey = 4; + break; + case KEY_F6: + fkey = 5; + break; + case KEY_F7: + fkey = 6; + break; + case KEY_F8: + fkey = 7; + break; + case KEY_F9: + fkey = 8; + break; + case KEY_F10: + fkey = 9; + break; + case KEY_F11: + fkey = 10; + break; + case KEY_F12: + fkey = 11; + break; + default: + fkey = -1; + break; + } + + if (fkey == -1) + break; + + if (cm->fkeyhelp[fkey].textname) + key = show_message_file(cm->fkeyhelp[fkey].textname, + cm->fkeyhelp[fkey].background); + else + break; + } +} + +static const char *edit_cmdline(const 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 */ + + strlcpy(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#9> \2#10%s", + CMDLINE_ROW, pad_line(cmdline, 0, max(len, prev_len))); + printf("\2#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 -= (prevcursor - cursor); + 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; + + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + show_fkey(key); + 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 void print_timeout_message(int tol, int row, const char *msg) +{ + static int last_msg_len = 0; + char buf[256]; + int nc = 0, nnc, padc; + const char *tp = msg; + char tc; + char *tq = buf; + + while ((size_t) (tq - buf) < (sizeof buf - 16) && (tc = *tp)) { + tp++; + if (tc == '#') { + nnc = sprintf(tq, "\2#15%d\2#14", tol); + tq += nnc; + nc += nnc - 8; /* 8 formatting characters */ + } else if (tc == '{') { + /* Deal with {singular[,dual],plural} constructs */ + struct { + const char *s, *e; + } tx[3]; + const char *tpp; + int n = 0; + + memset(tx, 0, sizeof tx); + + tx[0].s = tp; + + while (*tp && *tp != '}') { + if (*tp == ',' && n < 2) { + tx[n].e = tp; + n++; + tx[n].s = tp + 1; + } + tp++; + } + tx[n].e = tp; + + if (*tp) + tp++; /* Skip final bracket */ + + if (!tx[1].s) + tx[1] = tx[0]; + if (!tx[2].s) + tx[2] = tx[1]; + + /* Now [0] is singular, [1] is dual, and [2] is plural, + even if the user only specified some of them. */ + + switch (tol) { + case 1: + n = 0; + break; + case 2: + n = 1; + break; + default: + n = 2; + break; + } + + for (tpp = tx[n].s; tpp < tx[n].e; tpp++) { + if ((size_t) (tq - buf) < (sizeof buf)) { + *tq++ = *tpp; + nc++; + } + } + } else { + *tq++ = tc; + nc++; + } + } + *tq = '\0'; + + if (nc >= last_msg_len) { + padc = 0; + } else { + padc = (last_msg_len - nc + 1) >> 1; + } + + printf("\033[%d;%dH\2#14%*s%s%*s", row, + HSHIFT + 1 + ((WIDTH - nc) >> 1) - padc, + padc, "", buf, padc, ""); + + last_msg_len = nc; +} + +/* Set the background screen, etc. */ +static void prepare_screen_for_menu(void) +{ + console_color_table = cm->color_table; + console_color_table_size = menu_color_table_size; + set_background(cm->menu_background); +} + +static const char *do_hidden_menu(void) +{ + int key; + int timeout_left, this_timeout; + + clear_screen(); + + if (!setjmp(timeout_jump)) { + timeout_left = cm->timeout; + + while (!cm->timeout || timeout_left) { + int tol = timeout_left / CLK_TCK; + + print_timeout_message(tol, HIDDEN_ROW, cm->messages[MSG_AUTOBOOT]); + + this_timeout = min(timeout_left, CLK_TCK); + key = mygetkey(this_timeout); + + if (key != KEY_NONE) + return NULL; /* Key pressed */ + + timeout_left -= this_timeout; + } + } + + /* Clear the message from the screen */ + print_timeout_message(0, HIDDEN_ROW, ""); + + if (cm->ontimeout) + return cm->ontimeout; + else + return cm->menu_entries[cm->defentry]->cmdline; /* Default entry */ +} + +static const char *run_menu(void) +{ + int key; + int done = 0; + volatile int entry = cm->curentry; + int prev_entry = -1; + volatile int top = cm->curtop; + int prev_top = -1; + int clear = 1, to_clear; + const char *cmdline = NULL; + volatile clock_t key_timeout, timeout_left, this_timeout; + const struct menu_entry *me; + bool hotkey = false; + + /* Note: for both key_timeout and timeout == 0 means no limit */ + timeout_left = key_timeout = cm->timeout; + + /* If we're in shiftkey mode, exit immediately unless a shift key + is pressed */ + if (shiftkey && !shift_is_held()) { + return cm->menu_entries[cm->defentry]->cmdline; + } else { + shiftkey = 0; + } + + /* Do this before hiddenmenu handling, so we show the background */ + prepare_screen_for_menu(); + + /* Handle hiddenmenu */ + if (hiddenmenu) { + cmdline = do_hidden_menu(); + if (cmdline) + return cmdline; + + /* Otherwise display the menu now; the timeout has already been + cancelled, since the user pressed a key. */ + hiddenmenu = 0; + key_timeout = 0; + } + + /* Handle both local and global timeout */ + if (setjmp(timeout_jump)) { + entry = cm->defentry; + + if (top < 0 || top < entry - MENU_ROWS + 1) + top = max(0, entry - MENU_ROWS + 1); + else if (top > entry || top > max(0, cm->nentries - MENU_ROWS)) + top = min(entry, max(0, cm->nentries - MENU_ROWS)); + + draw_menu(cm->ontimeout ? -1 : entry, top, 1); + cmdline = + cm->ontimeout ? cm->ontimeout : cm->menu_entries[entry]->cmdline; + done = 1; + } + + while (!done) { + if (entry <= 0) { + entry = 0; + while (entry < cm->nentries && is_disabled(cm->menu_entries[entry])) + entry++; + } + if (entry >= cm->nentries) { + entry = cm->nentries - 1; + while (entry > 0 && is_disabled(cm->menu_entries[entry])) + entry--; + } + + me = cm->menu_entries[entry]; + + if (top < 0 || top < entry - MENU_ROWS + 1) + top = max(0, entry - MENU_ROWS + 1); + else if (top > entry || top > max(0, cm->nentries - MENU_ROWS)) + top = min(entry, max(0, cm->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 */ + if (clear >= 2) + prepare_screen_for_menu(); + clear_screen(); + clear = 0; + prev_entry = prev_top = -1; + } + + if (top != prev_top) { + draw_menu(entry, top, 1); + display_help(me->helptext); + } else if (entry != prev_entry) { + draw_row(prev_entry - top + 4 + VSHIFT, entry, top, 0, 0); + draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0); + display_help(me->helptext); + } + + prev_entry = entry; + prev_top = top; + cm->curentry = entry; + cm->curtop = top; + + /* Cursor movement cancels timeout */ + if (entry != cm->defentry) + key_timeout = 0; + + if (key_timeout) { + int tol = timeout_left / CLK_TCK; + print_timeout_message(tol, TIMEOUT_ROW, cm->messages[MSG_AUTOBOOT]); + to_clear = 1; + } else { + to_clear = 0; + } + + if (hotkey && me->immediate) { + /* If the hotkey was flagged immediate, simulate pressing ENTER */ + key = KEY_ENTER; + } else { + this_timeout = min(min(key_timeout, timeout_left), + (clock_t) CLK_TCK); + key = mygetkey(this_timeout); + + if (key != KEY_NONE) { + timeout_left = key_timeout; + if (to_clear) + printf("\033[%d;1H\1#0\033[K", TIMEOUT_ROW); + } + } + + hotkey = false; + + 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 (me->passwd) { + clear = 1; + done = ask_passwd(me->passwd); + } else { + done = 1; + } + cmdline = NULL; + if (done) { + switch (me->action) { + case MA_CMD: + cmdline = me->cmdline; + break; + case MA_SUBMENU: + case MA_GOTO: + case MA_EXIT: + done = 0; + clear = 2; + cm = me->submenu; + entry = cm->curentry; + top = cm->curtop; + break; + case MA_QUIT: + /* Quit menu system */ + done = 1; + clear = 1; + draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0); + break; + case MA_HELP: + key = show_message_file(me->cmdline, me->background); + /* If the exit was an F-key, display that help screen */ + show_fkey(key); + done = 0; + clear = 1; + break; + default: + done = 0; + break; + } + } + if (done && !me->passwd) { + /* Only save a new default if we don't have a password... */ + if (me->save && me->label) { + syslinux_setadv(ADV_MENUSAVE, strlen(me->label), me->label); + syslinux_adv_write(); + } + } + break; + + case KEY_UP: + case KEY_CTRL('P'): + while (entry > 0) { + entry--; + if (entry < top) + top -= MENU_ROWS; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_DOWN: + case KEY_CTRL('N'): + while (entry < cm->nentries - 1) { + entry++; + if (entry >= top + MENU_ROWS) + top += MENU_ROWS; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_PGUP: + case KEY_LEFT: + case KEY_CTRL('B'): + case '<': + entry -= MENU_ROWS; + top -= MENU_ROWS; + while (entry > 0 && is_disabled(cm->menu_entries[entry])) { + entry--; + if (entry < top) + top -= MENU_ROWS; + } + break; + + case KEY_PGDN: + case KEY_RIGHT: + case KEY_CTRL('F'): + case '>': + case ' ': + entry += MENU_ROWS; + top += MENU_ROWS; + while (entry < cm->nentries - 1 + && is_disabled(cm->menu_entries[entry])) { + entry++; + if (entry >= top + MENU_ROWS) + top += MENU_ROWS; + } + break; + + case '-': + while (entry > 0) { + entry--; + top--; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case '+': + while (entry < cm->nentries - 1) { + entry++; + top++; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_CTRL('A'): + case KEY_HOME: + top = entry = 0; + break; + + case KEY_CTRL('E'): + case KEY_END: + entry = cm->nentries - 1; + top = max(0, cm->nentries - MENU_ROWS); + break; + + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + show_fkey(key); + clear = 1; + break; + + case KEY_TAB: + if (cm->allowedit && me->action == MA_CMD) { + int ok = 1; + + key_timeout = 0; /* Cancels timeout */ + draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0); + + if (cm->menu_master_passwd) { + ok = ask_passwd(NULL); + clear_screen(); + draw_menu(-1, top, 0); + } else { + /* Erase [Tab] message and help text */ + printf("\033[%d;1H\1#0\033[K", TABMSG_ROW); + display_help(NULL); + } + + if (ok) { + cmdline = edit_cmdline(me->cmdline, top); + done = !!cmdline; + clear = 1; /* In case we hit [Esc] and done is null */ + } else { + draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0); + } + } + break; + case KEY_CTRL('C'): /* Ctrl-C */ + case KEY_ESC: /* Esc */ + if (cm->parent) { + cm = cm->parent; + clear = 2; + entry = cm->curentry; + top = cm->curtop; + } else if (cm->allowedit) { + done = 1; + clear = 1; + key_timeout = 0; + + draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0); + + if (cm->menu_master_passwd) + done = ask_passwd(NULL); + } + break; + default: + if (key > 0 && key < 0xFF) { + key &= ~0x20; /* Upper case */ + if (cm->menu_hotkeys[key]) { + key_timeout = 0; + entry = cm->menu_hotkeys[key]->entry; + /* Should we commit at this point? */ + hotkey = true; + } + } + break; + } + } + + printf("\033[?25h"); /* Show cursor */ + + /* Return the label name so localboot and ipappend work */ + return cmdline; +} + +int main(int argc, char *argv[]) +{ + const char *cmdline; + struct menu *m; + int rows, cols; + int i; + + (void)argc; + + parse_configs(argv + 1); + + /* + * We don't start the console until we have parsed the configuration + * file, since the configuration file might impact the console + * configuration, e.g. MENU RESOLUTION. + */ + start_console(); + if (getscreensize(1, &rows, &cols)) { + /* Unknown screen size? */ + rows = 24; + cols = 80; + } + + /* Some postprocessing for all menus */ + for (m = menu_list; m; m = m->next) { + if (!m->mparm[P_WIDTH]) + m->mparm[P_WIDTH] = cols; + + /* If anyone has specified negative parameters, consider them + relative to the bottom row of the screen. */ + for (i = 0; i < NPARAMS; i++) + if (m->mparm[i] < 0) + m->mparm[i] = max(m->mparm[i] + rows, 0); + } + + cm = start_menu; + + if (!cm->nentries) { + fputs("Initial menu has no LABEL entries!\n", stdout); + return 1; /* Error! */ + } + + for (;;) { + local_cursor_enable(true); + cmdline = run_menu(); + + if (clearmenu) + clear_screen(); + + local_cursor_enable(false); + printf("\033[?25h\033[%d;1H\033[0m", END_ROW); + + if (cmdline) { + execute(cmdline, KT_NONE); + if (cm->onerror) + execute(cm->onerror, KT_NONE); + } else { + return 0; /* Exit */ + } + } +} |