bootloader: usability improvements
- 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.
This commit is contained in:
parent
52cf951fdc
commit
9733fcdb43
8 changed files with 202 additions and 22 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))) {
|
||||
printf("type \"?\" or \"help\" for help.\n");
|
||||
bootmenu(); /* does not return */
|
||||
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, "
|
||||
"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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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*/
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue