1*84d9c625SLionel Sambuc /* $NetBSD: bootmenu.c,v 1.11 2013/07/28 08:50:09 he Exp $ */ 258a2b000SEvgeniy Ivanov 358a2b000SEvgeniy Ivanov /*- 458a2b000SEvgeniy Ivanov * Copyright (c) 2008 The NetBSD Foundation, Inc. 558a2b000SEvgeniy Ivanov * All rights reserved. 658a2b000SEvgeniy Ivanov * 758a2b000SEvgeniy Ivanov * Redistribution and use in source and binary forms, with or without 858a2b000SEvgeniy Ivanov * modification, are permitted provided that the following conditions 958a2b000SEvgeniy Ivanov * are met: 1058a2b000SEvgeniy Ivanov * 1. Redistributions of source code must retain the above copyright 1158a2b000SEvgeniy Ivanov * notice, this list of conditions and the following disclaimer. 1258a2b000SEvgeniy Ivanov * 2. Redistributions in binary form must reproduce the above copyright 1358a2b000SEvgeniy Ivanov * notice, this list of conditions and the following disclaimer in the 1458a2b000SEvgeniy Ivanov * documentation and/or other materials provided with the distribution. 1558a2b000SEvgeniy Ivanov * 1658a2b000SEvgeniy Ivanov * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 1758a2b000SEvgeniy Ivanov * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1858a2b000SEvgeniy Ivanov * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1958a2b000SEvgeniy Ivanov * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2058a2b000SEvgeniy Ivanov * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2158a2b000SEvgeniy Ivanov * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2258a2b000SEvgeniy Ivanov * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2358a2b000SEvgeniy Ivanov * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2458a2b000SEvgeniy Ivanov * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2558a2b000SEvgeniy Ivanov * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2658a2b000SEvgeniy Ivanov * POSSIBILITY OF SUCH DAMAGE. 2758a2b000SEvgeniy Ivanov */ 2858a2b000SEvgeniy Ivanov 2958a2b000SEvgeniy Ivanov #ifndef SMALL 3058a2b000SEvgeniy Ivanov 3158a2b000SEvgeniy Ivanov #include <sys/types.h> 3258a2b000SEvgeniy Ivanov #include <sys/reboot.h> 3358a2b000SEvgeniy Ivanov #include <sys/bootblock.h> 3458a2b000SEvgeniy Ivanov 3558a2b000SEvgeniy Ivanov #include <lib/libsa/stand.h> 3658a2b000SEvgeniy Ivanov #include <lib/libsa/ufs.h> 3758a2b000SEvgeniy Ivanov #include <lib/libkern/libkern.h> 3858a2b000SEvgeniy Ivanov 3958a2b000SEvgeniy Ivanov #include <libi386.h> 4058a2b000SEvgeniy Ivanov #include <bootmenu.h> 4158a2b000SEvgeniy Ivanov 4258a2b000SEvgeniy Ivanov #define isnum(c) ((c) >= '0' && (c) <= '9') 4358a2b000SEvgeniy Ivanov 44*84d9c625SLionel Sambuc static void docommandchoice(int); 45*84d9c625SLionel Sambuc 4658a2b000SEvgeniy Ivanov extern struct x86_boot_params boot_params; 4758a2b000SEvgeniy Ivanov extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[]; 4858a2b000SEvgeniy Ivanov 4958a2b000SEvgeniy Ivanov #define MENUFORMAT_AUTO 0 5058a2b000SEvgeniy Ivanov #define MENUFORMAT_NUMBER 1 5158a2b000SEvgeniy Ivanov #define MENUFORMAT_LETTER 2 5258a2b000SEvgeniy Ivanov 5358a2b000SEvgeniy Ivanov struct bootconf_def bootconf; 5458a2b000SEvgeniy Ivanov 5558a2b000SEvgeniy Ivanov int 5658a2b000SEvgeniy Ivanov atoi(const char *in) 5758a2b000SEvgeniy Ivanov { 5858a2b000SEvgeniy Ivanov char *c; 5958a2b000SEvgeniy Ivanov int ret; 6058a2b000SEvgeniy Ivanov 6158a2b000SEvgeniy Ivanov ret = 0; 6258a2b000SEvgeniy Ivanov c = (char *)in; 6358a2b000SEvgeniy Ivanov if (*c == '-') 6458a2b000SEvgeniy Ivanov c++; 6558a2b000SEvgeniy Ivanov for (; isnum(*c); c++) 6658a2b000SEvgeniy Ivanov ret = (ret * 10) + (*c - '0'); 6758a2b000SEvgeniy Ivanov 6858a2b000SEvgeniy Ivanov return (*in == '-') ? -ret : ret; 6958a2b000SEvgeniy Ivanov } 7058a2b000SEvgeniy Ivanov 7158a2b000SEvgeniy Ivanov /* 7258a2b000SEvgeniy Ivanov * This function parses a boot.cfg file in the root of the filesystem 7358a2b000SEvgeniy Ivanov * (if present) and populates the global boot configuration. 7458a2b000SEvgeniy Ivanov * 7558a2b000SEvgeniy Ivanov * The file consists of a number of lines each terminated by \n 7658a2b000SEvgeniy Ivanov * The lines are in the format keyword=value. There should not be spaces 7758a2b000SEvgeniy Ivanov * around the = sign. 7858a2b000SEvgeniy Ivanov * 7958a2b000SEvgeniy Ivanov * The recognised keywords are: 8058a2b000SEvgeniy Ivanov * banner: text displayed instead of the normal welcome text 8158a2b000SEvgeniy Ivanov * menu: Descriptive text:command to use 8258a2b000SEvgeniy Ivanov * timeout: Timeout in seconds (overrides that set by installboot) 8358a2b000SEvgeniy Ivanov * default: the default menu option to use if Return is pressed 8458a2b000SEvgeniy Ivanov * consdev: the console device to use 8558a2b000SEvgeniy Ivanov * format: how menu choices are displayed: (a)utomatic, (n)umbers or (l)etters 8658a2b000SEvgeniy Ivanov * clear: whether to clear the screen or not 8758a2b000SEvgeniy Ivanov * 8858a2b000SEvgeniy Ivanov * Example boot.cfg file: 8958a2b000SEvgeniy Ivanov * banner=Welcome to NetBSD 9058a2b000SEvgeniy Ivanov * banner=Please choose the boot type from the following menu 9158a2b000SEvgeniy Ivanov * menu=Boot NetBSD:boot netbsd 9258a2b000SEvgeniy Ivanov * menu=Boot into single user mode:boot netbsd -s 9358a2b000SEvgeniy Ivanov * menu=:boot hd1a:netbsd -cs 9458a2b000SEvgeniy Ivanov * menu=Goto boot comand line:prompt 9558a2b000SEvgeniy Ivanov * timeout=10 9658a2b000SEvgeniy Ivanov * consdev=com0 9758a2b000SEvgeniy Ivanov * default=1 9858a2b000SEvgeniy Ivanov */ 9958a2b000SEvgeniy Ivanov void 10058a2b000SEvgeniy Ivanov parsebootconf(const char *conf) 10158a2b000SEvgeniy Ivanov { 10258a2b000SEvgeniy Ivanov char *bc, *c; 10358a2b000SEvgeniy Ivanov int cmenu, cbanner, len; 10458a2b000SEvgeniy Ivanov int fd, err, off; 10558a2b000SEvgeniy Ivanov struct stat st; 10658a2b000SEvgeniy Ivanov char *next, *key, *value, *v2; 10758a2b000SEvgeniy Ivanov 10858a2b000SEvgeniy Ivanov /* Clear bootconf structure */ 10958a2b000SEvgeniy Ivanov memset((void *)&bootconf, 0, sizeof(bootconf)); 11058a2b000SEvgeniy Ivanov 11158a2b000SEvgeniy Ivanov /* Set timeout to configured */ 11258a2b000SEvgeniy Ivanov bootconf.timeout = boot_params.bp_timeout; 11358a2b000SEvgeniy Ivanov 11458a2b000SEvgeniy Ivanov /* automatically switch between letter and numbers on menu */ 11558a2b000SEvgeniy Ivanov bootconf.menuformat = MENUFORMAT_AUTO; 11658a2b000SEvgeniy Ivanov 11758a2b000SEvgeniy Ivanov fd = open(BOOTCONF, 0); 11858a2b000SEvgeniy Ivanov if (fd < 0) 11958a2b000SEvgeniy Ivanov return; 12058a2b000SEvgeniy Ivanov 12158a2b000SEvgeniy Ivanov err = fstat(fd, &st); 12258a2b000SEvgeniy Ivanov if (err == -1) { 12358a2b000SEvgeniy Ivanov close(fd); 12458a2b000SEvgeniy Ivanov return; 12558a2b000SEvgeniy Ivanov } 12658a2b000SEvgeniy Ivanov 12758a2b000SEvgeniy Ivanov /* 12858a2b000SEvgeniy Ivanov * Check the size. A bootconf file is normally only a few 12958a2b000SEvgeniy Ivanov * hundred bytes long. If it is much bigger than expected, 13058a2b000SEvgeniy Ivanov * don't try to load it. We can't load something big into 13158a2b000SEvgeniy Ivanov * an 8086 real mode segment anyway, and in pxeboot this is 13258a2b000SEvgeniy Ivanov * probably a case of the loader getting a filename for the 13358a2b000SEvgeniy Ivanov * kernel and thinking it is boot.cfg by accident. (The 32k 13458a2b000SEvgeniy Ivanov * number is arbitrary but 8086 real mode data segments max 13558a2b000SEvgeniy Ivanov * out at 64k.) 13658a2b000SEvgeniy Ivanov */ 13758a2b000SEvgeniy Ivanov if (st.st_size > 32768) { 13858a2b000SEvgeniy Ivanov close(fd); 13958a2b000SEvgeniy Ivanov return; 14058a2b000SEvgeniy Ivanov } 14158a2b000SEvgeniy Ivanov 14258a2b000SEvgeniy Ivanov bc = alloc(st.st_size + 1); 14358a2b000SEvgeniy Ivanov if (bc == NULL) { 14458a2b000SEvgeniy Ivanov printf("Could not allocate memory for boot configuration\n"); 14558a2b000SEvgeniy Ivanov return; 14658a2b000SEvgeniy Ivanov } 14758a2b000SEvgeniy Ivanov 14858a2b000SEvgeniy Ivanov off = 0; 14958a2b000SEvgeniy Ivanov do { 15058a2b000SEvgeniy Ivanov len = read(fd, bc + off, 1024); 15158a2b000SEvgeniy Ivanov if (len <= 0) 15258a2b000SEvgeniy Ivanov break; 15358a2b000SEvgeniy Ivanov off += len; 15458a2b000SEvgeniy Ivanov } while (len > 0); 15558a2b000SEvgeniy Ivanov bc[off] = '\0'; 15658a2b000SEvgeniy Ivanov 15758a2b000SEvgeniy Ivanov close(fd); 15858a2b000SEvgeniy Ivanov /* bc now contains the whole boot.cfg file */ 15958a2b000SEvgeniy Ivanov 16058a2b000SEvgeniy Ivanov cmenu = 0; 16158a2b000SEvgeniy Ivanov cbanner = 0; 16258a2b000SEvgeniy Ivanov for (c = bc; *c; c = next) { 16358a2b000SEvgeniy Ivanov key = c; 16458a2b000SEvgeniy Ivanov /* find end of line */ 16558a2b000SEvgeniy Ivanov for (; *c && *c != '\n'; c++) 16658a2b000SEvgeniy Ivanov /* zero terminate line on start of comment */ 16758a2b000SEvgeniy Ivanov if (*c == '#') 16858a2b000SEvgeniy Ivanov *c = 0; 16958a2b000SEvgeniy Ivanov /* zero terminate line */ 17058a2b000SEvgeniy Ivanov if (*(next = c)) 17158a2b000SEvgeniy Ivanov *next++ = 0; 17258a2b000SEvgeniy Ivanov /* Look for = separator between key and value */ 17358a2b000SEvgeniy Ivanov for (c = key; *c && *c != '='; c++) 17458a2b000SEvgeniy Ivanov continue; 17558a2b000SEvgeniy Ivanov /* Ignore lines with no key=value pair */ 17658a2b000SEvgeniy Ivanov if (*c == '\0') 17758a2b000SEvgeniy Ivanov continue; 17858a2b000SEvgeniy Ivanov 17958a2b000SEvgeniy Ivanov /* zero terminate key which points to keyword */ 18058a2b000SEvgeniy Ivanov *c++ = 0; 18158a2b000SEvgeniy Ivanov value = c; 18258a2b000SEvgeniy Ivanov /* Look for end of line (or file) and zero terminate value */ 18358a2b000SEvgeniy Ivanov for (; *c && *c != '\n'; c++) 18458a2b000SEvgeniy Ivanov continue; 18558a2b000SEvgeniy Ivanov *c = 0; 18658a2b000SEvgeniy Ivanov 18758a2b000SEvgeniy Ivanov if (!strncmp(key, "menu", 4)) { 18858a2b000SEvgeniy Ivanov /* 18958a2b000SEvgeniy Ivanov * Parse "menu=<description>:<command>". If the 19058a2b000SEvgeniy Ivanov * description is empty ("menu=:<command>)", 19158a2b000SEvgeniy Ivanov * then re-use the command as the description. 19258a2b000SEvgeniy Ivanov * Note that the command may contain embedded 19358a2b000SEvgeniy Ivanov * colons. 19458a2b000SEvgeniy Ivanov */ 19558a2b000SEvgeniy Ivanov if (cmenu >= MAXMENU) 19658a2b000SEvgeniy Ivanov continue; 19758a2b000SEvgeniy Ivanov bootconf.desc[cmenu] = value; 19858a2b000SEvgeniy Ivanov for (v2 = value; *v2 && *v2 != ':'; v2++) 19958a2b000SEvgeniy Ivanov continue; 20058a2b000SEvgeniy Ivanov if (*v2) { 20158a2b000SEvgeniy Ivanov *v2++ = 0; 20258a2b000SEvgeniy Ivanov bootconf.command[cmenu] = v2; 20358a2b000SEvgeniy Ivanov if (! *value) 20458a2b000SEvgeniy Ivanov bootconf.desc[cmenu] = v2; 20558a2b000SEvgeniy Ivanov cmenu++; 20658a2b000SEvgeniy Ivanov } else { 20758a2b000SEvgeniy Ivanov /* No delimiter means invalid line */ 20858a2b000SEvgeniy Ivanov bootconf.desc[cmenu] = NULL; 20958a2b000SEvgeniy Ivanov } 21058a2b000SEvgeniy Ivanov } else if (!strncmp(key, "banner", 6)) { 21158a2b000SEvgeniy Ivanov if (cbanner < MAXBANNER) 21258a2b000SEvgeniy Ivanov bootconf.banner[cbanner++] = value; 21358a2b000SEvgeniy Ivanov } else if (!strncmp(key, "timeout", 7)) { 21458a2b000SEvgeniy Ivanov if (!isnum(*value)) 21558a2b000SEvgeniy Ivanov bootconf.timeout = -1; 21658a2b000SEvgeniy Ivanov else 21758a2b000SEvgeniy Ivanov bootconf.timeout = atoi(value); 21858a2b000SEvgeniy Ivanov } else if (!strncmp(key, "default", 7)) { 21958a2b000SEvgeniy Ivanov bootconf.def = atoi(value) - 1; 22058a2b000SEvgeniy Ivanov } else if (!strncmp(key, "consdev", 7)) { 22158a2b000SEvgeniy Ivanov bootconf.consdev = value; 22258a2b000SEvgeniy Ivanov } else if (!strncmp(key, "load", 4)) { 22358a2b000SEvgeniy Ivanov module_add(value); 22458a2b000SEvgeniy Ivanov } else if (!strncmp(key, "format", 6)) { 22558a2b000SEvgeniy Ivanov printf("value:%c\n", *value); 22658a2b000SEvgeniy Ivanov switch (*value) { 22758a2b000SEvgeniy Ivanov case 'a': 22858a2b000SEvgeniy Ivanov case 'A': 22958a2b000SEvgeniy Ivanov bootconf.menuformat = MENUFORMAT_AUTO; 23058a2b000SEvgeniy Ivanov break; 23158a2b000SEvgeniy Ivanov 23258a2b000SEvgeniy Ivanov case 'n': 23358a2b000SEvgeniy Ivanov case 'N': 23458a2b000SEvgeniy Ivanov case 'd': 23558a2b000SEvgeniy Ivanov case 'D': 23658a2b000SEvgeniy Ivanov bootconf.menuformat = MENUFORMAT_NUMBER; 23758a2b000SEvgeniy Ivanov break; 23858a2b000SEvgeniy Ivanov 23958a2b000SEvgeniy Ivanov case 'l': 24058a2b000SEvgeniy Ivanov case 'L': 24158a2b000SEvgeniy Ivanov bootconf.menuformat = MENUFORMAT_LETTER; 24258a2b000SEvgeniy Ivanov break; 24358a2b000SEvgeniy Ivanov } 24458a2b000SEvgeniy Ivanov } else if (!strncmp(key, "clear", 5)) { 24558a2b000SEvgeniy Ivanov bootconf.clear = !!atoi(value); 24658a2b000SEvgeniy Ivanov } else if (!strncmp(key, "userconf", 8)) { 24758a2b000SEvgeniy Ivanov userconf_add(value); 24858a2b000SEvgeniy Ivanov } 24958a2b000SEvgeniy Ivanov } 25058a2b000SEvgeniy Ivanov switch (bootconf.menuformat) { 25158a2b000SEvgeniy Ivanov case MENUFORMAT_AUTO: 25258a2b000SEvgeniy Ivanov if (cmenu > 9 && bootconf.timeout > 0) 25358a2b000SEvgeniy Ivanov bootconf.menuformat = MENUFORMAT_LETTER; 25458a2b000SEvgeniy Ivanov else 25558a2b000SEvgeniy Ivanov bootconf.menuformat = MENUFORMAT_NUMBER; 25658a2b000SEvgeniy Ivanov break; 25758a2b000SEvgeniy Ivanov 25858a2b000SEvgeniy Ivanov case MENUFORMAT_NUMBER: 25958a2b000SEvgeniy Ivanov if (cmenu > 9 && bootconf.timeout > 0) 26058a2b000SEvgeniy Ivanov cmenu = 9; 26158a2b000SEvgeniy Ivanov break; 26258a2b000SEvgeniy Ivanov } 26358a2b000SEvgeniy Ivanov 26458a2b000SEvgeniy Ivanov bootconf.nummenu = cmenu; 26558a2b000SEvgeniy Ivanov if (bootconf.def < 0) 26658a2b000SEvgeniy Ivanov bootconf.def = 0; 26758a2b000SEvgeniy Ivanov if (bootconf.def >= cmenu) 26858a2b000SEvgeniy Ivanov bootconf.def = cmenu - 1; 26958a2b000SEvgeniy Ivanov } 27058a2b000SEvgeniy Ivanov 27158a2b000SEvgeniy Ivanov /* 27258a2b000SEvgeniy Ivanov * doboottypemenu will render the menu and parse any user input 27358a2b000SEvgeniy Ivanov */ 27458a2b000SEvgeniy Ivanov static int 27558a2b000SEvgeniy Ivanov getchoicefrominput(char *input, int def) 27658a2b000SEvgeniy Ivanov { 27758a2b000SEvgeniy Ivanov int choice, usedef; 27858a2b000SEvgeniy Ivanov 27958a2b000SEvgeniy Ivanov choice = -1; 28058a2b000SEvgeniy Ivanov usedef = 0; 28158a2b000SEvgeniy Ivanov 28258a2b000SEvgeniy Ivanov if (*input == '\0' || *input == '\r' || *input == '\n') { 28358a2b000SEvgeniy Ivanov choice = def; 28458a2b000SEvgeniy Ivanov usedef = 1; 28558a2b000SEvgeniy Ivanov } else if (*input >= 'A' && *input < bootconf.nummenu + 'A') 28658a2b000SEvgeniy Ivanov choice = (*input) - 'A'; 28758a2b000SEvgeniy Ivanov else if (*input >= 'a' && *input < bootconf.nummenu + 'a') 28858a2b000SEvgeniy Ivanov choice = (*input) - 'a'; 28958a2b000SEvgeniy Ivanov else if (isnum(*input)) { 29058a2b000SEvgeniy Ivanov choice = atoi(input) - 1; 29158a2b000SEvgeniy Ivanov if (choice < 0 || choice >= bootconf.nummenu) 29258a2b000SEvgeniy Ivanov choice = -1; 29358a2b000SEvgeniy Ivanov } 29458a2b000SEvgeniy Ivanov 29558a2b000SEvgeniy Ivanov if (bootconf.menuformat != MENUFORMAT_LETTER && 29658a2b000SEvgeniy Ivanov !isnum(*input) && !usedef) 29758a2b000SEvgeniy Ivanov choice = -1; 29858a2b000SEvgeniy Ivanov 29958a2b000SEvgeniy Ivanov return choice; 30058a2b000SEvgeniy Ivanov } 30158a2b000SEvgeniy Ivanov 3029733fcdbSDavid van Moolenbroek static void 303*84d9c625SLionel Sambuc docommandchoice(int choice) 304*84d9c625SLionel Sambuc { 305*84d9c625SLionel Sambuc char input[80], *ic, *oc; 306*84d9c625SLionel Sambuc 307*84d9c625SLionel Sambuc ic = bootconf.command[choice]; 308*84d9c625SLionel Sambuc /* Split command string at ; into separate commands */ 309*84d9c625SLionel Sambuc do { 310*84d9c625SLionel Sambuc oc = input; 311*84d9c625SLionel Sambuc /* Look for ; separator */ 312*84d9c625SLionel Sambuc for (; *ic && *ic != COMMAND_SEPARATOR; ic++) 313*84d9c625SLionel Sambuc *oc++ = *ic; 314*84d9c625SLionel Sambuc if (*input == '\0') 315*84d9c625SLionel Sambuc continue; 316*84d9c625SLionel Sambuc /* Strip out any trailing spaces */ 317*84d9c625SLionel Sambuc oc--; 318*84d9c625SLionel Sambuc for (; *oc == ' ' && oc > input; oc--); 319*84d9c625SLionel Sambuc *++oc = '\0'; 320*84d9c625SLionel Sambuc if (*ic == COMMAND_SEPARATOR) 321*84d9c625SLionel Sambuc ic++; 322*84d9c625SLionel Sambuc /* Stop silly command strings like ;;; */ 323*84d9c625SLionel Sambuc if (*input != '\0') 324*84d9c625SLionel Sambuc docommand(input); 325*84d9c625SLionel Sambuc /* Skip leading spaces */ 326*84d9c625SLionel Sambuc for (; *ic == ' '; ic++); 327*84d9c625SLionel Sambuc } while (*ic); 328*84d9c625SLionel Sambuc } 329*84d9c625SLionel Sambuc 330*84d9c625SLionel Sambuc void 331*84d9c625SLionel Sambuc bootdefault(void) 332*84d9c625SLionel Sambuc { 333*84d9c625SLionel Sambuc int choice; 334*84d9c625SLionel Sambuc static int entered; 335*84d9c625SLionel Sambuc 336*84d9c625SLionel Sambuc if (bootconf.nummenu > 0) { 337*84d9c625SLionel Sambuc if (entered) { 338*84d9c625SLionel Sambuc printf("default boot twice, skipping...\n"); 339*84d9c625SLionel Sambuc return; 340*84d9c625SLionel Sambuc } 341*84d9c625SLionel Sambuc entered = 1; 342*84d9c625SLionel Sambuc choice = bootconf.def; 343*84d9c625SLionel Sambuc printf("command(s): %s\n", bootconf.command[choice]); 344*84d9c625SLionel Sambuc docommandchoice(choice); 345*84d9c625SLionel Sambuc } 346*84d9c625SLionel Sambuc } 347*84d9c625SLionel Sambuc 348*84d9c625SLionel Sambuc #if defined(__minix) 349*84d9c625SLionel Sambuc static void 3509733fcdbSDavid van Moolenbroek showmenu(void) 35158a2b000SEvgeniy Ivanov { 35258a2b000SEvgeniy Ivanov int choice; 35358a2b000SEvgeniy Ivanov 35458a2b000SEvgeniy Ivanov printf("\n"); 35558a2b000SEvgeniy Ivanov /* Display menu */ 35658a2b000SEvgeniy Ivanov if (bootconf.menuformat == MENUFORMAT_LETTER) { 35758a2b000SEvgeniy Ivanov for (choice = 0; choice < bootconf.nummenu; choice++) 35858a2b000SEvgeniy Ivanov printf(" %c. %s\n", choice + 'A', 35958a2b000SEvgeniy Ivanov bootconf.desc[choice]); 36058a2b000SEvgeniy Ivanov } else { 36158a2b000SEvgeniy Ivanov /* Can't use %2d format string with libsa */ 36258a2b000SEvgeniy Ivanov for (choice = 0; choice < bootconf.nummenu; choice++) 36358a2b000SEvgeniy Ivanov printf(" %s%d. %s\n", 36458a2b000SEvgeniy Ivanov (choice < 9) ? " " : "", 36558a2b000SEvgeniy Ivanov choice + 1, 36658a2b000SEvgeniy Ivanov bootconf.desc[choice]); 36758a2b000SEvgeniy Ivanov } 3689733fcdbSDavid van Moolenbroek } 369*84d9c625SLionel Sambuc #endif /* defined(__minix) */ 3709733fcdbSDavid van Moolenbroek 3719733fcdbSDavid van Moolenbroek void 3729733fcdbSDavid van Moolenbroek doboottypemenu(void) 3739733fcdbSDavid van Moolenbroek { 374*84d9c625SLionel Sambuc #if !defined(__minix) 375*84d9c625SLionel Sambuc int choice; 376*84d9c625SLionel Sambuc char input[80]; 377*84d9c625SLionel Sambuc 378*84d9c625SLionel Sambuc printf("\n"); 379*84d9c625SLionel Sambuc /* Display menu */ 380*84d9c625SLionel Sambuc if (bootconf.menuformat == MENUFORMAT_LETTER) { 381*84d9c625SLionel Sambuc for (choice = 0; choice < bootconf.nummenu; choice++) 382*84d9c625SLionel Sambuc printf(" %c. %s\n", choice + 'A', 383*84d9c625SLionel Sambuc bootconf.desc[choice]); 384*84d9c625SLionel Sambuc } else { 385*84d9c625SLionel Sambuc /* Can't use %2d format string with libsa */ 386*84d9c625SLionel Sambuc for (choice = 0; choice < bootconf.nummenu; choice++) 387*84d9c625SLionel Sambuc printf(" %s%d. %s\n", 388*84d9c625SLionel Sambuc (choice < 9) ? " " : "", 389*84d9c625SLionel Sambuc choice + 1, 390*84d9c625SLionel Sambuc bootconf.desc[choice]); 391*84d9c625SLionel Sambuc } 392*84d9c625SLionel Sambuc #else 3939733fcdbSDavid van Moolenbroek int choice, editing; 3949733fcdbSDavid van Moolenbroek char input[256], *ic, *oc; 395*84d9c625SLionel Sambuc #endif /* !defined(__minix) */ 396*84d9c625SLionel Sambuc #if defined(__minix) 3979733fcdbSDavid van Moolenbroek showmenu(); 398*84d9c625SLionel Sambuc #endif /* defined(__minix) */ 39958a2b000SEvgeniy Ivanov choice = -1; 400*84d9c625SLionel Sambuc #if defined(__minix) 4019733fcdbSDavid van Moolenbroek editing = 0; 402*84d9c625SLionel Sambuc #endif /* defined(__minix) */ 40358a2b000SEvgeniy Ivanov for (;;) { 40458a2b000SEvgeniy Ivanov input[0] = '\0'; 40558a2b000SEvgeniy Ivanov 40658a2b000SEvgeniy Ivanov if (bootconf.timeout < 0) { 40758a2b000SEvgeniy Ivanov if (bootconf.menuformat == MENUFORMAT_LETTER) 408*84d9c625SLionel Sambuc #if !defined(__minix) 409*84d9c625SLionel Sambuc printf("\nOption: [%c]:", 410*84d9c625SLionel Sambuc #else 4119733fcdbSDavid van Moolenbroek printf("\nOption%s: [%c]:", 4129733fcdbSDavid van Moolenbroek editing ? " (edit)" : "", 413*84d9c625SLionel Sambuc #endif /* !defined(__minix) */ 41458a2b000SEvgeniy Ivanov bootconf.def + 'A'); 41558a2b000SEvgeniy Ivanov else 416*84d9c625SLionel Sambuc #if !defined(__minix) 417*84d9c625SLionel Sambuc printf("\nOption: [%d]:", 418*84d9c625SLionel Sambuc #else 4199733fcdbSDavid van Moolenbroek printf("\nOption%s: [%d]:", 4209733fcdbSDavid van Moolenbroek editing ? " (edit)" : "", 421*84d9c625SLionel Sambuc #endif /* !defined(__minix) */ 42258a2b000SEvgeniy Ivanov bootconf.def + 1); 42358a2b000SEvgeniy Ivanov 424*84d9c625SLionel Sambuc #if !defined(__minix) 425*84d9c625SLionel Sambuc gets(input); 426*84d9c625SLionel Sambuc #else 4279733fcdbSDavid van Moolenbroek editline(input, sizeof(input), NULL); 428*84d9c625SLionel Sambuc #endif /* !defined(__minix) */ 42958a2b000SEvgeniy Ivanov choice = getchoicefrominput(input, bootconf.def); 43058a2b000SEvgeniy Ivanov } else if (bootconf.timeout == 0) 43158a2b000SEvgeniy Ivanov choice = bootconf.def; 43258a2b000SEvgeniy Ivanov else { 43358a2b000SEvgeniy Ivanov printf("\nChoose an option; RETURN for default; " 43458a2b000SEvgeniy Ivanov "SPACE to stop countdown.\n"); 43558a2b000SEvgeniy Ivanov if (bootconf.menuformat == MENUFORMAT_LETTER) 43658a2b000SEvgeniy Ivanov printf("Option %c will be chosen in ", 43758a2b000SEvgeniy Ivanov bootconf.def + 'A'); 43858a2b000SEvgeniy Ivanov else 43958a2b000SEvgeniy Ivanov printf("Option %d will be chosen in ", 44058a2b000SEvgeniy Ivanov bootconf.def + 1); 44158a2b000SEvgeniy Ivanov input[0] = awaitkey(bootconf.timeout, 1); 44258a2b000SEvgeniy Ivanov input[1] = '\0'; 44358a2b000SEvgeniy Ivanov choice = getchoicefrominput(input, bootconf.def); 44458a2b000SEvgeniy Ivanov /* If invalid key pressed, drop to menu */ 44558a2b000SEvgeniy Ivanov if (choice == -1) 44658a2b000SEvgeniy Ivanov bootconf.timeout = -1; 44758a2b000SEvgeniy Ivanov } 44858a2b000SEvgeniy Ivanov if (choice < 0) 44958a2b000SEvgeniy Ivanov continue; 450*84d9c625SLionel Sambuc #if !defined(__minix) 451*84d9c625SLionel Sambuc if (!strcmp(bootconf.command[choice], "prompt") && 452*84d9c625SLionel Sambuc ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 || 453*84d9c625SLionel Sambuc check_password((char *)boot_params.bp_password))) { 454*84d9c625SLionel Sambuc printf("type \"?\" or \"help\" for help.\n"); 455*84d9c625SLionel Sambuc bootmenu(); /* does not return */ 456*84d9c625SLionel Sambuc } else { 457*84d9c625SLionel Sambuc docommandchoice(choice); 458*84d9c625SLionel Sambuc } 459*84d9c625SLionel Sambuc #else 4609733fcdbSDavid van Moolenbroek ic = bootconf.command[choice]; 4619733fcdbSDavid van Moolenbroek if (editing) { 4629733fcdbSDavid van Moolenbroek printf("> "); 4639733fcdbSDavid van Moolenbroek editline(input, sizeof(input), ic); 4649733fcdbSDavid van Moolenbroek ic = input; 4659733fcdbSDavid van Moolenbroek } 4669733fcdbSDavid van Moolenbroek if (!strcmp(ic, "edit") && 46758a2b000SEvgeniy Ivanov ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 || 46858a2b000SEvgeniy Ivanov check_password((char *)boot_params.bp_password))) { 4699733fcdbSDavid van Moolenbroek editing = 1; 4709733fcdbSDavid van Moolenbroek bootconf.timeout = -1; 4719733fcdbSDavid van Moolenbroek } else if (!strcmp(ic, "prompt") && 4729733fcdbSDavid van Moolenbroek ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 || 4739733fcdbSDavid van Moolenbroek check_password((char *)boot_params.bp_password))) { 4749733fcdbSDavid van Moolenbroek printf("type \"?\" or \"help\" for help, " 4759733fcdbSDavid van Moolenbroek "or \"menu\" to return to the menu.\n"); 4769733fcdbSDavid van Moolenbroek prompt(1); 4779733fcdbSDavid van Moolenbroek showmenu(); 4789733fcdbSDavid van Moolenbroek editing = 0; 4799733fcdbSDavid van Moolenbroek bootconf.timeout = -1; 48058a2b000SEvgeniy Ivanov } else { 48158a2b000SEvgeniy Ivanov /* Split command string at ; into separate commands */ 48258a2b000SEvgeniy Ivanov do { 4839733fcdbSDavid van Moolenbroek /* 4849733fcdbSDavid van Moolenbroek * This must support inline editing, since ic 4859733fcdbSDavid van Moolenbroek * may also point to input. 4869733fcdbSDavid van Moolenbroek */ 48758a2b000SEvgeniy Ivanov oc = input; 48858a2b000SEvgeniy Ivanov /* Look for ; separator */ 48958a2b000SEvgeniy Ivanov for (; *ic && *ic != COMMAND_SEPARATOR; ic++) 49058a2b000SEvgeniy Ivanov *oc++ = *ic; 4919733fcdbSDavid van Moolenbroek if (*ic == COMMAND_SEPARATOR) 4929733fcdbSDavid van Moolenbroek ic++; 4939733fcdbSDavid van Moolenbroek if (oc == input) 49458a2b000SEvgeniy Ivanov continue; 49558a2b000SEvgeniy Ivanov /* Strip out any trailing spaces */ 49658a2b000SEvgeniy Ivanov oc--; 49758a2b000SEvgeniy Ivanov for (; *oc == ' ' && oc > input; oc--); 49858a2b000SEvgeniy Ivanov *++oc = '\0'; 49958a2b000SEvgeniy Ivanov /* Stop silly command strings like ;;; */ 50058a2b000SEvgeniy Ivanov if (*input != '\0') 50158a2b000SEvgeniy Ivanov docommand(input); 50258a2b000SEvgeniy Ivanov /* Skip leading spaces */ 50358a2b000SEvgeniy Ivanov for (; *ic == ' '; ic++); 50458a2b000SEvgeniy Ivanov } while (*ic); 50558a2b000SEvgeniy Ivanov } 506*84d9c625SLionel Sambuc #endif /* !defined(__minix) */ 50758a2b000SEvgeniy Ivanov 50858a2b000SEvgeniy Ivanov } 50958a2b000SEvgeniy Ivanov } 51058a2b000SEvgeniy Ivanov 51158a2b000SEvgeniy Ivanov #endif /* !SMALL */ 512