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:
David van Moolenbroek
2012-10-11 00:09:19 +02:00
parent 52cf951fdc
commit 9733fcdb43
8 changed files with 202 additions and 22 deletions

View 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))) {
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);

View 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

View 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 *);

View 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*/
}

View 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)
{