interp.c revision 42000
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 * $Id: interp.c,v 1.8 1998/11/04 00:29:01 msmith Exp $ 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) stackPushINT32(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 i, 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 int argc; 91 char **argv; 92 93#ifdef BOOT_FORTH 94 bf_init(); 95#endif 96 97 /* 98 * Read our default configuration 99 */ 100 source("/boot/boot.conf"); 101 printf("\n"); 102 /* 103 * Before interacting, we might want to autoboot. 104 */ 105 autoboot_maybe(); 106 107 /* 108 * Not autobooting, go manual 109 */ 110 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 111 setenv("prompt", "${currdev}>", 1); 112 113 114 for (;;) { 115 input[0] = '\0'; 116 prompt(); 117 ngets(input, sizeof(input)); 118#ifdef BOOT_FORTH 119 bf_run(input); 120#else 121 if (!parse(&argc, &argv, input)) { 122 if (perform(argc, argv)) 123 printf("%s: %s\n", argv[0], command_errmsg); 124 free(argv); 125 } else { 126 printf("parse error\n"); 127 } 128#endif 129 } 130} 131 132/* 133 * Read commands from a file, then execute them. 134 * 135 * We store the commands in memory and close the source file so that the media 136 * holding it can safely go away while we are executing. 137 * 138 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 139 * that the script won't stop if they fail). 140 */ 141COMMAND_SET(source, "source", "read commands from a file", command_source); 142 143static int 144command_source(int argc, char *argv[]) 145{ 146 int i; 147 148 for (i = 1; i < argc; i++) 149 source(argv[i]); 150 RETURN(CMD_OK); 151} 152 153struct sourceline 154{ 155 char *text; 156 int flags; 157 int line; 158#define SL_QUIET (1<<0) 159#define SL_IGNOREERR (1<<1) 160 struct sourceline *next; 161}; 162 163void 164source(char *filename) 165{ 166 struct sourceline *script, *se, *sp; 167 char input[256]; /* big enough? */ 168 int argc; 169 char **argv, *cp; 170 int fd, flags, line; 171 172 if (((fd = open(filename, O_RDONLY)) == -1)) { 173 printf("can't open '%s': %s\n", filename, strerror(errno)); 174 return; 175 } 176 177 /* 178 * Read the script into memory. 179 */ 180 script = se = NULL; 181 line = 0; 182 183 while (fgetstr(input, sizeof(input), fd) > 0) { 184 line++; 185 flags = 0; 186 /* Discard comments */ 187 if (input[0] == '#') 188 continue; 189 cp = input; 190 /* Echo? */ 191 if (input[0] == '@') { 192 cp++; 193 flags |= SL_QUIET; 194 } 195 /* Error OK? */ 196 if (input[0] == '-') { 197 cp++; 198 flags |= SL_IGNOREERR; 199 } 200 /* Allocate script line structure and copy line, flags */ 201 sp = malloc(sizeof(struct sourceline) + strlen(cp) + 1); 202 sp->text = (char *)sp + sizeof(struct sourceline); 203 strcpy(sp->text, cp); 204 sp->flags = flags; 205 sp->line = line; 206 sp->next = NULL; 207 208 if (script == NULL) { 209 script = sp; 210 } else { 211 se->next = sp; 212 } 213 se = sp; 214 } 215 close(fd); 216 217 /* 218 * Execute the script 219 */ 220 argv = NULL; 221 for (sp = script; sp != NULL; sp = sp->next) { 222 223 /* print if not being quiet */ 224 if (!(sp->flags & SL_QUIET)) { 225 prompt(); 226 printf("%s\n", sp->text); 227 } 228 229 /* Parse the command */ 230 if (!parse(&argc, &argv, sp->text)) { 231 if ((argc > 0) && (perform(argc, argv) != 0)) { 232 /* normal command */ 233 printf("%s: %s\n", argv[0], command_errmsg); 234 if (!(sp->flags & SL_IGNOREERR)) 235 break; 236 } 237 free(argv); 238 argv = NULL; 239 } else { 240 printf("%s line %d: parse error\n", filename, sp->line); 241 break; 242 } 243 } 244 if (argv != NULL) 245 free(argv); 246 while(script != NULL) { 247 se = script; 248 script = script->next; 249 free(se); 250 } 251} 252 253/* 254 * Emit the current prompt; use the same syntax as the parser 255 * for embedding environment variables. 256 */ 257static void 258prompt(void) 259{ 260 char *p, *cp, *ev; 261 262 if ((cp = getenv("prompt")) == NULL) 263 cp = ">"; 264 p = strdup(cp); 265 266 while (*p != 0) { 267 if ((*p == '$') && (*(p+1) == '{')) { 268 for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 269 ; 270 *cp = 0; 271 ev = getenv(p + 2); 272 273 if (ev != NULL) 274 printf(ev); 275 p = cp + 1; 276 continue; 277 } 278 putchar(*p++); 279 } 280 putchar(' '); 281} 282