]> Zhao Yanbai Git Server - minix.git/commitdiff
bootloader: usability improvements
authorDavid van Moolenbroek <david@minix3.org>
Wed, 10 Oct 2012 22:09:19 +0000 (00:09 +0200)
committerDavid van Moolenbroek <david@minix3.org>
Thu, 11 Oct 2012 23:03:36 +0000 (01:03 +0200)
- add "edit" menu option, to edit menu commands before executing them;
- add "menu" boot command, to return to the menu from the prompt;
- provide more line editing features when getting input;
- fix a few potential buffer overflows as a side effect.

etc/boot.cfg.default
man/man5/boot.cfg.5
releasetools/release.functions
sys/arch/i386/stand/lib/bootmenu.c
sys/arch/i386/stand/lib/conio.S
sys/arch/i386/stand/lib/libi386.h
sys/arch/i386/stand/lib/menuutils.c
sys/arch/i386/stand/lib/pcio.c

index 25113d6ca4d71e68b57c0e6ff90e07c3c3f5d011..dfc971704dcc0e73c3edb029b2de2a73f80dc09b 100644 (file)
@@ -4,4 +4,5 @@ default=2
 menu=Start MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel rootdevname=$rootdevname
 menu=Start latest MINIX 3:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname
 menu=Start latest MINIX 3 in single user mode:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname bootopts=-s
+menu=Edit menu option:edit
 menu=Drop to boot prompt:prompt
index 119ce33a9c19fdf9f2516d1a45702006fc010934..63a23557999629647b4f1f548460ffebc0fb23df 100644 (file)
@@ -130,6 +130,9 @@ Each command is executed just as though the user had typed it in
 and so can be any valid command that would be accepted at the
 normal boot prompt.
 In addition,
+.Dq Ic edit
+can be used to put the menu in editing mode, allowing the user to modify the
+next selected option before executing it, and
 .Dq Ic prompt
 can be used to drop to the normal boot prompt.
 .It Sy timeout
index 2719f0af346bdba674bad00657ec09e8097e6e9d..0ef40c18df9b51607906f6a12a552c70399b31ad 100644 (file)
@@ -61,6 +61,7 @@ banner=Welcome to the MINIX 3 installation CD
 banner================================================================================
 banner=
 menu=Regular MINIX 3:multiboot /kernel bootcd=1 cdproberoot=1 rootdevname=ram disable=inet
+menu=Edit menu option:edit
 menu=Drop to boot prompt:prompt
 clear=1
 timeout=10
@@ -106,6 +107,7 @@ hdemu_root_changes()
 
 cat >$RELEASEMNTDIR/boot.cfg <<END_BOOT_CFG
 menu=Regular MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel bootcd=2 disable=inet bios_wini=yes bios_remap_first=1 ramimagedev=c0d7p0s0
+menu=Edit menu option:edit
 menu=Drop to boot prompt:prompt
 clear=1
 timeout=10
@@ -120,6 +122,7 @@ usb_root_changes()
        echo \
 cat >$RELEASEMNTDIR/boot.cfg <<END_BOOT_CFG
 menu=Regular MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel bios_wini=yes bios_remap_first=1 rootdevname=c0d7p0s0
+menu=Edit menu option:edit
 menu=Drop to boot prompt:prompt
 clear=1
 timeout=10
index 038d12648dfa55e4ebd9c84811d35d512f179f6c..f2e0a4dae947ccd1bba4c37d72bfbe12dc425b26 100644 (file)
@@ -297,11 +297,10 @@ getchoicefrominput(char *input, int def)
        return choice;
 }
 
-void
-doboottypemenu(void)
+static void
+showmenu(void)
 {
        int choice;
-       char input[80], *ic, *oc;
 
        printf("\n");
        /* Display menu */
@@ -317,19 +316,31 @@ doboottypemenu(void)
                            choice + 1,
                            bootconf.desc[choice]);
        }
+}
+
+void
+doboottypemenu(void)
+{
+       int choice, editing;
+       char input[256], *ic, *oc;
+
+       showmenu();
        choice = -1;
+       editing = 0;
        for (;;) {
                input[0] = '\0';
 
                if (bootconf.timeout < 0) {
                        if (bootconf.menuformat == MENUFORMAT_LETTER)
-                               printf("\nOption: [%c]:",
+                               printf("\nOption%s: [%c]:",
+                                   editing ? " (edit)" : "",
                                    bootconf.def + 'A');
                        else
-                               printf("\nOption: [%d]:",
+                               printf("\nOption%s: [%d]:",
+                                   editing ? " (edit)" : "",
                                    bootconf.def + 1);
 
-                       gets(input);
+                       editline(input, sizeof(input), NULL);
                        choice = getchoicefrominput(input, bootconf.def);
                } else if (bootconf.timeout == 0)
                        choice = bootconf.def;
@@ -351,27 +362,45 @@ doboottypemenu(void)
                }
                if (choice < 0)
                        continue;
-               if (!strcmp(bootconf.command[choice], "prompt") &&
+               ic = bootconf.command[choice];
+               if (editing) {
+                       printf("> ");
+                       editline(input, sizeof(input), ic);
+                       ic = input;
+               }
+               if (!strcmp(ic, "edit") &&
+                   ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
+                   check_password((char *)boot_params.bp_password))) {
+                       editing = 1;
+                       bootconf.timeout = -1;
+               } else if (!strcmp(ic, "prompt") &&
                    ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
                    check_password((char *)boot_params.bp_password))) {
-                       printf("type \"?\" or \"help\" for help.\n");
-                       bootmenu(); /* does not return */
+                       printf("type \"?\" or \"help\" for help, "
+                           "or \"menu\" to return to the menu.\n");
+                       prompt(1);
+                       showmenu();
+                       editing = 0;
+                       bootconf.timeout = -1;
                } else {
-                       ic = bootconf.command[choice];
                        /* Split command string at ; into separate commands */
                        do {
+                               /*
+                                * This must support inline editing, since ic
+                                * may also point to input.
+                                */
                                oc = input;
                                /* Look for ; separator */
                                for (; *ic && *ic != COMMAND_SEPARATOR; ic++)
                                        *oc++ = *ic;
-                               if (*input == '\0')
+                               if (*ic == COMMAND_SEPARATOR)
+                                       ic++;
+                               if (oc == input)
                                        continue;
                                /* Strip out any trailing spaces */
                                oc--;
                                for (; *oc == ' ' && oc > input; oc--);
                                *++oc = '\0';
-                               if (*ic == COMMAND_SEPARATOR)
-                                       ic++;
                                /* Stop silly command strings like ;;; */
                                if (*input != '\0')
                                        docommand(input);
index dd04a790f284043299df8fcefed7de321265fffb..3f62ec15f6e74227a4df4c1604efe7162a2f39ce 100644 (file)
@@ -45,9 +45,30 @@ ENTRY(conputc)
        call    _C_LABEL(prot_to_real)  # enter real mode
        .code16
 
+       cmp     $0x08, %al      # backspace?
+       jne     print_char
+
+       # Multiline backspace support.
+       push    %ax
+       movb    $0x3, %ah       # get cursor position
+       xorw    %bx, %bx
+       int     $0x10
+       pop     %ax
+       testb   %dl, %dl        # cursor on first column?
+       jnz     print_char
+       testb   %dh, %dh        # cursor not on first row?
+       jz      print_char
+       decb    %dh             # move up one row
+       movb    $0x4f, %dl      # move to last column
+       xorw    %bx, %bx
+       movb    $0x02, %ah      # set cursor position
+       jmp     do_int
+
+print_char:
        movw    $1,%bx
-       movb    $0x0e,%ah
+       movb    $0x0e,%ah       # print character
        movb    %al, %cl
+do_int:
        int     $0x10
 
        calll   _C_LABEL(real_to_prot) # back to protected mode
@@ -68,12 +89,12 @@ ENTRY(congetc)
 
        movb    $0x0,%ah
        int     $0x16
-       movb    %al,%bl
+       movw    %ax,%bx
 
        calll   _C_LABEL(real_to_prot) # back to protected mode
        .code32
 
-       movb    %bl, 28(%esp)
+       movw    %bx, 28(%esp)
 
        popa
        ret
index 2fb0ec5cb34c03c3c183149bf38c931ac789aa3c..6f28e627430d58a12d14bd173f6e94754b4f0b1e 100644 (file)
@@ -91,7 +91,9 @@ struct bootblk_command {
        void (*c_fn)(char *);
 };
 void bootmenu(void);
+void prompt(int);
 void docommand(char *);
+void editline(char *, size_t, char *);
 
 /* in "user code": */
 void command_help(char *);
@@ -114,6 +116,8 @@ int coniskey(void);
 __compactcall void conputc(int);
 void conclr(void);
 
+int getchar_ex(void);
+
 int getextmem2(int *);
 __compactcall int getextmemps2(void *);
 int getmementry(int *, int *);
index 92904ac70a691ddda78c28dd7b467793d89dbd71..f613e30549351962ebc3678a7be7bad9857634a1 100644 (file)
@@ -62,23 +62,136 @@ docommand(char *arg)
 }
 
 void
-bootmenu(void)
+prompt(int allowreturn)
 {
        char input[80];
 
        for (;;) {
                char *c = input;
 
-               input[0] = '\0';
                printf("> ");
-               gets(input);
+               editline(input, sizeof(input), NULL);
 
                /*
                 * Skip leading whitespace.
                 */
                while (*c == ' ')
                        c++;
+               if (allowreturn && !strcmp(c, "menu"))
+                       break;
                if (*c)
                        docommand(c);
        }
 }
+
+void
+bootmenu(void)
+{
+       prompt(0);
+}
+
+/* Derived from libsa gets(). */
+void
+editline(char *buf, size_t size, char *input)
+{
+       int c, i, pos, len = 0;
+
+       /* If an initial input has been given, copy and print this first. */
+       if (input != NULL) {
+               while (*input && len < size - 1)
+                       putchar(buf[len++] = *input++);
+       }
+       pos = len;
+
+       for (;;) {
+               c = getchar_ex();
+               switch (c & 0177) {
+               case '\0':
+                       switch (c) {
+                       case 0x4b00: /* Left arrow: move cursor to left. */
+                               if (pos > 0) {
+                                       putchar('\b');
+                                       pos--;
+                               }
+                               break;
+                       case 0x4d00: /* Right arrow: move cursor to right. */
+                               if (pos < len) putchar(buf[pos++]);
+                               break;
+                       }
+                       break;
+               case 'b' & 037: /* Ctrl+B: move cursor to left. */
+                       if (pos > 0) {
+                               putchar('\b');
+                               pos--;
+                       }
+                       break;
+               case 'f' & 037: /* Ctrl+F: move cursor to right. */
+                       if (pos < len) putchar(buf[pos++]);
+                       break;
+               case 'a' & 037: /* Ctrl+A: move cursor to start of line. */
+                       for ( ; pos > 0; pos--) putchar('\b');
+                       break;
+               case 'e' & 037: /* Ctrl+E: move cursor to end of line. */
+                       for ( ; pos < len; pos++) putchar(buf[pos]);
+                       break;
+               case '\n': /* Enter: return line. */
+               case '\r':
+                       for ( ; pos < len; pos++) putchar(buf[pos]);
+                       buf[len] = '\0';
+                       putchar('\n');
+                       return;
+#if HASH_ERASE
+               case '#':
+#endif
+               case '\b': /* Backspace: erase character before cursor. */
+               case '\177':
+                       if (pos > 0) {
+                               pos--;
+                               len--;
+                               putchar('\b');
+                               for (i = pos; i < len; i++)
+                                       putchar(buf[i] = buf[i + 1]);
+                               putchar(' ');
+                               for (i = pos; i < len; i++) putchar('\b');
+                               putchar('\b');
+                       }
+                       break;
+               case 'r' & 037: /* Ctrl+R: reprint line. */
+                       putchar('\n');
+                       for (i = 0; i < len; i++) putchar(buf[i]);
+                       for (i = len; i > pos; i--) putchar('\b');
+                       break;
+#if AT_ERASE
+               case '@':
+#endif
+               case 'u' & 037: /* Ctrl+U: clear entire line. */
+               case 'w' & 037:
+                       for ( ; pos > 0; pos--) putchar('\b');
+                       for ( ; pos < len; pos++) putchar(' ');
+                       for ( ; pos > 0; pos--) putchar('\b');
+                       len = 0;
+                       break;
+               case '\a': /* Ctrl+G: sound bell but do not store character. */
+                       putchar(c);
+                       break;
+               case '\t': /* Tab: convert to single space. */
+                       c = ' ';
+                       /*FALLTHROUGH*/
+               default: /* Insert character at cursor position. */
+                       if (len < size - 1) {
+                               for (i = len; i > pos; i--)
+                                       buf[i] = buf[i - 1];
+                               buf[pos] = c;
+                               pos++;
+                               len++;
+                               putchar(c);
+                               for (i = pos; i < len; i++) putchar(buf[i]);
+                               for (i = pos; i < len; i++) putchar('\b');
+                       } else {
+                               putchar('\a');
+                       }
+                       break;
+               }
+       }
+       /*NOTREACHED*/
+}
index d03e4466bc34dc81b88817cb297e3c7f01f8c001..f457244515e81b468c652f53ae70c419f68e1875 100644 (file)
@@ -259,7 +259,7 @@ putchar(int c)
 }
 
 int
-getchar(void)
+getchar_ex(void)
 {
        int c;
 #ifdef SUPPORT_SERIAL
@@ -272,9 +272,9 @@ getchar(void)
                c = congetc();
 #ifdef CONSOLE_KEYMAP
                {
-                       char *cp = strchr(CONSOLE_KEYMAP, c);
+                       char *cp = strchr(CONSOLE_KEYMAP, c & 0xff);
                        if (cp != 0 && cp[1] != 0)
-                               c = cp[1];
+                               c = cp[1] | (c & 0xff00);
                }
 #endif
                return c;
@@ -302,6 +302,12 @@ getchar(void)
 #endif /* SUPPORT_SERIAL */
 }
 
+int
+getchar(void)
+{
+       return getchar_ex() & 0xff;
+}
+
 int
 iskey(int intr)
 {