1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3ca987d46SWarner Losh * All rights reserved. 4ca987d46SWarner Losh * 5ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without 6ca987d46SWarner Losh * modification, are permitted provided that the following conditions 7ca987d46SWarner Losh * are met: 8ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright 9ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer. 10ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 11ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the 12ca987d46SWarner Losh * documentation and/or other materials provided with the distribution. 13ca987d46SWarner Losh * 14ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ca987d46SWarner Losh * SUCH DAMAGE. 25ca987d46SWarner Losh */ 26ba25195eSWarner Losh 27ca987d46SWarner Losh #include <sys/cdefs.h> 28ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 29ca987d46SWarner Losh 30ca987d46SWarner Losh /* 31ca987d46SWarner Losh * Simple commandline interpreter, toplevel and misc. 32ca987d46SWarner Losh * 33ca987d46SWarner Losh * XXX may be obsoleted by BootFORTH or some other, better, interpreter. 34ca987d46SWarner Losh */ 35ca987d46SWarner Losh 36ca987d46SWarner Losh #include <stand.h> 37ca987d46SWarner Losh #include <string.h> 38ca987d46SWarner Losh #include "bootstrap.h" 39ca987d46SWarner Losh 40ba25195eSWarner Losh #ifdef BOOT_FORTH 41ba25195eSWarner Losh #include "ficl.h" 42ba25195eSWarner Losh extern FICL_VM *bf_vm; 43ba25195eSWarner Losh #endif 44ca987d46SWarner Losh 45ca987d46SWarner Losh #define MAXARGS 20 /* maximum number of arguments allowed */ 46ca987d46SWarner Losh 47ba25195eSWarner Losh static void prompt(void); 48ca987d46SWarner Losh 49ba25195eSWarner Losh #ifndef BOOT_FORTH 50ca987d46SWarner Losh /* 51fb5af39aSWarner Losh * Perform the command 52ca987d46SWarner Losh */ 530ff3f28bSWarner Losh static int 54fb5af39aSWarner Losh perform(int argc, char *argv[]) 55ca987d46SWarner Losh { 56fb5af39aSWarner Losh int result; 57fb5af39aSWarner Losh struct bootblk_command **cmdp; 58fb5af39aSWarner Losh bootblk_cmd_t *cmd; 59ca987d46SWarner Losh 60fb5af39aSWarner Losh if (argc < 1) 61fb5af39aSWarner Losh return(CMD_OK); 62ca987d46SWarner Losh 63fb5af39aSWarner Losh /* set return defaults; a successful command will override these */ 64fb5af39aSWarner Losh command_errmsg = command_errbuf; 65fb5af39aSWarner Losh strcpy(command_errbuf, "no error message"); 66fb5af39aSWarner Losh cmd = NULL; 67fb5af39aSWarner Losh result = CMD_ERROR; 68ca987d46SWarner Losh 69fb5af39aSWarner Losh /* search the command set for the command */ 70fb5af39aSWarner Losh SET_FOREACH(cmdp, Xcommand_set) { 71fb5af39aSWarner Losh if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name)) 72fb5af39aSWarner Losh cmd = (*cmdp)->c_fn; 73ca987d46SWarner Losh } 74fb5af39aSWarner Losh if (cmd != NULL) { 75fb5af39aSWarner Losh result = (cmd)(argc, argv); 76ca987d46SWarner Losh } else { 77fb5af39aSWarner Losh command_errmsg = "unknown command"; 78ca987d46SWarner Losh } 790ff3f28bSWarner Losh return(result); 80ba25195eSWarner Losh } 81ba25195eSWarner Losh #endif /* ! BOOT_FORTH */ 82ba25195eSWarner Losh 83ba25195eSWarner Losh /* 84ba25195eSWarner Losh * Interactive mode 85ba25195eSWarner Losh */ 86ba25195eSWarner Losh void 87*6bc86037SWarner Losh interact(void) 88ba25195eSWarner Losh { 89ba25195eSWarner Losh static char input[256]; /* big enough? */ 90ba25195eSWarner Losh #ifndef BOOT_FORTH 91ba25195eSWarner Losh int argc; 92ba25195eSWarner Losh char **argv; 93ba25195eSWarner Losh #endif 94ba25195eSWarner Losh 95ba25195eSWarner Losh #ifdef BOOT_FORTH 96*6bc86037SWarner Losh bf_init(); 97ba25195eSWarner Losh #endif 98ba25195eSWarner Losh 99ba25195eSWarner Losh /* Read our default configuration. */ 100ba25195eSWarner Losh include("/boot/loader.rc"); 101ba25195eSWarner Losh 102ba25195eSWarner Losh printf("\n"); 103ba25195eSWarner Losh 104ba25195eSWarner Losh /* 105ba25195eSWarner Losh * Before interacting, we might want to autoboot. 106ba25195eSWarner Losh */ 107ba25195eSWarner Losh autoboot_maybe(); 108ba25195eSWarner Losh 109ba25195eSWarner Losh /* 110ba25195eSWarner Losh * Not autobooting, go manual 111ba25195eSWarner Losh */ 112ba25195eSWarner Losh printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 113ba25195eSWarner Losh if (getenv("prompt") == NULL) 114ba25195eSWarner Losh setenv("prompt", "${interpret}", 1); 115ba25195eSWarner Losh if (getenv("interpret") == NULL) 116ba25195eSWarner Losh setenv("interpret", "OK", 1); 117ba25195eSWarner Losh 118ba25195eSWarner Losh 119ba25195eSWarner Losh for (;;) { 120ba25195eSWarner Losh input[0] = '\0'; 121ba25195eSWarner Losh prompt(); 122ba25195eSWarner Losh ngets(input, sizeof(input)); 123ba25195eSWarner Losh #ifdef BOOT_FORTH 124ba25195eSWarner Losh bf_vm->sourceID.i = 0; 125ba25195eSWarner Losh bf_run(input); 126ba25195eSWarner Losh #else 127ba25195eSWarner Losh if (!parse(&argc, &argv, input)) { 128ba25195eSWarner Losh if (perform(argc, argv)) 129ba25195eSWarner Losh printf("%s: %s\n", argv[0], command_errmsg); 130ba25195eSWarner Losh free(argv); 131ba25195eSWarner Losh } else { 132ba25195eSWarner Losh printf("parse error\n"); 133ba25195eSWarner Losh } 134ba25195eSWarner Losh #endif 135ba25195eSWarner Losh } 136ba25195eSWarner Losh } 137ba25195eSWarner Losh 138ba25195eSWarner Losh /* 139ba25195eSWarner Losh * Read commands from a file, then execute them. 140ba25195eSWarner Losh * 141ba25195eSWarner Losh * We store the commands in memory and close the source file so that the media 142ba25195eSWarner Losh * holding it can safely go away while we are executing. 143ba25195eSWarner Losh * 144ba25195eSWarner Losh * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 145ba25195eSWarner Losh * that the script won't stop if they fail). 146ba25195eSWarner Losh */ 147ba25195eSWarner Losh COMMAND_SET(include, "include", "read commands from a file", command_include); 148ba25195eSWarner Losh 149ba25195eSWarner Losh static int 150ba25195eSWarner Losh command_include(int argc, char *argv[]) 151ba25195eSWarner Losh { 152ba25195eSWarner Losh int i; 153ba25195eSWarner Losh int res; 154ba25195eSWarner Losh char **argvbuf; 155ba25195eSWarner Losh 156ba25195eSWarner Losh /* 157ba25195eSWarner Losh * Since argv is static, we need to save it here. 158ba25195eSWarner Losh */ 159ba25195eSWarner Losh argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); 160ba25195eSWarner Losh for (i = 0; i < argc; i++) 161ba25195eSWarner Losh argvbuf[i] = strdup(argv[i]); 162ba25195eSWarner Losh 163ba25195eSWarner Losh res=CMD_OK; 164ba25195eSWarner Losh for (i = 1; (i < argc) && (res == CMD_OK); i++) 165ba25195eSWarner Losh res = include(argvbuf[i]); 166ba25195eSWarner Losh 167ba25195eSWarner Losh for (i = 0; i < argc; i++) 168ba25195eSWarner Losh free(argvbuf[i]); 169ba25195eSWarner Losh free(argvbuf); 170ba25195eSWarner Losh 171ba25195eSWarner Losh return(res); 172ba25195eSWarner Losh } 173ba25195eSWarner Losh 174ba25195eSWarner Losh /* 175ba25195eSWarner Losh * Header prepended to each line. The text immediately follows the header. 176ba25195eSWarner Losh * We try to make this short in order to save memory -- the loader has 177ba25195eSWarner Losh * limited memory available, and some of the forth files are very long. 178ba25195eSWarner Losh */ 179ba25195eSWarner Losh struct includeline 180ba25195eSWarner Losh { 181ba25195eSWarner Losh struct includeline *next; 182ba25195eSWarner Losh #ifndef BOOT_FORTH 183ba25195eSWarner Losh int flags; 184ba25195eSWarner Losh int line; 185ba25195eSWarner Losh #define SL_QUIET (1<<0) 186ba25195eSWarner Losh #define SL_IGNOREERR (1<<1) 187ba25195eSWarner Losh #endif 188ba25195eSWarner Losh char text[0]; 189ba25195eSWarner Losh }; 190ba25195eSWarner Losh 191ba25195eSWarner Losh int 192ba25195eSWarner Losh include(const char *filename) 193ba25195eSWarner Losh { 194ba25195eSWarner Losh struct includeline *script, *se, *sp; 195ba25195eSWarner Losh char input[256]; /* big enough? */ 196ba25195eSWarner Losh #ifdef BOOT_FORTH 197ba25195eSWarner Losh int res; 198ba25195eSWarner Losh char *cp; 199ba25195eSWarner Losh int prevsrcid, fd, line; 200ba25195eSWarner Losh #else 201ba25195eSWarner Losh int argc,res; 202ba25195eSWarner Losh char **argv, *cp; 203ba25195eSWarner Losh int fd, flags, line; 204ba25195eSWarner Losh #endif 205ba25195eSWarner Losh 206ba25195eSWarner Losh if (((fd = open(filename, O_RDONLY)) == -1)) { 207ba25195eSWarner Losh snprintf(command_errbuf, sizeof(command_errbuf), 208ba25195eSWarner Losh "can't open '%s': %s", filename, strerror(errno)); 209ba25195eSWarner Losh return(CMD_ERROR); 210ba25195eSWarner Losh } 211ba25195eSWarner Losh 212ba25195eSWarner Losh /* 213ba25195eSWarner Losh * Read the script into memory. 214ba25195eSWarner Losh */ 215ba25195eSWarner Losh script = se = NULL; 216ba25195eSWarner Losh line = 0; 217ba25195eSWarner Losh 218ba25195eSWarner Losh while (fgetstr(input, sizeof(input), fd) >= 0) { 219ba25195eSWarner Losh line++; 220ba25195eSWarner Losh #ifdef BOOT_FORTH 221ba25195eSWarner Losh cp = input; 222ba25195eSWarner Losh #else 223ba25195eSWarner Losh flags = 0; 224ba25195eSWarner Losh /* Discard comments */ 225ba25195eSWarner Losh if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0) 226ba25195eSWarner Losh continue; 227ba25195eSWarner Losh cp = input; 228ba25195eSWarner Losh /* Echo? */ 229ba25195eSWarner Losh if (input[0] == '@') { 230ba25195eSWarner Losh cp++; 231ba25195eSWarner Losh flags |= SL_QUIET; 232ba25195eSWarner Losh } 233ba25195eSWarner Losh /* Error OK? */ 234ba25195eSWarner Losh if (input[0] == '-') { 235ba25195eSWarner Losh cp++; 236ba25195eSWarner Losh flags |= SL_IGNOREERR; 237ba25195eSWarner Losh } 238ba25195eSWarner Losh #endif 239ba25195eSWarner Losh /* Allocate script line structure and copy line, flags */ 240ba25195eSWarner Losh if (*cp == '\0') 241ba25195eSWarner Losh continue; /* ignore empty line, save memory */ 242ba25195eSWarner Losh sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 243ba25195eSWarner Losh /* On malloc failure (it happens!), free as much as possible and exit */ 244ba25195eSWarner Losh if (sp == NULL) { 245ba25195eSWarner Losh while (script != NULL) { 246ba25195eSWarner Losh se = script; 247ba25195eSWarner Losh script = script->next; 248ba25195eSWarner Losh free(se); 249ba25195eSWarner Losh } 250ba25195eSWarner Losh snprintf(command_errbuf, sizeof(command_errbuf), 251ba25195eSWarner Losh "file '%s' line %d: memory allocation failure - aborting", 252ba25195eSWarner Losh filename, line); 253ba25195eSWarner Losh return (CMD_ERROR); 254ba25195eSWarner Losh } 255ba25195eSWarner Losh strcpy(sp->text, cp); 256ba25195eSWarner Losh #ifndef BOOT_FORTH 257ba25195eSWarner Losh sp->flags = flags; 258ba25195eSWarner Losh sp->line = line; 259ba25195eSWarner Losh #endif 260ba25195eSWarner Losh sp->next = NULL; 261ba25195eSWarner Losh 262ba25195eSWarner Losh if (script == NULL) { 263ba25195eSWarner Losh script = sp; 264ba25195eSWarner Losh } else { 265ba25195eSWarner Losh se->next = sp; 266ba25195eSWarner Losh } 267ba25195eSWarner Losh se = sp; 268ba25195eSWarner Losh } 269ba25195eSWarner Losh close(fd); 270ba25195eSWarner Losh 271ba25195eSWarner Losh /* 272ba25195eSWarner Losh * Execute the script 273ba25195eSWarner Losh */ 274ba25195eSWarner Losh #ifndef BOOT_FORTH 275ba25195eSWarner Losh argv = NULL; 276ba25195eSWarner Losh #else 277ba25195eSWarner Losh prevsrcid = bf_vm->sourceID.i; 278ba25195eSWarner Losh bf_vm->sourceID.i = fd; 279ba25195eSWarner Losh #endif 280ba25195eSWarner Losh res = CMD_OK; 281ba25195eSWarner Losh for (sp = script; sp != NULL; sp = sp->next) { 282ba25195eSWarner Losh 283ba25195eSWarner Losh #ifdef BOOT_FORTH 284ba25195eSWarner Losh res = bf_run(sp->text); 285ba25195eSWarner Losh if (res != VM_OUTOFTEXT) { 286ba25195eSWarner Losh snprintf(command_errbuf, sizeof(command_errbuf), 287ba25195eSWarner Losh "Error while including %s, in the line:\n%s", 288ba25195eSWarner Losh filename, sp->text); 289ba25195eSWarner Losh res = CMD_ERROR; 290ba25195eSWarner Losh break; 291ba25195eSWarner Losh } else 292ba25195eSWarner Losh res = CMD_OK; 293ba25195eSWarner Losh #else 294ba25195eSWarner Losh /* print if not being quiet */ 295ba25195eSWarner Losh if (!(sp->flags & SL_QUIET)) { 296ba25195eSWarner Losh prompt(); 297ba25195eSWarner Losh printf("%s\n", sp->text); 298ba25195eSWarner Losh } 299ba25195eSWarner Losh 300ba25195eSWarner Losh /* Parse the command */ 301ba25195eSWarner Losh if (!parse(&argc, &argv, sp->text)) { 302ba25195eSWarner Losh if ((argc > 0) && (perform(argc, argv) != 0)) { 303ba25195eSWarner Losh /* normal command */ 304ba25195eSWarner Losh printf("%s: %s\n", argv[0], command_errmsg); 305ba25195eSWarner Losh if (!(sp->flags & SL_IGNOREERR)) { 306ba25195eSWarner Losh res=CMD_ERROR; 307ba25195eSWarner Losh break; 308ba25195eSWarner Losh } 309ba25195eSWarner Losh } 310ba25195eSWarner Losh free(argv); 311ba25195eSWarner Losh argv = NULL; 312ba25195eSWarner Losh } else { 313ba25195eSWarner Losh printf("%s line %d: parse error\n", filename, sp->line); 314ba25195eSWarner Losh res=CMD_ERROR; 315ba25195eSWarner Losh break; 316ba25195eSWarner Losh } 317ba25195eSWarner Losh #endif 318ba25195eSWarner Losh } 319ba25195eSWarner Losh #ifndef BOOT_FORTH 320ba25195eSWarner Losh if (argv != NULL) 321ba25195eSWarner Losh free(argv); 322ba25195eSWarner Losh #else 323ba25195eSWarner Losh bf_vm->sourceID.i = prevsrcid; 324ba25195eSWarner Losh #endif 325ba25195eSWarner Losh while(script != NULL) { 326ba25195eSWarner Losh se = script; 327ba25195eSWarner Losh script = script->next; 328ba25195eSWarner Losh free(se); 329ba25195eSWarner Losh } 330ba25195eSWarner Losh return(res); 331ca987d46SWarner Losh } 332ca987d46SWarner Losh 333ca987d46SWarner Losh /* 334ca987d46SWarner Losh * Emit the current prompt; use the same syntax as the parser 335ca987d46SWarner Losh * for embedding environment variables. 336ca987d46SWarner Losh */ 337ba25195eSWarner Losh static void 338ca987d46SWarner Losh prompt(void) 339ca987d46SWarner Losh { 340ca987d46SWarner Losh char *pr, *p, *cp, *ev; 341ca987d46SWarner Losh 342ca987d46SWarner Losh if ((cp = getenv("prompt")) == NULL) 343ca987d46SWarner Losh cp = ">"; 344ca987d46SWarner Losh pr = p = strdup(cp); 345ca987d46SWarner Losh 346ca987d46SWarner Losh while (*p != 0) { 347ca987d46SWarner Losh if ((*p == '$') && (*(p+1) == '{')) { 348ca987d46SWarner Losh for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 349ca987d46SWarner Losh ; 350ca987d46SWarner Losh *cp = 0; 351ca987d46SWarner Losh ev = getenv(p + 2); 352ca987d46SWarner Losh 353ca987d46SWarner Losh if (ev != NULL) 354ca987d46SWarner Losh printf("%s", ev); 355ca987d46SWarner Losh p = cp + 1; 356ca987d46SWarner Losh continue; 357ca987d46SWarner Losh } 358ca987d46SWarner Losh putchar(*p++); 359ca987d46SWarner Losh } 360ca987d46SWarner Losh putchar(' '); 361ca987d46SWarner Losh free(pr); 362ca987d46SWarner Losh } 363