home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 August (Alt) / CHIP 2005-08.1.iso / program / guvenlik / syslinux-3.07.exe / com32 / modules / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2005-01-05  |  11.1 KB  |  468 lines

  1. #ident "$Id: menu.c,v 1.14 2005/01/05 07:13:04 hpa Exp $"
  2. /* ----------------------------------------------------------------------- *
  3.  *   
  4.  *   Copyright 2004-2005 H. Peter Anvin - All Rights Reserved
  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., 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.
  11.  *
  12.  * ----------------------------------------------------------------------- */
  13.  
  14. /*
  15.  * menu.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. #define _GNU_SOURCE        /* Needed for asprintf() on Linux */
  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 <time.h>
  29. #include <sys/times.h>
  30. #include <unistd.h>
  31. #ifdef __COM32__
  32. #include <com32.h>
  33. #endif
  34.  
  35. #include "menu.h"
  36.  
  37. #ifndef CLK_TCK
  38. # define CLK_TCK sysconf(_SC_CLK_TCK)
  39. #endif
  40.  
  41. struct menu_attrib {
  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 *scrollbar;    /* Scroll bar */
  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 */
  53. };
  54.  
  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;7;37;40m",
  61.   .hotsel       = "\033[1;7;37;40m",
  62.   .scrollbar    = "\033[0;30;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",
  67. };
  68.  
  69. const struct menu_attrib *menu_attrib = &default_attrib;
  70.  
  71. #define WIDTH        80
  72. #define MARGIN        10
  73. #define MENU_ROWS    12
  74. #define TABMSG_ROW    18
  75. #define CMDLINE_ROW    20
  76. #define END_ROW        24
  77.  
  78. char *pad_line(const char *text, int align, int width)
  79. {
  80.   static char buffer[256];
  81.   int n, p;
  82.  
  83.   if ( width >= (int) sizeof buffer )
  84.     return NULL;        /* Can't do it */
  85.  
  86.   n = strlen(text);
  87.   if ( n >= width )
  88.     n = width;
  89.  
  90.   memset(buffer, ' ', width);
  91.   buffer[width] = 0;
  92.   p = ((width-n)*align)>>1;
  93.   memcpy(buffer+p, text, n);
  94.  
  95.   return buffer;
  96. }
  97.  
  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)
  103. {
  104.   const char *p = entry->displayname;
  105.  
  106.   while ( width ) {
  107.     if ( *p ) {
  108.       if ( *p == '^' ) {
  109.     p++;
  110.     if ( *p && (unsigned char)*p == entry->hotkey ) {
  111.       fputs(hotattrib, stdout);
  112.       putchar(*p++);
  113.       fputs(attrib, stdout);
  114.       width--;
  115.     }
  116.       } else {
  117.     putchar(*p++);
  118.     width--;
  119.       }
  120.     } else {
  121.       putchar(' ');
  122.       width--;
  123.     }
  124.   }
  125. }
  126.  
  127. void draw_row(int y, int sel, int top, int sbtop, int sbbot)
  128. {
  129.   int i = (y-4)+top;
  130.   
  131.   printf("\033[%d;%dH%s\016x\017%s ",
  132.      y, MARGIN+1, menu_attrib->border,
  133.      (i == sel) ? menu_attrib->sel : menu_attrib->unsel);
  134.   
  135.   if ( i >= nentries ) {
  136.     fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout);
  137.   } else {
  138.     display_entry(&menu_entries[i],
  139.           (i == sel) ? menu_attrib->sel : menu_attrib->unsel,
  140.           (i == sel) ? menu_attrib->hotsel : menu_attrib->hotkey,
  141.           WIDTH-2*MARGIN-4);
  142.   }
  143.  
  144.   if ( nentries <= MENU_ROWS ) {
  145.     printf(" %s\016x\017", menu_attrib->border);
  146.   } else if ( sbtop > 0 ) {
  147.     if ( y >= sbtop && y <= sbbot )
  148.       printf(" %s\016a\017", menu_attrib->scrollbar);
  149.     else
  150.       printf(" %s\016x\017", menu_attrib->border);
  151.   } else {
  152.     putchar(' ');        /* Don't modify the scrollbar */
  153.   }
  154. }
  155.  
  156. void draw_menu(int sel, int top)
  157. {
  158.   int x, y;
  159.   int sbtop = 0, sbbot = 0;
  160.   
  161.   if ( nentries > MENU_ROWS ) {
  162.     int sblen = MENU_ROWS*MENU_ROWS/nentries;
  163.     sbtop = (MENU_ROWS-sblen+1)*top/(nentries-MENU_ROWS+1);
  164.     sbbot = sbtop + sblen - 1;
  165.     
  166.     sbtop += 4;  sbbot += 4;    /* Starting row of scrollbar */
  167.   }
  168.   
  169.   printf("\033[1;%dH%s\016l", MARGIN+1, menu_attrib->border);
  170.   for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
  171.     putchar('q');
  172.   
  173.   printf("k\033[2;%dH%sx\017%s %s %s\016x",
  174.      MARGIN+1,
  175.      menu_attrib->border,
  176.      menu_attrib->title,
  177.      pad_line(menu_title, 1, WIDTH-2*MARGIN-4),
  178.      menu_attrib->border);
  179.   
  180.   printf("\033[3;%dH%st", MARGIN+1, menu_attrib->border);
  181.   for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
  182.     putchar('q');
  183.   fputs("u\017", stdout);
  184.   
  185.   for ( y = 4 ; y < 4+MENU_ROWS ; y++ )
  186.     draw_row(y, sel, top, sbtop, sbbot);
  187.  
  188.   printf("\033[%d;%dH%s\016m", y, MARGIN+1, menu_attrib->border);
  189.   for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
  190.     putchar('q');
  191.   fputs("j\017", stdout);
  192.  
  193.   if ( allowedit )
  194.     printf("%s\033[%d;1H%s", menu_attrib->tabmsg, TABMSG_ROW,
  195.        pad_line("Press [Tab] to edit options", 1, WIDTH));
  196.  
  197.   printf("%s\033[%d;1H", menu_attrib->screen, END_ROW);
  198. }
  199.  
  200. const char *edit_cmdline(char *input, int top)
  201. {
  202.   static char cmdline[MAX_CMDLINE_LEN];
  203.   int key, len;
  204.   int redraw = 1;        /* We enter with the menu already drawn */
  205.  
  206.   strncpy(cmdline, input, MAX_CMDLINE_LEN);
  207.   cmdline[MAX_CMDLINE_LEN-1] = '\0';
  208.  
  209.   len = strlen(cmdline);
  210.  
  211.   for (;;) {
  212.     if ( redraw > 1 ) {
  213.       /* Clear and redraw whole screen */
  214.       /* Enable ASCII on G0 and DEC VT on G1; do it in this order
  215.      to avoid confusing the Linux console */
  216.       printf("\033e\033%%@\033)0\033(B%s\033[?25l\033[2J", menu_attrib->screen);
  217.       draw_menu(-1, top);
  218.     }
  219.  
  220.     if ( redraw > 0 ) {
  221.       /* Redraw the command line */
  222.       printf("\033[?25h\033[%d;1H%s> %s%s",
  223.          CMDLINE_ROW, menu_attrib->cmdmark,
  224.          menu_attrib->cmdline, pad_line(cmdline, 0, MAX_CMDLINE_LEN-1));
  225.       printf("%s\033[%d;3H%s",
  226.          menu_attrib->cmdline, CMDLINE_ROW, cmdline);
  227.       redraw = 0;
  228.     }
  229.  
  230.     key = get_key(stdin, 0);
  231.  
  232.     /* FIX: should handle arrow keys and edit-in-middle */
  233.  
  234.     switch( key ) {
  235.     case KEY_CTRL('L'):
  236.       redraw = 2;
  237.       break;
  238.     case KEY_ENTER:
  239.     case KEY_CTRL('J'):
  240.       return cmdline;
  241.     case KEY_ESC:
  242.     case KEY_CTRL('C'):
  243.       return NULL;
  244.     case KEY_BACKSPACE:
  245.     case KEY_DEL:
  246.     case '\x7F':
  247.       if ( len ) {
  248.     cmdline[--len] = '\0';
  249.     redraw = 1;
  250.       }
  251.       break;
  252.     case KEY_CTRL('U'):
  253.       if ( len ) {
  254.     len = 0;
  255.     cmdline[len] = '\0';
  256.     redraw = 1;
  257.       }
  258.       break;
  259.     case KEY_CTRL('W'):
  260.       if ( len ) {
  261.     int wasbs = (cmdline[len-1] <= ' ');
  262.     while ( len && (cmdline[len-1] <= ' ' || !wasbs) ) {
  263.       len--;
  264.       wasbs = wasbs || (cmdline[len-1] <= ' ');
  265.     }
  266.     cmdline[len] = '\0';
  267.     redraw = 1;
  268.       }
  269.       break;
  270.     default:
  271.       if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) {
  272.     cmdline[len] = key;
  273.     cmdline[++len] = '\0';
  274.     putchar(key);
  275.       }
  276.       break;
  277.     }
  278.   }
  279. }
  280.  
  281. const char *run_menu(void)
  282. {
  283.   int key;
  284.   int done = 0;
  285.   int entry = defentry, prev_entry = -1;
  286.   int top = 0, prev_top = -1;
  287.   int clear = 1;
  288.   const char *cmdline = NULL;
  289.   clock_t key_timeout;
  290.  
  291.   /* Convert timeout from deciseconds to clock ticks */
  292.   /* Note: for both key_timeout and timeout == 0 means no limit */
  293.   key_timeout = (clock_t)(CLK_TCK*timeout+9)/10;
  294.  
  295.   while ( !done ) {
  296.     if ( entry < 0 )
  297.       entry = 0;
  298.     else if ( entry >= nentries )
  299.       entry = nentries-1;
  300.  
  301.     if ( top < 0 || top < entry-MENU_ROWS+1 )
  302.       top = max(0, entry-MENU_ROWS+1);
  303.     else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
  304.       top = min(entry, max(0,nentries-MENU_ROWS));
  305.  
  306.     /* Start with a clear screen */
  307.     if ( clear ) {
  308.       /* Clear and redraw whole screen */
  309.       /* Enable ASCII on G0 and DEC VT on G1; do it in this order
  310.      to avoid confusing the Linux console */
  311.       printf("\033e\033%%@\033)0\033(B%s\033[?25l\033[2J", menu_attrib->screen);
  312.       clear = 0;
  313.       prev_entry = prev_top = -1;
  314.     }
  315.  
  316.     if ( top != prev_top ) {
  317.       draw_menu(entry, top);
  318.     } else if ( entry != prev_entry ) {
  319.       draw_row(prev_entry-top+4, entry, top, 0, 0);
  320.       draw_row(entry-top+4, entry, top, 0, 0);
  321.     }
  322.  
  323.     prev_entry = entry;  prev_top = top;
  324.  
  325.     key = get_key(stdin, key_timeout);
  326.     switch ( key ) {
  327.     case KEY_NONE:        /* Timeout */
  328.       /* This is somewhat hacky, but this at least lets the user
  329.      know what's going on, and still deals with "phantom inputs"
  330.      e.g. on serial ports. */
  331.       if ( entry != defentry )
  332.     entry = defentry;
  333.       else {
  334.     cmdline = menu_entries[defentry].label;
  335.     done = 1;
  336.       }
  337.       break;
  338.     case KEY_CTRL('L'):
  339.       clear = 1;
  340.       break;
  341.     case KEY_ENTER:
  342.     case KEY_CTRL('J'):
  343.       cmdline = menu_entries[entry].label;
  344.       done = 1;
  345.       break;
  346.     case 'P':
  347.     case 'p':
  348.     case KEY_UP:
  349.       if ( entry > 0 ) {
  350.     entry--;
  351.     if ( entry < top )
  352.       top -= MENU_ROWS;
  353.       }
  354.       break;
  355.     case 'N':
  356.     case 'n':
  357.     case KEY_DOWN:
  358.       if ( entry < nentries-1 ) {
  359.     entry++;
  360.     if ( entry >= top+MENU_ROWS )
  361.       top += MENU_ROWS;
  362.       }
  363.       break;
  364.     case KEY_CTRL('P'):
  365.     case KEY_PGUP:
  366.     case KEY_LEFT:
  367.       entry -= MENU_ROWS;
  368.       top   -= MENU_ROWS;
  369.       break;
  370.     case KEY_CTRL('N'):
  371.     case KEY_PGDN:
  372.     case KEY_RIGHT:
  373.     case ' ':
  374.       entry += MENU_ROWS;
  375.       top   += MENU_ROWS;
  376.       break;
  377.     case '-':
  378.       entry--;
  379.       top--;
  380.       break;
  381.     case '+':
  382.       entry++;
  383.       top++;
  384.       break;
  385.     case KEY_CTRL('A'):
  386.     case KEY_HOME:
  387.       top = entry = 0;
  388.       break;
  389.     case KEY_CTRL('E'):
  390.     case KEY_END:
  391.       entry = nentries - 1;
  392.       top = max(0, nentries-MENU_ROWS);
  393.       break;
  394.     case KEY_TAB:
  395.       if ( allowedit ) {
  396.     draw_row(entry-top+4, -1, top, 0, 0);
  397.     cmdline = edit_cmdline(menu_entries[entry].cmdline, top);
  398.     done = !!cmdline;
  399.     clear = 1;        /* In case we hit [Esc] and done is null */
  400.       }
  401.       break;
  402.     case KEY_CTRL('C'):        /* Ctrl-C */
  403.     case KEY_ESC:        /* Esc */
  404.       if ( allowedit )
  405.     done = 1;
  406.       break;
  407.     default:
  408.       if ( key > 0 && key < 0xFF ) {
  409.     key &= ~0x20;        /* Upper case */
  410.     if ( menu_hotkeys[key] ) {
  411.       entry = menu_hotkeys[key] - menu_entries;
  412.       /* Should we commit at this point? */
  413.     }
  414.       }
  415.       break;
  416.     }
  417.   }
  418.  
  419.   printf("\033[?25h");        /* Show cursor */
  420.  
  421.   /* Return the label name so localboot and ipappend work */
  422.   return cmdline;
  423. }
  424.  
  425.  
  426. void __attribute__((noreturn)) execute(const char *cmdline)
  427. {
  428. #ifdef __COM32__
  429.   static com32sys_t ireg;
  430.  
  431.   strcpy(__com32.cs_bounce, cmdline);
  432.   ireg.eax.w[0] = 0x0003;    /* Run command */
  433.   ireg.ebx.w[0] = OFFS(__com32.cs_bounce);
  434.   ireg.es = SEG(__com32.cs_bounce);
  435.   __intcall(0x22, &ireg, NULL);
  436.   exit(255);  /* Shouldn't return */
  437. #else
  438.   /* For testing... */
  439.   printf("\n>>> %s\n", cmdline);
  440.   exit(0);
  441. #endif
  442. }
  443.  
  444. int main(int argc, char *argv[])
  445. {
  446.   const char *cmdline = NULL;
  447.   int err = 0;
  448.  
  449.   (void)argc;
  450.  
  451.   console_ansi_raw();
  452.  
  453.   parse_config(argv[1]);
  454.  
  455.   if ( !nentries ) {
  456.     fputs("No LABEL entries found in configuration file!\n", stdout);
  457.     err = 1;
  458.   } else {
  459.     cmdline = run_menu();
  460.   }
  461.  
  462.   printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
  463.   if ( cmdline )
  464.     execute(cmdline);
  465.   else
  466.     return err;
  467. }
  468.