1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004-2007 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
16 * Simple menu system which displays a list and allows the user to select
17 * a command line and/or edit it.
20 #define _GNU_SOURCE /* Needed for asprintf() on Linux */
39 * The color/attribute indexes (\1#X, \2#XX, \3#XXX) are as follows
41 * 00 - screen Rest of the screen
42 * 01 - border Border area
43 * 02 - title Title bar
44 * 03 - unsel Unselected menu item
45 * 04 - hotkey Unselected hotkey
46 * 05 - sel Selection bar
47 * 06 - hotsel Selected hotkey
48 * 07 - scrollbar Scroll bar
49 * 08 - tabmsg Press [Tab] message
50 * 09 - cmdmark Command line marker
51 * 10 - cmdline Command line
52 * 11 - pwdborder Password box border
53 * 12 - pwdheader Password box header
54 * 13 - pwdentry Password box contents
55 * 14 - timeout_msg Timeout message
56 * 15 - timeout Timeout counter
57 * 16 - help Current entry help text
60 static const struct color_table default_color_table[] = {
61 { "screen", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL },
62 { "border", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL },
63 { "title", "1;36;44", 0xc00090f0, 0x00000000, SHADOW_NORMAL },
64 { "unsel", "37;44", 0x90ffffff, 0x00000000, SHADOW_NORMAL },
65 { "hotkey", "1;37;44", 0xffffffff, 0x00000000, SHADOW_NORMAL },
66 { "sel", "7;37;40", 0xe0000000, 0x20ff8000, SHADOW_ALL },
67 { "hotsel", "1;7;37;40", 0xe0400000, 0x20ff8000, SHADOW_ALL },
68 { "scrollbar", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL },
69 { "tabmsg", "31;40", 0x90ffff00, 0x00000000, SHADOW_NORMAL },
70 { "cmdmark", "1;36;40", 0xc000ffff, 0x00000000, SHADOW_NORMAL },
71 { "cmdline", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL },
72 { "pwdborder", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL },
73 { "pwdheader", "31;47", 0x80ff8080, 0x20ffffff, SHADOW_NORMAL },
74 { "pwdentry", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL },
75 { "timeout_msg", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL },
76 { "timeout", "1;37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL },
77 { "help", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL },
80 #define NCOLORS (sizeof default_color_table/sizeof(struct color_table))
81 const int message_base_color = NCOLORS;
83 struct menu_parameter mparm[] = {
86 { "passwordmargin", 3 },
91 { "passwordrow", 11 },
94 { "helpmsgendrow", -1 },
101 #define WIDTH mparm[0].value
102 #define MARGIN mparm[1].value
103 #define PASSWD_MARGIN mparm[2].value
104 #define MENU_ROWS mparm[3].value
105 #define TABMSG_ROW (mparm[4].value+VSHIFT)
106 #define CMDLINE_ROW (mparm[5].value+VSHIFT)
107 #define END_ROW mparm[6].value
108 #define PASSWD_ROW (mparm[7].value+VSHIFT)
109 #define TIMEOUT_ROW (mparm[8].value+VSHIFT)
110 #define HELPMSG_ROW (mparm[9].value+VSHIFT)
111 #define HELPMSGEND_ROW mparm[10].value
112 #define HSHIFT mparm[11].value
113 #define VSHIFT mparm[12].value
114 #define HIDDEN_ROW mparm[13].value
116 void set_msg_colors_global(unsigned int fg, unsigned int bg,
117 enum color_table_shadow shadow)
119 struct color_table *cp = console_color_table+message_base_color;
121 unsigned int fga, bga;
122 unsigned int fgh, bgh;
123 unsigned int fg_idx, bg_idx;
124 unsigned int fg_rgb, bg_rgb;
126 static const unsigned int pc2rgb[8] =
127 { 0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00,
130 /* Converting PC RGBI to sensible RGBA values is an "interesting"
131 proposition. This algorithm may need plenty of tweaking. */
133 fga = fg & 0xff000000;
134 fgh = ((fg >> 1) & 0xff000000) | 0x80000000;
136 bga = bg & 0xff000000;
137 bgh = ((bg >> 1) & 0xff000000) | 0x80000000;
139 for (i = 0; i < 256; i++) {
143 fg_rgb = pc2rgb[fg_idx & 7] & fg;
144 bg_rgb = pc2rgb[bg_idx & 7] & bg;
147 /* High intensity foreground */
154 /* Default black background, assume transparent */
156 } else if (bg_idx & 8) {
162 cp->argb_fg = fg_rgb;
163 cp->argb_bg = bg_rgb;
170 install_default_color_table(void)
173 const struct color_table *dp;
174 struct color_table *cp;
175 static struct color_table color_table[NCOLORS+256];
176 static const int pc2ansi[8] = {0, 4, 2, 6, 1, 5, 3, 7};
178 dp = default_color_table;
181 for (i = 0; i < NCOLORS; i++) {
183 free((void *)cp->ansi);
186 cp->ansi = strdup(dp->ansi);
192 for (i = 0; i < 256; i++) {
194 asprintf((char **)&cp->name, "msg%02x", i);
197 free((void *)cp->ansi);
199 asprintf((char **)&cp->ansi, "%s3%d;4%d", (i & 8) ? "1;" : "",
200 pc2ansi[i & 7], pc2ansi[(i >> 4) & 7]);
205 console_color_table = color_table;
206 console_color_table_size = NCOLORS+256;
208 set_msg_colors_global(MSG_COLORS_DEF_FG, MSG_COLORS_DEF_BG,
209 MSG_COLORS_DEF_SHADOW);
213 pad_line(const char *text, int align, int width)
215 static char buffer[MAX_CMDLINE_LEN];
218 if ( width >= (int) sizeof buffer )
219 return NULL; /* Can't do it */
225 memset(buffer, ' ', width);
227 p = ((width-n)*align)>>1;
228 memcpy(buffer+p, text, n);
233 /* Display an entry, with possible hotkey highlight. Assumes
234 that the current attribute is the non-hotkey one, and will
235 guarantee that as an exit condition as well. */
237 display_entry(const struct menu_entry *entry, const char *attrib,
238 const char *hotattrib, int width)
240 const char *p = entry->displayname;
246 if ( *p && ((unsigned char)*p & ~0x20) == entry->hotkey ) {
247 fputs(hotattrib, stdout);
249 fputs(attrib, stdout);
264 draw_row(int y, int sel, int top, int sbtop, int sbbot)
266 int i = (y-4-VSHIFT)+top;
268 printf("\033[%d;%dH\1#1\016x\017%s ",
269 y, MARGIN+1+HSHIFT, (i == sel) ? "\1#5" : "\1#3");
271 if ( i >= nentries ) {
272 fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout);
274 display_entry(&menu_entries[i],
275 (i == sel) ? "\1#5" : "\1#3",
276 (i == sel) ? "\1#6" : "\1#4",
280 if ( nentries <= MENU_ROWS ) {
281 printf(" \1#1\016x\017");
282 } else if ( sbtop > 0 ) {
283 if ( y >= sbtop && y <= sbbot )
284 printf(" \1#7\016a\017");
286 printf(" \1#1\016x\017");
288 putchar(' '); /* Don't modify the scrollbar */
292 static int passwd_compare_sha1(const char *passwd, const char *entry)
296 unsigned char sha1[20], pwdsha1[20];
298 if ( (p = strchr(passwd+3, '$')) ) {
299 SHA1Update(&ctx, (void *)passwd+3, p-(passwd+3));
302 p = passwd+3; /* Assume no salt */
307 SHA1Update(&ctx, (void *)entry, strlen(entry));
308 SHA1Final(sha1, &ctx);
310 memset(pwdsha1, 0, 20);
311 unbase64(pwdsha1, 20, p);
313 return !memcmp(sha1, pwdsha1, 20);
316 static int passwd_compare_md5(const char *passwd, const char *entry)
318 const char *crypted = crypt_md5(entry, passwd+3);
319 int len = strlen(crypted);
321 return !strncmp(crypted, passwd, len) &&
322 (passwd[len] == '\0' || passwd[len] == '$');
326 passwd_compare(const char *passwd, const char *entry)
328 if ( passwd[0] != '$' ) /* Plaintext passwd, yuck! */
329 return !strcmp(entry, passwd);
330 else if ( !strncmp(passwd, "$4$", 3) )
331 return passwd_compare_sha1(passwd, entry);
332 else if ( !strncmp(passwd, "$1$", 3) )
333 return passwd_compare_md5(passwd, entry);
335 return 0; /* Invalid encryption algorithm */
338 static jmp_buf timeout_jump;
340 int mygetkey(clock_t timeout)
347 return get_key(stdin, timeout);
350 tto = min(totaltimeout, INT_MAX);
351 to = timeout ? min(tto, timeout) : tto;
354 key = get_key(stdin, to);
355 t = times(NULL) - t0;
357 if ( totaltimeout <= t )
358 longjmp(timeout_jump, 1);
362 if ( key != KEY_NONE )
375 ask_passwd(const char *menu_entry)
377 char user_passwd[WIDTH], *p;
382 printf("\033[%d;%dH\2#11\016l", PASSWD_ROW, PASSWD_MARGIN+1);
383 for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
386 printf("k\033[%d;%dHx", PASSWD_ROW+1, PASSWD_MARGIN+1);
387 for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
390 printf("x\033[%d;%dHm", PASSWD_ROW+2, PASSWD_MARGIN+1);
391 for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
394 printf("j\017\033[%d;%dH\2#12 %s \033[%d;%dH\2#13",
395 PASSWD_ROW, (WIDTH-(strlen(messages[MSG_PASSPROMPT].msg)+2))/2,
396 messages[MSG_PASSPROMPT].msg, PASSWD_ROW+1, PASSWD_MARGIN+3);
398 /* Actually allow user to type a password, then compare to the SHA1 */
413 p = user_passwd; /* No password entered */
420 if ( p > user_passwd ) {
427 while ( p > user_passwd ) {
434 if ( key >= ' ' && key <= 0xFF &&
435 (p-user_passwd) < WIDTH-2*PASSWD_MARGIN-5 ) {
443 if ( p == user_passwd )
444 return 0; /* No password entered */
448 return (menu_master_passwd && passwd_compare(menu_master_passwd, user_passwd))
449 || (menu_entry && passwd_compare(menu_entry, user_passwd));
454 draw_menu(int sel, int top, int edit_line)
457 int sbtop = 0, sbbot = 0;
461 if ( nentries > MENU_ROWS ) {
462 int sblen = MENU_ROWS*MENU_ROWS/nentries;
463 sbtop = (MENU_ROWS-sblen+1)*top/(nentries-MENU_ROWS+1);
464 sbbot = sbtop + sblen - 1;
466 sbtop += 4; sbbot += 4; /* Starting row of scrollbar */
469 printf("\033[%d;%dH\1#1\016l", VSHIFT+1, HSHIFT+MARGIN+1);
470 for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ )
473 printf("k\033[%d;%dH\1#1x\017\1#2 %s \1#1\016x",
476 pad_line(messages[MSG_TITLE].msg, 1, WIDTH-2*MARGIN-4));
478 printf("\033[%d;%dH\1#1t", VSHIFT+3, HSHIFT+MARGIN+1);
479 for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ )
481 fputs("u\017", stdout);
483 for ( y = 4+VSHIFT ; y < 4+VSHIFT+MENU_ROWS ; y++ )
484 draw_row(y, sel, top, sbtop, sbbot);
486 printf("\033[%d;%dH\1#1\016m", y, HSHIFT+MARGIN+1);
487 for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ )
489 fputs("j\017", stdout);
491 if ( edit_line && allowedit && !menu_master_passwd )
492 tabmsg = messages[MSG_TAB].msg;
494 tabmsg = messages[MSG_NOTAB].msg;
496 tabmsg_len = strlen(tabmsg);
498 printf("\1#8\033[%d;%dH%s",
499 TABMSG_ROW, 1+HSHIFT+((WIDTH-tabmsg_len)>>1), tabmsg);
500 printf("\1#0\033[%d;1H", END_ROW);
506 fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout);
510 display_help(const char *text)
517 printf("\1#0\033[%d;1H", HELPMSG_ROW);
519 printf("\2#16\033[%d;1H", HELPMSG_ROW);
522 for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) {
530 printf("\033[K\033[%d;1H", ++row);
537 fputs("\033[K", stdout);
539 while (row <= HELPMSGEND_ROW) {
540 printf("\033[K\033[%d;1H", ++row);
544 static void show_fkey(int key)
550 case KEY_F1: fkey = 0; break;
551 case KEY_F2: fkey = 1; break;
552 case KEY_F3: fkey = 2; break;
553 case KEY_F4: fkey = 3; break;
554 case KEY_F5: fkey = 4; break;
555 case KEY_F6: fkey = 5; break;
556 case KEY_F7: fkey = 6; break;
557 case KEY_F8: fkey = 7; break;
558 case KEY_F9: fkey = 8; break;
559 case KEY_F10: fkey = 9; break;
560 case KEY_F11: fkey = 10; break;
561 case KEY_F12: fkey = 11; break;
562 default: fkey = -1; break;
568 if (fkeyhelp[fkey].textname)
569 key = show_message_file(fkeyhelp[fkey].textname,
570 fkeyhelp[fkey].background);
577 edit_cmdline(char *input, int top)
579 static char cmdline[MAX_CMDLINE_LEN];
580 int key, len, prev_len, cursor;
581 int redraw = 1; /* We enter with the menu already drawn */
583 strncpy(cmdline, input, MAX_CMDLINE_LEN);
584 cmdline[MAX_CMDLINE_LEN-1] = '\0';
586 len = cursor = strlen(cmdline);
591 /* Clear and redraw whole screen */
592 /* Enable ASCII on G0 and DEC VT on G1; do it in this order
593 to avoid confusing the Linux console */
595 draw_menu(-1, top, 1);
600 /* Redraw the command line */
601 printf("\033[?25l\033[%d;1H\1#9> \2#10%s",
602 CMDLINE_ROW, pad_line(cmdline, 0, max(len, prev_len)));
603 printf("\2#10\033[%d;3H%s\033[?25h",
604 CMDLINE_ROW, pad_line(cmdline, 0, cursor));
627 memmove(cmdline+cursor-1, cmdline+cursor, len-cursor+1);
636 if ( cursor < len ) {
637 memmove(cmdline+cursor, cmdline+cursor+1, len-cursor);
653 int prevcursor = cursor;
655 while ( cursor && my_isspace(cmdline[cursor-1]) )
658 while ( cursor && !my_isspace(cmdline[cursor-1]) )
661 memmove(cmdline+cursor, cmdline+prevcursor, len-prevcursor+1);
662 len -= (cursor-prevcursor);
677 if ( cursor < len ) {
678 putchar(cmdline[cursor++]);
683 if ( cursor < len ) {
684 cmdline[len = cursor] = '\0';
699 if ( cursor != len ) {
722 if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) {
723 if ( cursor == len ) {
725 cmdline[++len] = '\0';
730 memmove(cmdline+cursor+1, cmdline+cursor, len-cursor+1);
731 cmdline[cursor++] = key;
744 uint8_t shift_bits = *(uint8_t *)0x417;
746 return !!(shift_bits & 0x5d); /* Caps/Scroll/Alt/Shift */
750 print_timeout_message(int tol, int row, const char *msg)
754 const char *tp = msg;
758 while ((size_t)(tq-buf) < (sizeof buf-16) && (tc = *tp)) {
760 nnc = sprintf(tq, "\2#15%d\2#14", tol);
762 nc += nnc-8; /* 8 formatting characters */
771 printf("\033[%d;%dH\2#14 %s ", row, HSHIFT+1+((WIDTH-nc-2)>>1), buf);
778 int timeout_left, this_timeout;
782 if ( !setjmp(timeout_jump) ) {
783 timeout_left = timeout;
785 while (!timeout || timeout_left) {
786 int tol = timeout_left/CLK_TCK;
788 print_timeout_message(tol, HIDDEN_ROW, messages[MSG_AUTOBOOT].msg);
790 this_timeout = min(timeout_left, CLK_TCK);
791 key = mygetkey(this_timeout);
794 return NULL; /* Key pressed */
796 timeout_left -= this_timeout;
800 return menu_entries[defentry].cmdline; /* Default entry */
808 volatile int entry = defentry, prev_entry = -1;
809 int top = 0, prev_top = -1;
810 int clear = 1, to_clear;
811 const char *cmdline = NULL;
812 volatile clock_t key_timeout, timeout_left, this_timeout;
814 /* Note: for both key_timeout and timeout == 0 means no limit */
815 timeout_left = key_timeout = timeout;
817 /* If we're in shiftkey mode, exit immediately unless a shift key is pressed */
818 if ( shiftkey && !shift_is_held() ) {
819 return menu_entries[defentry].cmdline;
822 /* Handle hiddenmenu */
824 cmdline = do_hidden_menu();
828 /* Otherwise display the menu now; the timeout has already been
829 cancelled, since the user pressed a key. */
834 /* Handle both local and global timeout */
835 if ( setjmp(timeout_jump) ) {
838 if ( top < 0 || top < entry-MENU_ROWS+1 )
839 top = max(0, entry-MENU_ROWS+1);
840 else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
841 top = min(entry, max(0,nentries-MENU_ROWS));
843 draw_menu(ontimeout ? -1 : entry, top, 1);
844 cmdline = ontimeout ? ontimeout : menu_entries[entry].cmdline;
851 else if ( entry >= nentries )
854 if ( top < 0 || top < entry-MENU_ROWS+1 )
855 top = max(0, entry-MENU_ROWS+1);
856 else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
857 top = min(entry, max(0,nentries-MENU_ROWS));
859 /* Start with a clear screen */
861 /* Clear and redraw whole screen */
862 /* Enable ASCII on G0 and DEC VT on G1; do it in this order
863 to avoid confusing the Linux console */
866 prev_entry = prev_top = -1;
869 if ( top != prev_top ) {
870 draw_menu(entry, top, 1);
871 display_help(menu_entries[entry].helptext);
872 } else if ( entry != prev_entry ) {
873 draw_row(prev_entry-top+4+VSHIFT, entry, top, 0, 0);
874 draw_row(entry-top+4+VSHIFT, entry, top, 0, 0);
875 display_help(menu_entries[entry].helptext);
878 prev_entry = entry; prev_top = top;
880 /* Cursor movement cancels timeout */
881 if ( entry != defentry )
885 int tol = timeout_left/CLK_TCK;
886 print_timeout_message(tol, TIMEOUT_ROW, messages[MSG_AUTOBOOT].msg);
892 this_timeout = min(min(key_timeout, timeout_left), CLK_TCK);
893 key = mygetkey(this_timeout);
895 if ( key != KEY_NONE ) {
896 timeout_left = key_timeout;
898 printf("\033[%d;1H\1#0\033[K", TIMEOUT_ROW);
902 case KEY_NONE: /* Timeout */
903 /* This is somewhat hacky, but this at least lets the user
904 know what's going on, and still deals with "phantom inputs"
905 e.g. on serial ports.
907 Warning: a timeout will boot the default entry without any
910 if ( timeout_left <= this_timeout )
911 longjmp(timeout_jump, 1);
913 timeout_left -= this_timeout;
923 key_timeout = 0; /* Cancels timeout */
924 if ( menu_entries[entry].passwd ) {
926 done = ask_passwd(menu_entries[entry].passwd);
930 cmdline = menu_entries[entry].cmdline;
944 if ( entry < nentries-1 ) {
946 if ( entry >= top+MENU_ROWS )
985 entry = nentries - 1;
986 top = max(0, nentries-MENU_ROWS);
1009 key_timeout = 0; /* Cancels timeout */
1010 draw_row(entry-top+4+VSHIFT, -1, top, 0, 0);
1012 if ( menu_master_passwd ) {
1013 ok = ask_passwd(NULL);
1015 draw_menu(-1, top, 0);
1017 /* Erase [Tab] message and help text*/
1018 printf("\033[%d;1H\1#0\033[K", TABMSG_ROW);
1023 cmdline = edit_cmdline(menu_entries[entry].cmdline, top);
1025 clear = 1; /* In case we hit [Esc] and done is null */
1027 draw_row(entry-top+4+VSHIFT, entry, top, 0, 0);
1031 case KEY_CTRL('C'): /* Ctrl-C */
1032 case KEY_ESC: /* Esc */
1038 draw_row(entry-top+4+VSHIFT, -1, top, 0, 0);
1040 if ( menu_master_passwd )
1041 done = ask_passwd(NULL);
1045 if ( key > 0 && key < 0xFF ) {
1046 key &= ~0x20; /* Upper case */
1047 if ( menu_hotkeys[key] ) {
1049 entry = menu_hotkeys[key] - menu_entries;
1050 /* Should we commit at this point? */
1057 printf("\033[?25h"); /* Show cursor */
1059 /* Return the label name so localboot and ipappend work */
1065 execute(const char *cmdline, enum kernel_type type)
1068 const char *p, **pp;
1069 char *q = __com32.cs_bounce;
1070 const char *kernel, *args;
1072 memset(&ireg, 0, sizeof ireg);
1076 while ( *p && !my_isspace(*p) ) {
1082 while ( *p && my_isspace(*p) )
1087 if (kernel[0] == '.' && type == KT_NONE) {
1088 /* It might be a type specifier */
1089 enum kernel_type type = KT_NONE;
1090 for (pp = kernel_types; *pp; pp++, type++) {
1091 if (!strcmp(kernel+1, *pp)) {
1092 execute(p, type); /* Strip the type specifier and retry */
1097 if (type == KT_LOCALBOOT) {
1098 ireg.eax.w[0] = 0x0014; /* Local boot */
1099 ireg.edx.w[0] = strtoul(kernel, NULL, 0);
1101 if (type < KT_KERNEL)
1104 ireg.eax.w[0] = 0x0016; /* Run kernel image */
1105 ireg.esi.w[0] = OFFS(kernel);
1106 ireg.ds = SEG(kernel);
1107 ireg.ebx.w[0] = OFFS(args);
1108 ireg.es = SEG(args);
1109 ireg.edx.l = type-KT_KERNEL;
1110 /* ireg.ecx.l = 0; */ /* We do ipappend "manually" */
1113 __intcall(0x22, &ireg, NULL);
1115 /* If this returns, something went bad; return to menu */
1118 int menu_main(int argc, char *argv[])
1120 const char *cmdline;
1128 install_default_color_table();
1129 if (getscreensize(1, &rows, &cols)) {
1130 /* Unknown screen size? */
1136 parse_configs(argv+1);
1138 /* If anyone has specified negative parameters, consider them
1139 relative to the bottom row of the screen. */
1140 for (i = 0; mparm[i].name; i++)
1141 if (mparm[i].value < 0)
1142 mparm[i].value = max(mparm[i].value+rows, 0);
1144 draw_background(menu_background);
1147 fputs("No LABEL entries found in configuration file!\n", stdout);
1148 return 1; /* Error! */
1152 cmdline = run_menu();
1154 printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
1158 execute(cmdline, KT_NONE);
1160 execute(onerror, KT_NONE);
1162 return 0; /* Exit */
1165 console_prepare(); /* If we're looping... */