2 /* ----------------------------------------------------------------------- *
4 * Copyright 2004 H. Peter Anvin - All Rights Reserved
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., 53 Temple Place Ste 330,
9 * Boston MA 02111-1307, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
17 * Simple menu system which displays a list and allows the user to select
18 * a command line and/or edit it.
21 #define _GNU_SOURCE /* Needed for asprintf() on Linux */
29 #include <sys/times.h>
38 # define CLK_TCK sysconf(_SC_CLK_TCK)
42 const char *border; /* Border area */
43 const char *title; /* Title bar */
44 const char *unsel; /* Unselected menu item */
45 const char *hotkey; /* Unselected hotkey */
46 const char *sel; /* Selected */
47 const char *hotsel; /* Selected hotkey */
48 const char *more; /* [More] tag */
49 const char *tabmsg; /* Press [Tab] message */
50 const char *cmdmark; /* Command line marker */
51 const char *cmdline; /* Command line */
52 const char *screen; /* Rest of the screen */
55 const struct menu_attrib default_attrib = {
56 .border = "\033[0;30;44m",
57 .title = "\033[1;36;44m",
58 .unsel = "\033[0;37;44m",
59 .hotkey = "\033[1;37;44m",
60 .sel = "\033[0;30;47m",
61 .hotsel = "\033[1;30;47m",
62 .more = "\033[0;37;44m",
63 .tabmsg = "\033[0;31;40m",
64 .cmdmark = "\033[1;36;40m",
65 .cmdline = "\033[0;37;40m",
66 .screen = "\033[0;37;40m",
69 const struct menu_attrib *menu_attrib = &default_attrib;
75 #define CMDLINE_ROW 20
78 char *pad_line(const char *text, int align, int width)
80 static char buffer[256];
83 if ( width >= (int) sizeof buffer )
84 return NULL; /* Can't do it */
90 memset(buffer, ' ', width);
92 p = ((width-n)*align)>>1;
93 memcpy(buffer+p, text, n);
98 /* Display an entry, with possible hotkey highlight. Assumes
99 that the current attribute is the non-hotkey one, and will
100 guarantee that as an exit condition as well. */
101 void display_entry(const struct menu_entry *entry, const char *attrib,
102 const char *hotattrib, int width)
104 const char *p = entry->displayname;
110 if ( *p && (unsigned char)*p == entry->hotkey ) {
111 fputs(hotattrib, stdout);
113 fputs(attrib, stdout);
127 void draw_menu(int sel, int top)
131 printf("\033[1;%dH%s\311", MARGIN+1, menu_attrib->border);
132 for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
136 printf("\033[2;%dH\272%s %s %s\272",
139 pad_line(menu_title, 1, WIDTH-2*MARGIN-4),
140 menu_attrib->border);
142 printf("\033[3;%dH\307", MARGIN+1);
143 for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
148 printf("%s\033[3;%dH[-]%s",
149 menu_attrib->more, WIDTH-MARGIN-5,
150 menu_attrib->border);
152 for ( y = 4 ; y < 4+MENU_ROWS ; y++ ) {
155 printf("\033[%d;%dH\272%s ",
157 (i == sel) ? menu_attrib->sel : menu_attrib->unsel);
159 if ( i >= nentries ) {
160 fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout);
162 display_entry(&menu_entries[i],
163 (i == sel) ? menu_attrib->sel : menu_attrib->unsel,
164 (i == sel) ? menu_attrib->hotsel : menu_attrib->hotkey,
168 printf(" %s\272", menu_attrib->border);
171 printf("\033[%d;%dH\310", y, MARGIN+1);
172 for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
176 if ( top < nentries-MENU_ROWS )
177 printf("%s\033[%d;%dH[+]", menu_attrib->more, y, WIDTH-MARGIN-5);
180 printf("%s\033[%d;1H%s", menu_attrib->tabmsg, TABMSG_ROW,
181 pad_line("Press [Tab] to edit options", 1, WIDTH));
183 printf("%s\033[%d;1H", menu_attrib->screen, END_ROW);
186 const char *edit_cmdline(char *input, int top)
188 static char cmdline[MAX_CMDLINE_LEN];
192 strncpy(cmdline, input, MAX_CMDLINE_LEN);
193 cmdline[MAX_CMDLINE_LEN-1] = '\0';
195 len = strlen(cmdline);
199 /* Clear and redraw whole screen */
200 printf("%s\033[2J", menu_attrib->screen);
205 /* Redraw the command line */
206 printf("\033[%d;1H%s> %s%s",
207 CMDLINE_ROW, menu_attrib->cmdmark,
208 menu_attrib->cmdline, pad_line(cmdline, 0, MAX_CMDLINE_LEN-1));
209 printf("%s\033[%d;3H%s",
210 menu_attrib->cmdline, CMDLINE_ROW, cmdline);
214 key = get_key(stdin, 0);
216 /* FIX: should handle arrow keys and edit-in-middle */
232 cmdline[--len] = '\0';
245 int wasbs = (cmdline[len-1] <= ' ');
246 while ( len && (cmdline[len-1] <= ' ' || !wasbs) ) {
248 wasbs = wasbs || (cmdline[len-1] <= ' ');
255 if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) {
257 cmdline[++len] = '\0';
265 const char *run_menu(void)
269 int entry = defentry;
272 const char *cmdline = NULL;
275 /* Convert timeout from deciseconds to clock ticks */
276 /* Note: for both key_timeout and timeout == 0 means no limit */
277 key_timeout = (clock_t)(CLK_TCK*timeout+9)/10;
279 printf("\033[?25l"); /* Hide cursor */
284 else if ( entry >= nentries )
287 if ( top < 0 || top < entry-MENU_ROWS+1 )
288 top = max(0, entry-MENU_ROWS+1);
289 else if ( top > entry )
292 /* Start with a clear screen */
294 printf("%s\033[2J", menu_attrib->screen);
297 draw_menu(entry, top);
299 key = get_key(stdin, key_timeout);
301 case KEY_NONE: /* Timeout */
302 /* This is somewhat hacky, but this at least lets the user
303 know what's going on, and still deals with "phantom inputs"
304 e.g. on serial ports. */
305 if ( entry != defentry )
308 cmdline = menu_entries[defentry].label;
317 cmdline = menu_entries[entry].label;
351 printf("\033[?25h"); /* Show cursor */
352 cmdline = edit_cmdline(menu_entries[entry].cmdline, top);
353 printf("\033[?25l"); /* Hide cursor */
355 clear = 1; /* In case we hit [Esc] and done is null */
358 case KEY_CTRL('C'): /* Ctrl-C */
359 case KEY_ESC: /* Esc */
364 if ( key > 0 && key < 0xFF ) {
365 key &= ~0x20; /* Upper case */
366 if ( menu_hotkeys[key] ) {
367 entry = menu_hotkeys[key] - menu_entries;
368 /* Should we commit at this point? */
375 printf("\033[?25h"); /* Show cursor */
377 /* Return the label name so localboot and ipappend work */
382 void __attribute__((noreturn)) execute(const char *cmdline)
385 static com32sys_t ireg;
387 strcpy(__com32.cs_bounce, cmdline);
388 ireg.eax.w[0] = 0x0003; /* Run command */
389 ireg.ebx.w[0] = OFFS(__com32.cs_bounce);
390 ireg.es = SEG(__com32.cs_bounce);
391 __intcall(0x22, &ireg, NULL);
392 exit(255); /* Shouldn't return */
395 printf("\n>>> %s\n", cmdline);
400 int main(int argc, char *argv[])
407 fputs("\033%@\033(U", stdout); /* Enable CP 437 graphics on a real console */
409 parse_config(argv[1]);
411 cmdline = run_menu();
412 printf("\033[?25h\033[%d;1H\033[0m", END_ROW);