1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved 4 * Copyright 2009-2014 Intel Corporation; author: H. Peter Anvin 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 9 * Boston MA 02110-1301, USA; either version 2 of the License, or 10 * (at your option) any later version; incorporated herein by reference. 11 * 12 * ----------------------------------------------------------------------- */ 13 14 /* 15 * menumain.c 16 * 17 * Simple menu system which displays a list and allows the user to select 18 * a command line and/or edit it. 19 */ 20 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 <com32.h> 31 #include <core.h> 32 #include <syslinux/adv.h> 33 #include <syslinux/boot.h> 34 35 #include "menu.h" 36 37 /* The symbol "cm" always refers to the current menu across this file... */ 38 static struct menu *cm; 39 40 const struct menu_parameter mparm[NPARAMS] = { 41 [P_WIDTH] = {"width", 0}, 42 [P_MARGIN] = {"margin", 10}, 43 [P_PASSWD_MARGIN] = {"passwordmargin", 3}, 44 [P_MENU_ROWS] = {"rows", 12}, 45 [P_TABMSG_ROW] = {"tabmsgrow", 18}, 46 [P_CMDLINE_ROW] = {"cmdlinerow", 18}, 47 [P_END_ROW] = {"endrow", -1}, 48 [P_PASSWD_ROW] = {"passwordrow", 11}, 49 [P_TIMEOUT_ROW] = {"timeoutrow", 20}, 50 [P_HELPMSG_ROW] = {"helpmsgrow", 22}, 51 [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1}, 52 [P_HSHIFT] = {"hshift", 0}, 53 [P_VSHIFT] = {"vshift", 0}, 54 [P_HIDDEN_ROW] = {"hiddenrow", -2}, 55 }; 56 57 /* These macros assume "cm" is a pointer to the current menu */ 58 #define WIDTH (cm->mparm[P_WIDTH]) 59 #define MARGIN (cm->mparm[P_MARGIN]) 60 #define PASSWD_MARGIN (cm->mparm[P_PASSWD_MARGIN]) 61 #define MENU_ROWS (cm->mparm[P_MENU_ROWS]) 62 #define TABMSG_ROW (cm->mparm[P_TABMSG_ROW]+VSHIFT) 63 #define CMDLINE_ROW (cm->mparm[P_CMDLINE_ROW]+VSHIFT) 64 #define END_ROW (cm->mparm[P_END_ROW]) 65 #define PASSWD_ROW (cm->mparm[P_PASSWD_ROW]+VSHIFT) 66 #define TIMEOUT_ROW (cm->mparm[P_TIMEOUT_ROW]+VSHIFT) 67 #define HELPMSG_ROW (cm->mparm[P_HELPMSG_ROW]+VSHIFT) 68 #define HELPMSGEND_ROW (cm->mparm[P_HELPMSGEND_ROW]) 69 #define HSHIFT (cm->mparm[P_HSHIFT]) 70 #define VSHIFT (cm->mparm[P_VSHIFT]) 71 #define HIDDEN_ROW (cm->mparm[P_HIDDEN_ROW]) 72 73 static char *pad_line(const char *text, int align, int width) 74 { 75 static char buffer[MAX_CMDLINE_LEN]; 76 int n, p; 77 78 if (width >= (int)sizeof buffer) 79 return NULL; /* Can't do it */ 80 81 n = strlen(text); 82 if (n >= width) 83 n = width; 84 85 memset(buffer, ' ', width); 86 buffer[width] = 0; 87 p = ((width - n) * align) >> 1; 88 memcpy(buffer + p, text, n); 89 90 return buffer; 91 } 92 93 /* Display an entry, with possible hotkey highlight. Assumes 94 that the current attribute is the non-hotkey one, and will 95 guarantee that as an exit condition as well. */ 96 static void 97 display_entry(const struct menu_entry *entry, const char *attrib, 98 const char *hotattrib, int width) 99 { 100 const char *p = entry->displayname; 101 char marker; 102 103 if (!p) 104 p = ""; 105 106 switch (entry->action) { 107 case MA_SUBMENU: 108 marker = '>'; 109 break; 110 case MA_EXIT: 111 marker = '<'; 112 break; 113 default: 114 marker = 0; 115 break; 116 } 117 118 if (marker) 119 width -= 2; 120 121 while (width) { 122 if (*p) { 123 if (*p == '^') { 124 p++; 125 if (*p && ((unsigned char)*p & ~0x20) == entry->hotkey) { 126 fputs(hotattrib, stdout); 127 putchar(*p++); 128 fputs(attrib, stdout); 129 width--; 130 } 131 } else { 132 putchar(*p++); 133 width--; 134 } 135 } else { 136 putchar(' '); 137 width--; 138 } 139 } 140 141 if (marker) { 142 putchar(' '); 143 putchar(marker); 144 } 145 } 146 147 static void draw_row(int y, int sel, int top, int sbtop, int sbbot) 148 { 149 int i = (y - 4 - VSHIFT) + top; 150 int dis = (i < cm->nentries) && is_disabled(cm->menu_entries[i]); 151 152 printf("\033[%d;%dH\1#1\016x\017%s ", 153 y, MARGIN + 1 + HSHIFT, 154 (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3"); 155 156 if (i >= cm->nentries) { 157 fputs(pad_line("", 0, WIDTH - 2 * MARGIN - 4), stdout); 158 } else { 159 display_entry(cm->menu_entries[i], 160 (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3", 161 (i == sel) ? "\1#6" : dis ? "\2#17" : "\1#4", 162 WIDTH - 2 * MARGIN - 4); 163 } 164 165 if (cm->nentries <= MENU_ROWS) { 166 printf(" \1#1\016x\017"); 167 } else if (sbtop > 0) { 168 if (y >= sbtop && y <= sbbot) 169 printf(" \1#7\016a\017"); 170 else 171 printf(" \1#1\016x\017"); 172 } else { 173 putchar(' '); /* Don't modify the scrollbar */ 174 } 175 } 176 177 static jmp_buf timeout_jump; 178 179 int mygetkey(clock_t timeout) 180 { 181 clock_t t0, t; 182 clock_t tto, to; 183 int key; 184 185 if (!totaltimeout) 186 return get_key(stdin, timeout); 187 188 for (;;) { 189 tto = min(totaltimeout, INT_MAX); 190 to = timeout ? min(tto, timeout) : tto; 191 192 t0 = times(NULL); 193 key = get_key(stdin, to); 194 t = times(NULL) - t0; 195 196 if (totaltimeout <= t) 197 longjmp(timeout_jump, 1); 198 199 totaltimeout -= t; 200 201 if (key != KEY_NONE) 202 return key; 203 204 if (timeout) { 205 if (timeout <= t) 206 return KEY_NONE; 207 208 timeout -= t; 209 } 210 } 211 } 212 213 static int ask_passwd(const char *menu_entry) 214 { 215 char user_passwd[WIDTH], *p; 216 int done; 217 int key; 218 int x; 219 int rv; 220 221 printf("\033[%d;%dH\2#11\016l", PASSWD_ROW, PASSWD_MARGIN + 1); 222 for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++) 223 putchar('q'); 224 225 printf("k\033[%d;%dHx", PASSWD_ROW + 1, PASSWD_MARGIN + 1); 226 for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++) 227 putchar(' '); 228 229 printf("x\033[%d;%dHm", PASSWD_ROW + 2, PASSWD_MARGIN + 1); 230 for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++) 231 putchar('q'); 232 233 printf("j\017\033[%d;%dH\2#12 %s \033[%d;%dH\2#13", 234 PASSWD_ROW, (WIDTH - (strlen(cm->messages[MSG_PASSPROMPT]) + 2)) / 2, 235 cm->messages[MSG_PASSPROMPT], PASSWD_ROW + 1, PASSWD_MARGIN + 3); 236 237 drain_keyboard(); 238 239 /* Actually allow user to type a password, then compare to the SHA1 */ 240 done = 0; 241 p = user_passwd; 242 243 while (!done) { 244 key = mygetkey(0); 245 246 switch (key) { 247 case KEY_ENTER: 248 case KEY_CTRL('J'): 249 done = 1; 250 break; 251 252 case KEY_ESC: 253 case KEY_CTRL('C'): 254 p = user_passwd; /* No password entered */ 255 done = 1; 256 break; 257 258 case KEY_BACKSPACE: 259 case KEY_DEL: 260 case KEY_DELETE: 261 if (p > user_passwd) { 262 printf("\b \b"); 263 p--; 264 } 265 break; 266 267 case KEY_CTRL('U'): 268 while (p > user_passwd) { 269 printf("\b \b"); 270 p--; 271 } 272 break; 273 274 default: 275 if (key >= ' ' && key <= 0xFF && 276 (p - user_passwd) < WIDTH - 2 * PASSWD_MARGIN - 5) { 277 *p++ = key; 278 putchar('*'); 279 } 280 break; 281 } 282 } 283 284 if (p == user_passwd) 285 return 0; /* No password entered */ 286 287 *p = '\0'; 288 289 rv = (cm->menu_master_passwd && 290 passwd_compare(cm->menu_master_passwd, user_passwd)) 291 || (menu_entry && passwd_compare(menu_entry, user_passwd)); 292 293 /* Clean up */ 294 memset(user_passwd, 0, WIDTH); 295 drain_keyboard(); 296 297 return rv; 298 } 299 300 static void draw_menu(int sel, int top, int edit_line) 301 { 302 int x, y; 303 int sbtop = 0, sbbot = 0; 304 const char *tabmsg; 305 int tabmsg_len; 306 307 if (cm->nentries > MENU_ROWS) { 308 int sblen = max(MENU_ROWS * MENU_ROWS / cm->nentries, 1); 309 sbtop = (MENU_ROWS - sblen + 1) * top / (cm->nentries - MENU_ROWS + 1); 310 sbbot = sbtop + sblen - 1; 311 sbtop += 4; 312 sbbot += 4; /* Starting row of scrollbar */ 313 } 314 315 printf("\033[%d;%dH\1#1\016l", VSHIFT + 1, HSHIFT + MARGIN + 1); 316 for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++) 317 putchar('q'); 318 319 printf("k\033[%d;%dH\1#1x\017\1#2 %s \1#1\016x", 320 VSHIFT + 2, 321 HSHIFT + MARGIN + 1, pad_line(cm->title, 1, WIDTH - 2 * MARGIN - 4)); 322 323 printf("\033[%d;%dH\1#1t", VSHIFT + 3, HSHIFT + MARGIN + 1); 324 for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++) 325 putchar('q'); 326 fputs("u\017", stdout); 327 328 for (y = 4 + VSHIFT; y < 4 + VSHIFT + MENU_ROWS; y++) 329 draw_row(y, sel, top, sbtop, sbbot); 330 331 printf("\033[%d;%dH\1#1\016m", y, HSHIFT + MARGIN + 1); 332 for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++) 333 putchar('q'); 334 fputs("j\017", stdout); 335 336 if (edit_line && cm->allowedit && !cm->menu_master_passwd) 337 tabmsg = cm->messages[MSG_TAB]; 338 else 339 tabmsg = cm->messages[MSG_NOTAB]; 340 341 tabmsg_len = strlen(tabmsg); 342 343 printf("\1#8\033[%d;%dH%s", 344 TABMSG_ROW, 1 + HSHIFT + ((WIDTH - tabmsg_len) >> 1), tabmsg); 345 printf("\1#0\033[%d;1H", END_ROW); 346 } 347 348 static void clear_screen(void) 349 { 350 fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout); 351 } 352 353 static void display_help(const char *text) 354 { 355 int row; 356 const char *p; 357 358 if (!text) { 359 text = ""; 360 printf("\1#0\033[%d;1H", HELPMSG_ROW); 361 } else { 362 printf("\2#16\033[%d;1H", HELPMSG_ROW); 363 } 364 365 for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) { 366 switch (*p) { 367 case '\r': 368 case '\f': 369 case '\v': 370 case '\033': 371 break; 372 case '\n': 373 printf("\033[K\033[%d;1H", ++row); 374 break; 375 default: 376 putchar(*p); 377 } 378 } 379 380 fputs("\033[K", stdout); 381 382 while (row <= HELPMSGEND_ROW) { 383 printf("\033[K\033[%d;1H", ++row); 384 } 385 } 386 387 static void show_fkey(int key) 388 { 389 int fkey; 390 391 while (1) { 392 switch (key) { 393 case KEY_F1: 394 fkey = 0; 395 break; 396 case KEY_F2: 397 fkey = 1; 398 break; 399 case KEY_F3: 400 fkey = 2; 401 break; 402 case KEY_F4: 403 fkey = 3; 404 break; 405 case KEY_F5: 406 fkey = 4; 407 break; 408 case KEY_F6: 409 fkey = 5; 410 break; 411 case KEY_F7: 412 fkey = 6; 413 break; 414 case KEY_F8: 415 fkey = 7; 416 break; 417 case KEY_F9: 418 fkey = 8; 419 break; 420 case KEY_F10: 421 fkey = 9; 422 break; 423 case KEY_F11: 424 fkey = 10; 425 break; 426 case KEY_F12: 427 fkey = 11; 428 break; 429 default: 430 fkey = -1; 431 break; 432 } 433 434 if (fkey == -1) 435 break; 436 437 if (cm->fkeyhelp[fkey].textname) 438 key = show_message_file(cm->fkeyhelp[fkey].textname, 439 cm->fkeyhelp[fkey].background); 440 else 441 break; 442 } 443 } 444 445 static const char *edit_cmdline(const char *input, int top) 446 { 447 static char cmdline[MAX_CMDLINE_LEN]; 448 int key, len, prev_len, cursor; 449 int redraw = 1; /* We enter with the menu already drawn */ 450 451 strlcpy(cmdline, input, MAX_CMDLINE_LEN); 452 cmdline[MAX_CMDLINE_LEN - 1] = '\0'; 453 454 len = cursor = strlen(cmdline); 455 prev_len = 0; 456 457 for (;;) { 458 if (redraw > 1) { 459 /* Clear and redraw whole screen */ 460 /* Enable ASCII on G0 and DEC VT on G1; do it in this order 461 to avoid confusing the Linux console */ 462 clear_screen(); 463 draw_menu(-1, top, 1); 464 prev_len = 0; 465 } 466 467 if (redraw > 0) { 468 /* Redraw the command line */ 469 printf("\033[?25l\033[%d;1H\1#9> \2#10%s", 470 CMDLINE_ROW, pad_line(cmdline, 0, max(len, prev_len))); 471 printf("\2#10\033[%d;3H%s\033[?25h", 472 CMDLINE_ROW, pad_line(cmdline, 0, cursor)); 473 prev_len = len; 474 redraw = 0; 475 } 476 477 key = mygetkey(0); 478 479 switch (key) { 480 case KEY_CTRL('L'): 481 redraw = 2; 482 break; 483 484 case KEY_ENTER: 485 case KEY_CTRL('J'): 486 return cmdline; 487 488 case KEY_ESC: 489 case KEY_CTRL('C'): 490 return NULL; 491 492 case KEY_BACKSPACE: 493 case KEY_DEL: 494 if (cursor) { 495 memmove(cmdline + cursor - 1, cmdline + cursor, 496 len - cursor + 1); 497 len--; 498 cursor--; 499 redraw = 1; 500 } 501 break; 502 503 case KEY_CTRL('D'): 504 case KEY_DELETE: 505 if (cursor < len) { 506 memmove(cmdline + cursor, cmdline + cursor + 1, len - cursor); 507 len--; 508 redraw = 1; 509 } 510 break; 511 512 case KEY_CTRL('U'): 513 if (len) { 514 len = cursor = 0; 515 cmdline[len] = '\0'; 516 redraw = 1; 517 } 518 break; 519 520 case KEY_CTRL('W'): 521 if (cursor) { 522 int prevcursor = cursor; 523 524 while (cursor && my_isspace(cmdline[cursor - 1])) 525 cursor--; 526 527 while (cursor && !my_isspace(cmdline[cursor - 1])) 528 cursor--; 529 530 memmove(cmdline + cursor, cmdline + prevcursor, 531 len - prevcursor + 1); 532 len -= (prevcursor - cursor); 533 redraw = 1; 534 } 535 break; 536 537 case KEY_LEFT: 538 case KEY_CTRL('B'): 539 if (cursor) { 540 cursor--; 541 redraw = 1; 542 } 543 break; 544 545 case KEY_RIGHT: 546 case KEY_CTRL('F'): 547 if (cursor < len) { 548 putchar(cmdline[cursor++]); 549 } 550 break; 551 552 case KEY_CTRL('K'): 553 if (cursor < len) { 554 cmdline[len = cursor] = '\0'; 555 redraw = 1; 556 } 557 break; 558 559 case KEY_HOME: 560 case KEY_CTRL('A'): 561 if (cursor) { 562 cursor = 0; 563 redraw = 1; 564 } 565 break; 566 567 case KEY_END: 568 case KEY_CTRL('E'): 569 if (cursor != len) { 570 cursor = len; 571 redraw = 1; 572 } 573 break; 574 575 case KEY_F1: 576 case KEY_F2: 577 case KEY_F3: 578 case KEY_F4: 579 case KEY_F5: 580 case KEY_F6: 581 case KEY_F7: 582 case KEY_F8: 583 case KEY_F9: 584 case KEY_F10: 585 case KEY_F11: 586 case KEY_F12: 587 show_fkey(key); 588 redraw = 1; 589 break; 590 591 default: 592 if (key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN - 1) { 593 if (cursor == len) { 594 cmdline[len] = key; 595 cmdline[++len] = '\0'; 596 cursor++; 597 putchar(key); 598 prev_len++; 599 } else { 600 memmove(cmdline + cursor + 1, cmdline + cursor, 601 len - cursor + 1); 602 cmdline[cursor++] = key; 603 len++; 604 redraw = 1; 605 } 606 } 607 break; 608 } 609 } 610 } 611 612 static void print_timeout_message(int tol, int row, const char *msg) 613 { 614 static int last_msg_len = 0; 615 char buf[256]; 616 int nc = 0, nnc, padc; 617 const char *tp = msg; 618 char tc; 619 char *tq = buf; 620 621 while ((size_t) (tq - buf) < (sizeof buf - 16) && (tc = *tp)) { 622 tp++; 623 if (tc == '#') { 624 nnc = sprintf(tq, "\2#15%d\2#14", tol); 625 tq += nnc; 626 nc += nnc - 8; /* 8 formatting characters */ 627 } else if (tc == '{') { 628 /* Deal with {singular[,dual],plural} constructs */ 629 struct { 630 const char *s, *e; 631 } tx[3]; 632 const char *tpp; 633 int n = 0; 634 635 memset(tx, 0, sizeof tx); 636 637 tx[0].s = tp; 638 639 while (*tp && *tp != '}') { 640 if (*tp == ',' && n < 2) { 641 tx[n].e = tp; 642 n++; 643 tx[n].s = tp + 1; 644 } 645 tp++; 646 } 647 tx[n].e = tp; 648 649 if (*tp) 650 tp++; /* Skip final bracket */ 651 652 if (!tx[1].s) 653 tx[1] = tx[0]; 654 if (!tx[2].s) 655 tx[2] = tx[1]; 656 657 /* Now [0] is singular, [1] is dual, and [2] is plural, 658 even if the user only specified some of them. */ 659 660 switch (tol) { 661 case 1: 662 n = 0; 663 break; 664 case 2: 665 n = 1; 666 break; 667 default: 668 n = 2; 669 break; 670 } 671 672 for (tpp = tx[n].s; tpp < tx[n].e; tpp++) { 673 if ((size_t) (tq - buf) < (sizeof buf)) { 674 *tq++ = *tpp; 675 nc++; 676 } 677 } 678 } else { 679 *tq++ = tc; 680 nc++; 681 } 682 } 683 *tq = '\0'; 684 685 if (nc >= last_msg_len) { 686 padc = 0; 687 } else { 688 padc = (last_msg_len - nc + 1) >> 1; 689 } 690 691 printf("\033[%d;%dH\2#14%*s%s%*s", row, 692 HSHIFT + 1 + ((WIDTH - nc) >> 1) - padc, 693 padc, "", buf, padc, ""); 694 695 last_msg_len = nc; 696 } 697 698 /* Set the background screen, etc. */ 699 static void prepare_screen_for_menu(void) 700 { 701 console_color_table = cm->color_table; 702 console_color_table_size = menu_color_table_size; 703 set_background(cm->menu_background); 704 } 705 706 static const char *do_hidden_menu(void) 707 { 708 int key; 709 int timeout_left, this_timeout; 710 711 clear_screen(); 712 713 if (!setjmp(timeout_jump)) { 714 timeout_left = cm->timeout; 715 716 while (!cm->timeout || timeout_left) { 717 int tol = timeout_left / CLK_TCK; 718 719 print_timeout_message(tol, HIDDEN_ROW, cm->messages[MSG_AUTOBOOT]); 720 721 this_timeout = min(timeout_left, CLK_TCK); 722 key = mygetkey(this_timeout); 723 724 if (key != KEY_NONE) { 725 /* Clear the message from the screen */ 726 print_timeout_message(0, HIDDEN_ROW, ""); 727 return hide_key[key]; /* NULL if no MENU HIDEKEY in effect */ 728 } 729 730 timeout_left -= this_timeout; 731 } 732 } 733 734 /* Clear the message from the screen */ 735 print_timeout_message(0, HIDDEN_ROW, ""); 736 737 if (cm->ontimeout) 738 return cm->ontimeout; 739 else 740 return cm->menu_entries[cm->defentry]->cmdline; /* Default entry */ 741 } 742 743 static const char *run_menu(void) 744 { 745 int key; 746 int done = 0; 747 volatile int entry = cm->curentry; 748 int prev_entry = -1; 749 volatile int top = cm->curtop; 750 int prev_top = -1; 751 int clear = 1, to_clear; 752 const char *cmdline = NULL; 753 volatile clock_t key_timeout, timeout_left, this_timeout; 754 const struct menu_entry *me; 755 bool hotkey = false; 756 757 /* Note: for both key_timeout and timeout == 0 means no limit */ 758 timeout_left = key_timeout = cm->timeout; 759 760 /* If we're in shiftkey mode, exit immediately unless a shift key 761 is pressed */ 762 if (shiftkey && !shift_is_held()) { 763 return cm->menu_entries[cm->defentry]->cmdline; 764 } else { 765 shiftkey = 0; 766 } 767 768 /* Do this before hiddenmenu handling, so we show the background */ 769 prepare_screen_for_menu(); 770 771 /* Handle hiddenmenu */ 772 if (hiddenmenu) { 773 cmdline = do_hidden_menu(); 774 if (cmdline) 775 return cmdline; 776 777 /* Otherwise display the menu now; the timeout has already been 778 cancelled, since the user pressed a key. */ 779 hiddenmenu = 0; 780 key_timeout = 0; 781 } 782 783 /* Handle both local and global timeout */ 784 if (setjmp(timeout_jump)) { 785 entry = cm->defentry; 786 787 if (top < 0 || top < entry - MENU_ROWS + 1) 788 top = max(0, entry - MENU_ROWS + 1); 789 else if (top > entry || top > max(0, cm->nentries - MENU_ROWS)) 790 top = min(entry, max(0, cm->nentries - MENU_ROWS)); 791 792 draw_menu(cm->ontimeout ? -1 : entry, top, 1); 793 cmdline = 794 cm->ontimeout ? cm->ontimeout : cm->menu_entries[entry]->cmdline; 795 done = 1; 796 } 797 798 while (!done) { 799 if (entry <= 0) { 800 entry = 0; 801 while (entry < cm->nentries && is_disabled(cm->menu_entries[entry])) 802 entry++; 803 } 804 if (entry >= cm->nentries - 1) { 805 entry = cm->nentries - 1; 806 while (entry > 0 && is_disabled(cm->menu_entries[entry])) 807 entry--; 808 } 809 810 me = cm->menu_entries[entry]; 811 812 if (top < 0 || top < entry - MENU_ROWS + 1) 813 top = max(0, entry - MENU_ROWS + 1); 814 else if (top > entry || top > max(0, cm->nentries - MENU_ROWS)) 815 top = min(entry, max(0, cm->nentries - MENU_ROWS)); 816 817 /* Start with a clear screen */ 818 if (clear) { 819 /* Clear and redraw whole screen */ 820 /* Enable ASCII on G0 and DEC VT on G1; do it in this order 821 to avoid confusing the Linux console */ 822 if (clear >= 2) 823 prepare_screen_for_menu(); 824 clear_screen(); 825 clear = 0; 826 prev_entry = prev_top = -1; 827 } 828 829 if (top != prev_top) { 830 draw_menu(entry, top, 1); 831 display_help(me->helptext); 832 } else if (entry != prev_entry) { 833 draw_row(prev_entry - top + 4 + VSHIFT, entry, top, 0, 0); 834 draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0); 835 display_help(me->helptext); 836 } 837 838 prev_entry = entry; 839 prev_top = top; 840 cm->curentry = entry; 841 cm->curtop = top; 842 843 /* Cursor movement cancels timeout */ 844 if (entry != cm->defentry) 845 key_timeout = 0; 846 847 if (key_timeout) { 848 int tol = timeout_left / CLK_TCK; 849 print_timeout_message(tol, TIMEOUT_ROW, cm->messages[MSG_AUTOBOOT]); 850 to_clear = 1; 851 } else { 852 to_clear = 0; 853 } 854 855 if (hotkey && me->immediate) { 856 /* If the hotkey was flagged immediate, simulate pressing ENTER */ 857 key = KEY_ENTER; 858 } else { 859 this_timeout = min(min(key_timeout, timeout_left), 860 (clock_t) CLK_TCK); 861 key = mygetkey(this_timeout); 862 863 if (key != KEY_NONE) { 864 timeout_left = key_timeout; 865 if (to_clear) 866 printf("\033[%d;1H\1#0\033[K", TIMEOUT_ROW); 867 } 868 } 869 870 hotkey = false; 871 872 switch (key) { 873 case KEY_NONE: /* Timeout */ 874 /* This is somewhat hacky, but this at least lets the user 875 know what's going on, and still deals with "phantom inputs" 876 e.g. on serial ports. 877 878 Warning: a timeout will boot the default entry without any 879 password! */ 880 if (key_timeout) { 881 if (timeout_left <= this_timeout) 882 longjmp(timeout_jump, 1); 883 884 timeout_left -= this_timeout; 885 } 886 break; 887 888 case KEY_CTRL('L'): 889 clear = 1; 890 break; 891 892 case KEY_ENTER: 893 case KEY_CTRL('J'): 894 key_timeout = 0; /* Cancels timeout */ 895 if (me->passwd) { 896 clear = 1; 897 done = ask_passwd(me->passwd); 898 } else { 899 done = 1; 900 } 901 cmdline = NULL; 902 if (done) { 903 switch (me->action) { 904 case MA_CMD: 905 cmdline = me->cmdline; 906 break; 907 case MA_SUBMENU: 908 case MA_GOTO: 909 case MA_EXIT: 910 done = 0; 911 clear = 2; 912 cm = me->submenu; 913 entry = cm->curentry; 914 top = cm->curtop; 915 break; 916 case MA_QUIT: 917 /* Quit menu system */ 918 done = 1; 919 clear = 1; 920 draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0); 921 break; 922 case MA_HELP: 923 key = show_message_file(me->cmdline, me->background); 924 /* If the exit was an F-key, display that help screen */ 925 show_fkey(key); 926 done = 0; 927 clear = 1; 928 break; 929 default: 930 done = 0; 931 break; 932 } 933 } 934 if (done && !me->passwd) { 935 /* Only save a new default if we don't have a password... */ 936 if (me->save && me->label) { 937 syslinux_setadv(ADV_MENUSAVE, strlen(me->label), me->label); 938 syslinux_adv_write(); 939 } 940 } 941 break; 942 943 case KEY_UP: 944 case KEY_CTRL('P'): 945 while (entry > 0) { 946 entry--; 947 if (entry < top) 948 top -= MENU_ROWS; 949 if (!is_disabled(cm->menu_entries[entry])) 950 break; 951 } 952 break; 953 954 case KEY_DOWN: 955 case KEY_CTRL('N'): 956 while (entry < cm->nentries - 1) { 957 entry++; 958 if (entry >= top + MENU_ROWS) 959 top += MENU_ROWS; 960 if (!is_disabled(cm->menu_entries[entry])) 961 break; 962 } 963 break; 964 965 case KEY_PGUP: 966 case KEY_LEFT: 967 case KEY_CTRL('B'): 968 case '<': 969 entry -= MENU_ROWS; 970 top -= MENU_ROWS; 971 while (entry > 0 && is_disabled(cm->menu_entries[entry])) { 972 entry--; 973 if (entry < top) 974 top -= MENU_ROWS; 975 } 976 break; 977 978 case KEY_PGDN: 979 case KEY_RIGHT: 980 case KEY_CTRL('F'): 981 case '>': 982 case ' ': 983 entry += MENU_ROWS; 984 top += MENU_ROWS; 985 while (entry < cm->nentries - 1 986 && is_disabled(cm->menu_entries[entry])) { 987 entry++; 988 if (entry >= top + MENU_ROWS) 989 top += MENU_ROWS; 990 } 991 break; 992 993 case '-': 994 while (entry > 0) { 995 entry--; 996 top--; 997 if (!is_disabled(cm->menu_entries[entry])) 998 break; 999 } 1000 break; 1001 1002 case '+': 1003 while (entry < cm->nentries - 1) { 1004 entry++; 1005 top++; 1006 if (!is_disabled(cm->menu_entries[entry])) 1007 break; 1008 } 1009 break; 1010 1011 case KEY_CTRL('A'): 1012 case KEY_HOME: 1013 top = entry = 0; 1014 break; 1015 1016 case KEY_CTRL('E'): 1017 case KEY_END: 1018 entry = cm->nentries - 1; 1019 top = max(0, cm->nentries - MENU_ROWS); 1020 break; 1021 1022 case KEY_F1: 1023 case KEY_F2: 1024 case KEY_F3: 1025 case KEY_F4: 1026 case KEY_F5: 1027 case KEY_F6: 1028 case KEY_F7: 1029 case KEY_F8: 1030 case KEY_F9: 1031 case KEY_F10: 1032 case KEY_F11: 1033 case KEY_F12: 1034 show_fkey(key); 1035 clear = 1; 1036 break; 1037 1038 case KEY_TAB: 1039 if (cm->allowedit && me->action == MA_CMD) { 1040 int ok = 1; 1041 1042 key_timeout = 0; /* Cancels timeout */ 1043 draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0); 1044 1045 if (cm->menu_master_passwd) { 1046 ok = ask_passwd(NULL); 1047 clear_screen(); 1048 draw_menu(-1, top, 0); 1049 } else { 1050 /* Erase [Tab] message and help text */ 1051 printf("\033[%d;1H\1#0\033[K", TABMSG_ROW); 1052 display_help(NULL); 1053 } 1054 1055 if (ok) { 1056 cmdline = edit_cmdline(me->cmdline, top); 1057 done = !!cmdline; 1058 clear = 1; /* In case we hit [Esc] and done is null */ 1059 } else { 1060 draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0); 1061 } 1062 } 1063 break; 1064 case KEY_CTRL('C'): /* Ctrl-C */ 1065 case KEY_ESC: /* Esc */ 1066 if (cm->parent) { 1067 cm = cm->parent; 1068 clear = 2; 1069 entry = cm->curentry; 1070 top = cm->curtop; 1071 } else if (cm->allowedit) { 1072 done = 1; 1073 clear = 1; 1074 key_timeout = 0; 1075 1076 draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0); 1077 1078 if (cm->menu_master_passwd) 1079 done = ask_passwd(NULL); 1080 } 1081 break; 1082 default: 1083 if (key > 0 && key < 0xFF) { 1084 key &= ~0x20; /* Upper case */ 1085 if (cm->menu_hotkeys[key]) { 1086 key_timeout = 0; 1087 entry = cm->menu_hotkeys[key]->entry; 1088 /* Should we commit at this point? */ 1089 hotkey = true; 1090 } 1091 } 1092 break; 1093 } 1094 } 1095 1096 printf("\033[?25h"); /* Show cursor */ 1097 1098 /* Return the label name so localboot and ipappend work */ 1099 return cmdline; 1100 } 1101 1102 int main(int argc, char *argv[]) 1103 { 1104 const char *cmdline; 1105 struct menu *m; 1106 int rows, cols; 1107 int i; 1108 1109 (void)argc; 1110 1111 parse_configs(argv + 1); 1112 1113 /* 1114 * We don't start the console until we have parsed the configuration 1115 * file, since the configuration file might impact the console 1116 * configuration, e.g. MENU RESOLUTION. 1117 */ 1118 start_console(); 1119 if (getscreensize(1, &rows, &cols)) { 1120 /* Unknown screen size? */ 1121 rows = 24; 1122 cols = 80; 1123 } 1124 1125 /* Some postprocessing for all menus */ 1126 for (m = menu_list; m; m = m->next) { 1127 if (!m->mparm[P_WIDTH]) 1128 m->mparm[P_WIDTH] = cols; 1129 1130 /* If anyone has specified negative parameters, consider them 1131 relative to the bottom row of the screen. */ 1132 for (i = 0; i < NPARAMS; i++) 1133 if (m->mparm[i] < 0) 1134 m->mparm[i] = max(m->mparm[i] + rows, 0); 1135 } 1136 1137 cm = start_menu; 1138 1139 if (!cm->nentries) { 1140 fputs("Initial menu has no LABEL entries!\n", stdout); 1141 return 1; /* Error! */ 1142 } 1143 1144 for (;;) { 1145 local_cursor_enable(true); 1146 cmdline = run_menu(); 1147 1148 if (clearmenu) 1149 clear_screen(); 1150 1151 local_cursor_enable(false); 1152 printf("\033[?25h\033[%d;1H\033[0m", END_ROW); 1153 1154 if (cmdline) { 1155 uint32_t type = parse_image_type(cmdline); 1156 1157 execute(cmdline, type, false); 1158 if (cm->onerror) { 1159 type = parse_image_type(cm->onerror); 1160 execute(cm->onerror, type, true); 1161 } 1162 } else { 1163 return 0; /* Exit */ 1164 } 1165 } 1166 } 1167