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