interp.c revision 52748
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 52748 1999-11-01 08:05:22Z dcs $ 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 * Perform the command 53 */ 54int 55perform(int argc, char *argv[]) 56{ 57 int result; 58 struct bootblk_command **cmdp; 59 bootblk_cmd_t *cmd; 60 61 if (argc < 1) 62 return(CMD_OK); 63 64 /* set return defaults; a successful command will override these */ 65 command_errmsg = command_errbuf; 66 strcpy(command_errbuf, "no error message"); 67 cmd = NULL; 68 result = CMD_ERROR; 69 70 /* search the command set for the command */ 71 SET_FOREACH(cmdp, Xcommand_set) { 72 if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name)) 73 cmd = (*cmdp)->c_fn; 74 } 75 if (cmd != NULL) { 76 result = (cmd)(argc, argv); 77 } else { 78 command_errmsg = "unknown command"; 79 } 80 RETURN(result); 81} 82 83/* 84 * Interactive mode 85 */ 86void 87interact(void) 88{ 89 char input[256]; /* big enough? */ 90#ifndef BOOT_FORTH 91 int argc; 92 char **argv; 93#endif 94 95#ifdef BOOT_FORTH 96 bf_init(); 97#endif 98 99 /* 100 * Read our default configuration 101 */ 102 if(include("/boot/loader.rc")!=CMD_OK) 103 include("/boot/boot.conf"); 104 printf("\n"); 105 /* 106 * Before interacting, we might want to autoboot. 107 */ 108 autoboot_maybe(); 109 110 /* 111 * Not autobooting, go manual 112 */ 113 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 114 if (getenv("prompt") == NULL) 115 setenv("prompt", "${interpret}\n", 1); 116 if (getenv("interpret") == NULL) 117 setenv("interpret", "ok", 1); 118 119 120 for (;;) { 121 input[0] = '\0'; 122 prompt(); 123 ngets(input, sizeof(input)); 124#ifdef BOOT_FORTH 125 bf_run(input); 126#else 127 if (!parse(&argc, &argv, input)) { 128 if (perform(argc, argv)) 129 printf("%s: %s\n", argv[0], command_errmsg); 130 free(argv); 131 } else { 132 printf("parse error\n"); 133 } 134#endif 135 } 136} 137 138/* 139 * Read commands from a file, then execute them. 140 * 141 * We store the commands in memory and close the source file so that the media 142 * holding it can safely go away while we are executing. 143 * 144 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 145 * that the script won't stop if they fail). 146 */ 147COMMAND_SET(include, "include", "read commands from a file", command_include); 148 149static int 150command_include(int argc, char *argv[]) 151{ 152 int i; 153 int res; 154 char **argvbuf; 155 156 /* 157 * Since argv is static, we need to save it here. 158 */ 159 argvbuf = (char**) calloc(argc, sizeof(char*)); 160 for (i = 0; i < argc; i++) 161 argvbuf[i] = strdup(argv[i]); 162 163 res=CMD_OK; 164 for (i = 1; (i < argc) && (res == CMD_OK); i++) 165 res = include(argvbuf[i]); 166 167 for (i = 0; i < argc; i++) 168 free(argvbuf[i]); 169 free(argvbuf); 170 171 return(res); 172} 173 174struct includeline 175{ 176 char *text; 177 int flags; 178 int line; 179#define SL_QUIET (1<<0) 180#define SL_IGNOREERR (1<<1) 181 struct includeline *next; 182}; 183 184int 185include(char *filename) 186{ 187 struct includeline *script, *se, *sp; 188 char input[256]; /* big enough? */ 189#ifdef BOOT_FORTH 190 int res; 191 char *cp; 192 int fd, line; 193#else 194 int argc,res; 195 char **argv, *cp; 196 int fd, flags, line; 197#endif 198 199 if (((fd = open(filename, O_RDONLY)) == -1)) { 200 sprintf(command_errbuf,"can't open '%s': %s\n", filename, strerror(errno)); 201 return(CMD_ERROR); 202 } 203 204 /* 205 * Read the script into memory. 206 */ 207 script = se = NULL; 208 line = 0; 209 210 while (fgetstr(input, sizeof(input), fd) >= 0) { 211 line++; 212#ifdef BOOT_FORTH 213 cp = input; 214#else 215 flags = 0; 216 /* Discard comments */ 217 if (input[0] == '#') 218 continue; 219 cp = input; 220 /* Echo? */ 221 if (input[0] == '@') { 222 cp++; 223 flags |= SL_QUIET; 224 } 225 /* Error OK? */ 226 if (input[0] == '-') { 227 cp++; 228 flags |= SL_IGNOREERR; 229 } 230#endif 231 /* Allocate script line structure and copy line, flags */ 232 sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 233 sp->text = (char *)sp + sizeof(struct includeline); 234 strcpy(sp->text, cp); 235#ifndef BOOT_FORTH 236 sp->flags = flags; 237#endif 238 sp->line = line; 239 sp->next = NULL; 240 241 if (script == NULL) { 242 script = sp; 243 } else { 244 se->next = sp; 245 } 246 se = sp; 247 } 248 close(fd); 249 250 /* 251 * Execute the script 252 */ 253#ifndef BOOT_FORTH 254 argv = NULL; 255#endif 256 res = CMD_OK; 257 for (sp = script; sp != NULL; sp = sp->next) { 258 259#ifdef BOOT_FORTH 260 res = bf_run(sp->text); 261 if (res != VM_OUTOFTEXT) { 262 sprintf(command_errbuf, "Error while including %s, in the line:\n%s", filename, sp->text); 263 res = CMD_ERROR; 264 break; 265 } else 266 res = CMD_OK; 267#else 268 /* print if not being quiet */ 269 if (!(sp->flags & SL_QUIET)) { 270 prompt(); 271 printf("%s\n", sp->text); 272 } 273 274 /* Parse the command */ 275 if (!parse(&argc, &argv, sp->text)) { 276 if ((argc > 0) && (perform(argc, argv) != 0)) { 277 /* normal command */ 278 printf("%s: %s\n", argv[0], command_errmsg); 279 if (!(sp->flags & SL_IGNOREERR)) { 280 res=CMD_ERROR; 281 break; 282 } 283 } 284 free(argv); 285 argv = NULL; 286 } else { 287 printf("%s line %d: parse error\n", filename, sp->line); 288 res=CMD_ERROR; 289 break; 290 } 291#endif 292 } 293#ifndef BOOT_FORTH 294 if (argv != NULL) 295 free(argv); 296#endif 297 while(script != NULL) { 298 se = script; 299 script = script->next; 300 free(se); 301 } 302 return(res); 303} 304 305/* 306 * Emit the current prompt; use the same syntax as the parser 307 * for embedding environment variables. 308 */ 309static void 310prompt(void) 311{ 312 char *pr, *p, *cp, *ev; 313 314 if ((cp = getenv("prompt")) == NULL) 315 cp = ">"; 316 pr = p = strdup(cp); 317 318 while (*p != 0) { 319 if ((*p == '$') && (*(p+1) == '{')) { 320 for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 321 ; 322 *cp = 0; 323 ev = getenv(p + 2); 324 325 if (ev != NULL) 326 printf(ev); 327 p = cp + 1; 328 continue; 329 } 330 putchar(*p++); 331 } 332 putchar(' '); 333 free(pr); 334} 335