138465Smsmith/*- 238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 338465Smsmith * All rights reserved. 438465Smsmith * 538465Smsmith * Redistribution and use in source and binary forms, with or without 638465Smsmith * modification, are permitted provided that the following conditions 738465Smsmith * are met: 838465Smsmith * 1. Redistributions of source code must retain the above copyright 938465Smsmith * notice, this list of conditions and the following disclaimer. 1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138465Smsmith * notice, this list of conditions and the following disclaimer in the 1238465Smsmith * documentation and/or other materials provided with the distribution. 1338465Smsmith * 1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738465Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438465Smsmith * SUCH DAMAGE. 2538465Smsmith */ 26119483Sobrien 27119483Sobrien#include <sys/cdefs.h> 28119483Sobrien__FBSDID("$FreeBSD$"); 29119483Sobrien 3038465Smsmith/* 3138716Smsmith * Simple commandline interpreter, toplevel and misc. 3238465Smsmith * 3340015Smsmith * XXX may be obsoleted by BootFORTH or some other, better, interpreter. 3438465Smsmith */ 3538465Smsmith 3638465Smsmith#include <stand.h> 3738465Smsmith#include <string.h> 3838465Smsmith#include "bootstrap.h" 3938465Smsmith 4042000Sabial#ifdef BOOT_FORTH 4142000Sabial#include "ficl.h" 4251786Sdcs#define RETURN(x) stackPushINT(bf_vm->pStack,!x); return(x) 4342000Sabial 4442000Sabialextern FICL_VM *bf_vm; 4542000Sabial#else 4642000Sabial#define RETURN(x) return(x) 4742000Sabial#endif 4842000Sabial 4938465Smsmith#define MAXARGS 20 /* maximum number of arguments allowed */ 5038465Smsmith 5138465Smsmithstatic void prompt(void); 5238465Smsmith 5364220Sjhb#ifndef BOOT_FORTH 5464220Sjhbstatic int perform(int argc, char *argv[]); 5564220Sjhb 5638465Smsmith/* 5764220Sjhb * Perform the command 5864220Sjhb */ 5964220Sjhbint 6064220Sjhbperform(int argc, char *argv[]) 6164220Sjhb{ 6264220Sjhb int result; 6364220Sjhb struct bootblk_command **cmdp; 6464220Sjhb bootblk_cmd_t *cmd; 6564220Sjhb 6664220Sjhb if (argc < 1) 6764220Sjhb return(CMD_OK); 6864220Sjhb 6964220Sjhb /* set return defaults; a successful command will override these */ 7064220Sjhb command_errmsg = command_errbuf; 7164220Sjhb strcpy(command_errbuf, "no error message"); 7264220Sjhb cmd = NULL; 7364220Sjhb result = CMD_ERROR; 7464220Sjhb 7564220Sjhb /* search the command set for the command */ 7664220Sjhb SET_FOREACH(cmdp, Xcommand_set) { 7764220Sjhb if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name)) 7864220Sjhb cmd = (*cmdp)->c_fn; 7964220Sjhb } 8064220Sjhb if (cmd != NULL) { 8164220Sjhb result = (cmd)(argc, argv); 8264220Sjhb } else { 8364220Sjhb command_errmsg = "unknown command"; 8464220Sjhb } 8564220Sjhb RETURN(result); 8664220Sjhb} 8764220Sjhb#endif /* ! BOOT_FORTH */ 8864220Sjhb 8964220Sjhb/* 9038465Smsmith * Interactive mode 9138465Smsmith */ 9238465Smsmithvoid 9338465Smsmithinteract(void) 9438465Smsmith{ 95185132Sluigi static char input[256]; /* big enough? */ 9642289Speter#ifndef BOOT_FORTH 9738465Smsmith int argc; 9838465Smsmith char **argv; 9942289Speter#endif 10038465Smsmith 10140875Smsmith#ifdef BOOT_FORTH 10240875Smsmith bf_init(); 10340875Smsmith#endif 10440875Smsmith 10538764Smsmith /* 10638764Smsmith * Read our default configuration 10738764Smsmith */ 108221601Savg if (include("/boot/loader.rc") != CMD_OK) 10943077Smsmith include("/boot/boot.conf"); 11038764Smsmith printf("\n"); 11138764Smsmith /* 11240015Smsmith * Before interacting, we might want to autoboot. 11338764Smsmith */ 11440015Smsmith autoboot_maybe(); 11540015Smsmith 11638764Smsmith /* 11738764Smsmith * Not autobooting, go manual 11838764Smsmith */ 11938764Smsmith printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 12045867Sjoerg if (getenv("prompt") == NULL) 12153786Sdcs setenv("prompt", "${interpret}", 1); 12252748Sdcs if (getenv("interpret") == NULL) 12386608Sobrien setenv("interpret", "OK", 1); 12438764Smsmith 12538764Smsmith 12638465Smsmith for (;;) { 12738465Smsmith input[0] = '\0'; 12838465Smsmith prompt(); 12938465Smsmith ngets(input, sizeof(input)); 13040875Smsmith#ifdef BOOT_FORTH 13161693Sdcs bf_vm->sourceID.i = 0; 13240875Smsmith bf_run(input); 13340875Smsmith#else 13438716Smsmith if (!parse(&argc, &argv, input)) { 13538716Smsmith if (perform(argc, argv)) 13638465Smsmith printf("%s: %s\n", argv[0], command_errmsg); 13738716Smsmith free(argv); 13838716Smsmith } else { 13938716Smsmith printf("parse error\n"); 14038716Smsmith } 14140875Smsmith#endif 14238465Smsmith } 14338465Smsmith} 14438465Smsmith 14538465Smsmith/* 14640015Smsmith * Read commands from a file, then execute them. 14740015Smsmith * 14840015Smsmith * We store the commands in memory and close the source file so that the media 14940015Smsmith * holding it can safely go away while we are executing. 15040015Smsmith * 15140015Smsmith * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 15240015Smsmith * that the script won't stop if they fail). 15338465Smsmith */ 15443077SmsmithCOMMAND_SET(include, "include", "read commands from a file", command_include); 15538465Smsmith 15638465Smsmithstatic int 15743077Smsmithcommand_include(int argc, char *argv[]) 15838465Smsmith{ 15938465Smsmith int i; 16042682Sabial int res; 16144210Sdcs char **argvbuf; 16238465Smsmith 16344210Sdcs /* 16444210Sdcs * Since argv is static, we need to save it here. 16544210Sdcs */ 16664187Sjhb argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); 16744210Sdcs for (i = 0; i < argc; i++) 16844210Sdcs argvbuf[i] = strdup(argv[i]); 16944210Sdcs 17042682Sabial res=CMD_OK; 17142682Sabial for (i = 1; (i < argc) && (res == CMD_OK); i++) 17244210Sdcs res = include(argvbuf[i]); 17344210Sdcs 17444210Sdcs for (i = 0; i < argc; i++) 17544210Sdcs free(argvbuf[i]); 17644210Sdcs free(argvbuf); 17744210Sdcs 17842682Sabial return(res); 17938465Smsmith} 18038465Smsmith 181185132Sluigi/* 182185132Sluigi * Header prepended to each line. The text immediately follows the header. 183185132Sluigi * We try to make this short in order to save memory -- the loader has 184185132Sluigi * limited memory available, and some of the forth files are very long. 185185132Sluigi */ 18643077Smsmithstruct includeline 18740015Smsmith{ 188185132Sluigi struct includeline *next; 189185132Sluigi#ifndef BOOT_FORTH 19040015Smsmith int flags; 19140015Smsmith int line; 19240015Smsmith#define SL_QUIET (1<<0) 19340015Smsmith#define SL_IGNOREERR (1<<1) 194185132Sluigi#endif 195185132Sluigi char text[0]; 19640015Smsmith}; 19740015Smsmith 19842682Sabialint 19964187Sjhbinclude(const char *filename) 20038465Smsmith{ 20143077Smsmith struct includeline *script, *se, *sp; 20240015Smsmith char input[256]; /* big enough? */ 20343614Sdcs#ifdef BOOT_FORTH 20443614Sdcs int res; 20543614Sdcs char *cp; 20661693Sdcs int prevsrcid, fd, line; 20743614Sdcs#else 20842682Sabial int argc,res; 20940015Smsmith char **argv, *cp; 21040015Smsmith int fd, flags, line; 21143614Sdcs#endif 21238465Smsmith 21338465Smsmith if (((fd = open(filename, O_RDONLY)) == -1)) { 214242084Smav sprintf(command_errbuf,"can't open '%s': %s", filename, strerror(errno)); 21542682Sabial return(CMD_ERROR); 21640015Smsmith } 21738465Smsmith 21840015Smsmith /* 21940015Smsmith * Read the script into memory. 22040015Smsmith */ 22140015Smsmith script = se = NULL; 22240015Smsmith line = 0; 22340015Smsmith 22444427Sdcs while (fgetstr(input, sizeof(input), fd) >= 0) { 22540015Smsmith line++; 22643614Sdcs#ifdef BOOT_FORTH 22743614Sdcs cp = input; 22843614Sdcs#else 22940015Smsmith flags = 0; 23040015Smsmith /* Discard comments */ 23165683Sdcs if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0) 23240015Smsmith continue; 23340015Smsmith cp = input; 23440015Smsmith /* Echo? */ 23540015Smsmith if (input[0] == '@') { 23640015Smsmith cp++; 23740015Smsmith flags |= SL_QUIET; 23840015Smsmith } 23940015Smsmith /* Error OK? */ 24040015Smsmith if (input[0] == '-') { 24140015Smsmith cp++; 24240015Smsmith flags |= SL_IGNOREERR; 24340015Smsmith } 24443614Sdcs#endif 24540015Smsmith /* Allocate script line structure and copy line, flags */ 246185132Sluigi if (*cp == '\0') 247185132Sluigi continue; /* ignore empty line, save memory */ 24843077Smsmith sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 249218974Sbrucec /* On malloc failure (it happens!), free as much as possible and exit */ 250218974Sbrucec if (sp == NULL) { 251218974Sbrucec while (script != NULL) { 252218974Sbrucec se = script; 253218974Sbrucec script = script->next; 254218974Sbrucec free(se); 255218974Sbrucec } 256218974Sbrucec sprintf(command_errbuf, "file '%s' line %d: memory allocation " 257242084Smav "failure - aborting", filename, line); 258218974Sbrucec return (CMD_ERROR); 259218974Sbrucec } 26040106Smsmith strcpy(sp->text, cp); 26143614Sdcs#ifndef BOOT_FORTH 26240015Smsmith sp->flags = flags; 263185132Sluigi sp->line = line; 26443614Sdcs#endif 26540015Smsmith sp->next = NULL; 26640015Smsmith 26740015Smsmith if (script == NULL) { 26840015Smsmith script = sp; 26940015Smsmith } else { 27040015Smsmith se->next = sp; 27140015Smsmith } 27240015Smsmith se = sp; 27340015Smsmith } 27440015Smsmith close(fd); 27540015Smsmith 27640015Smsmith /* 27740015Smsmith * Execute the script 27840015Smsmith */ 27943614Sdcs#ifndef BOOT_FORTH 28040015Smsmith argv = NULL; 28161693Sdcs#else 28261693Sdcs prevsrcid = bf_vm->sourceID.i; 28361693Sdcs bf_vm->sourceID.i = fd; 28443614Sdcs#endif 28542682Sabial res = CMD_OK; 28640015Smsmith for (sp = script; sp != NULL; sp = sp->next) { 28740015Smsmith 28843614Sdcs#ifdef BOOT_FORTH 28943614Sdcs res = bf_run(sp->text); 29043614Sdcs if (res != VM_OUTOFTEXT) { 29144210Sdcs sprintf(command_errbuf, "Error while including %s, in the line:\n%s", filename, sp->text); 29243614Sdcs res = CMD_ERROR; 29343614Sdcs break; 29443614Sdcs } else 29543614Sdcs res = CMD_OK; 29643614Sdcs#else 29740015Smsmith /* print if not being quiet */ 29840015Smsmith if (!(sp->flags & SL_QUIET)) { 29940015Smsmith prompt(); 30040015Smsmith printf("%s\n", sp->text); 30140015Smsmith } 30238465Smsmith 30340015Smsmith /* Parse the command */ 30440015Smsmith if (!parse(&argc, &argv, sp->text)) { 30540106Smsmith if ((argc > 0) && (perform(argc, argv) != 0)) { 30640015Smsmith /* normal command */ 30740015Smsmith printf("%s: %s\n", argv[0], command_errmsg); 30842682Sabial if (!(sp->flags & SL_IGNOREERR)) { 30942682Sabial res=CMD_ERROR; 31040015Smsmith break; 31142682Sabial } 31238716Smsmith } 31340015Smsmith free(argv); 31440015Smsmith argv = NULL; 31540015Smsmith } else { 31640015Smsmith printf("%s line %d: parse error\n", filename, sp->line); 31742682Sabial res=CMD_ERROR; 31840015Smsmith break; 31938465Smsmith } 32043614Sdcs#endif 32138465Smsmith } 32243614Sdcs#ifndef BOOT_FORTH 32340015Smsmith if (argv != NULL) 32440015Smsmith free(argv); 32561693Sdcs#else 32661693Sdcs bf_vm->sourceID.i = prevsrcid; 32743614Sdcs#endif 32840015Smsmith while(script != NULL) { 32940015Smsmith se = script; 33040015Smsmith script = script->next; 33140015Smsmith free(se); 33240015Smsmith } 33342682Sabial return(res); 33438465Smsmith} 33538465Smsmith 33638465Smsmith/* 33738764Smsmith * Emit the current prompt; use the same syntax as the parser 33838764Smsmith * for embedding environment variables. 33938465Smsmith */ 34038465Smsmithstatic void 34138465Smsmithprompt(void) 34238465Smsmith{ 34344570Sdcs char *pr, *p, *cp, *ev; 34438465Smsmith 34538764Smsmith if ((cp = getenv("prompt")) == NULL) 34638764Smsmith cp = ">"; 34744570Sdcs pr = p = strdup(cp); 34838465Smsmith 34938465Smsmith while (*p != 0) { 35038764Smsmith if ((*p == '$') && (*(p+1) == '{')) { 35138764Smsmith for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 35238465Smsmith ; 35338465Smsmith *cp = 0; 35438764Smsmith ev = getenv(p + 2); 35538465Smsmith 35638764Smsmith if (ev != NULL) 35762874Skris printf("%s", ev); 35838764Smsmith p = cp + 1; 35938764Smsmith continue; 36038465Smsmith } 36138465Smsmith putchar(*p++); 36238465Smsmith } 36338465Smsmith putchar(' '); 36444570Sdcs free(pr); 36538465Smsmith} 366