/* ----------------------------------------------------------------------- * * * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved * Copyright 2009-2014 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 #include #include #include #include #include #include #include #include #include #include #include #include #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 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) { /* Clear the message from the screen */ print_timeout_message(0, HIDDEN_ROW, ""); return hide_key[key]; /* NULL if no MENU HIDEKEY in effect */ } 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 - 1) { 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) { uint32_t type = parse_image_type(cmdline); execute(cmdline, type, false); if (cm->onerror) { type = parse_image_type(cm->onerror); execute(cm->onerror, type, true); } } else { return 0; /* Exit */ } } }