interp.c revision 64187
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 * $FreeBSD: head/sys/boot/common/interp.c 64187 2000-08-03 09:14:02Z jhb $ 27 */ 28/* 29 * Simple commandline interpreter, toplevel and misc. 30 * 31 * XXX may be obsoleted by BootFORTH or some other, better, interpreter. 32 */ 33 34#include <stand.h> 35#include <string.h> 36#include "bootstrap.h" 37 38#ifdef BOOT_FORTH 39#include "ficl.h" 40#define RETURN(x) stackPushINT(bf_vm->pStack,!x); return(x) 41 42extern FICL_VM *bf_vm; 43#else 44#define RETURN(x) return(x) 45#endif 46 47#define MAXARGS 20 /* maximum number of arguments allowed */ 48 49static void prompt(void); 50 51/* 52 * Interactive mode 53 */ 54void 55interact(void) 56{ 57 char input[256]; /* big enough? */ 58#ifndef BOOT_FORTH 59 int argc; 60 char **argv; 61#endif 62 63#ifdef BOOT_FORTH 64 bf_init(); 65#endif 66 67 /* 68 * Read our default configuration 69 */ 70 if(include("/boot/loader.rc")!=CMD_OK) 71 include("/boot/boot.conf"); 72 printf("\n"); 73 /* 74 * Before interacting, we might want to autoboot. 75 */ 76 autoboot_maybe(); 77 78 /* 79 * Not autobooting, go manual 80 */ 81 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 82 if (getenv("prompt") == NULL) 83 setenv("prompt", "${interpret}", 1); 84 if (getenv("interpret") == NULL) 85 setenv("interpret", "ok", 1); 86 87 88 for (;;) { 89 input[0] = '\0'; 90 prompt(); 91 ngets(input, sizeof(input)); 92#ifdef BOOT_FORTH 93 bf_vm->sourceID.i = 0; 94 bf_run(input); 95#else 96 if (!parse(&argc, &argv, input)) { 97 if (perform(argc, argv)) 98 printf("%s: %s\n", argv[0], command_errmsg); 99 free(argv); 100 } else { 101 printf("parse error\n"); 102 } 103#endif 104 } 105} 106 107/* 108 * Read commands from a file, then execute them. 109 * 110 * We store the commands in memory and close the source file so that the media 111 * holding it can safely go away while we are executing. 112 * 113 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 114 * that the script won't stop if they fail). 115 */ 116COMMAND_SET(include, "include", "read commands from a file", command_include); 117 118static int 119command_include(int argc, char *argv[]) 120{ 121 int i; 122 int res; 123 char **argvbuf; 124 125 /* 126 * Since argv is static, we need to save it here. 127 */ 128 argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); 129 for (i = 0; i < argc; i++) 130 argvbuf[i] = strdup(argv[i]); 131 132 res=CMD_OK; 133 for (i = 1; (i < argc) && (res == CMD_OK); i++) 134 res = include(argvbuf[i]); 135 136 for (i = 0; i < argc; i++) 137 free(argvbuf[i]); 138 free(argvbuf); 139 140 return(res); 141} 142 143struct includeline 144{ 145 char *text; 146 int flags; 147 int line; 148#define SL_QUIET (1<<0) 149#define SL_IGNOREERR (1<<1) 150 struct includeline *next; 151}; 152 153int 154include(const char *filename) 155{ 156 struct includeline *script, *se, *sp; 157 char input[256]; /* big enough? */ 158#ifdef BOOT_FORTH 159 int res; 160 char *cp; 161 int prevsrcid, fd, line; 162#else 163 int argc,res; 164 char **argv, *cp; 165 int fd, flags, line; 166#endif 167 168 if (((fd = open(filename, O_RDONLY)) == -1)) { 169 sprintf(command_errbuf,"can't open '%s': %s\n", filename, strerror(errno)); 170 return(CMD_ERROR); 171 } 172 173 /* 174 * Read the script into memory. 175 */ 176 script = se = NULL; 177 line = 0; 178 179 while (fgetstr(input, sizeof(input), fd) >= 0) { 180 line++; 181#ifdef BOOT_FORTH 182 cp = input; 183#else 184 flags = 0; 185 /* Discard comments */ 186 if (input[0] == '#') 187 continue; 188 cp = input; 189 /* Echo? */ 190 if (input[0] == '@') { 191 cp++; 192 flags |= SL_QUIET; 193 } 194 /* Error OK? */ 195 if (input[0] == '-') { 196 cp++; 197 flags |= SL_IGNOREERR; 198 } 199#endif 200 /* Allocate script line structure and copy line, flags */ 201 sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 202 sp->text = (char *)sp + sizeof(struct includeline); 203 strcpy(sp->text, cp); 204#ifndef BOOT_FORTH 205 sp->flags = flags; 206#endif 207 sp->line = line; 208 sp->next = NULL; 209 210 if (script == NULL) { 211 script = sp; 212 } else { 213 se->next = sp; 214 } 215 se = sp; 216 } 217 close(fd); 218 219 /* 220 * Execute the script 221 */ 222#ifndef BOOT_FORTH 223 argv = NULL; 224#else 225 prevsrcid = bf_vm->sourceID.i; 226 bf_vm->sourceID.i = fd; 227#endif 228 res = CMD_OK; 229 for (sp = script; sp != NULL; sp = sp->next) { 230 231#ifdef BOOT_FORTH 232 res = bf_run(sp->text); 233 if (res != VM_OUTOFTEXT) { 234 sprintf(command_errbuf, "Error while including %s, in the line:\n%s", filename, sp->text); 235 res = CMD_ERROR; 236 break; 237 } else 238 res = CMD_OK; 239#else 240 /* print if not being quiet */ 241 if (!(sp->flags & SL_QUIET)) { 242 prompt(); 243 printf("%s\n", sp->text); 244 } 245 246 /* Parse the command */ 247 if (!parse(&argc, &argv, sp->text)) { 248 if ((argc > 0) && (perform(argc, argv) != 0)) { 249 /* normal command */ 250 printf("%s: %s\n", argv[0], command_errmsg); 251 if (!(sp->flags & SL_IGNOREERR)) { 252 res=CMD_ERROR; 253 break; 254 } 255 } 256 free(argv); 257 argv = NULL; 258 } else { 259 printf("%s line %d: parse error\n", filename, sp->line); 260 res=CMD_ERROR; 261 break; 262 } 263#endif 264 } 265#ifndef BOOT_FORTH 266 if (argv != NULL) 267 free(argv); 268#else 269 bf_vm->sourceID.i = prevsrcid; 270#endif 271 while(script != NULL) { 272 se = script; 273 script = script->next; 274 free(se); 275 } 276 return(res); 277} 278 279/* 280 * Emit the current prompt; use the same syntax as the parser 281 * for embedding environment variables. 282 */ 283static void 284prompt(void) 285{ 286 char *pr, *p, *cp, *ev; 287 288 if ((cp = getenv("prompt")) == NULL) 289 cp = ">"; 290 pr = p = strdup(cp); 291 292 while (*p != 0) { 293 if ((*p == '$') && (*(p+1) == '{')) { 294 for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 295 ; 296 *cp = 0; 297 ev = getenv(p + 2); 298 299 if (ev != NULL) 300 printf("%s", ev); 301 p = cp + 1; 302 continue; 303 } 304 putchar(*p++); 305 } 306 putchar(' '); 307 free(pr); 308} 309