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$"); 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 44 extern 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 51 static void prompt(void); 52 53 #ifndef BOOT_FORTH 54 static int perform(int argc, char *argv[]); 55 56 /* 57 * Perform the command 58 */ 59 int 60 perform(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 */ 92 void 93 interact(const char *rc) 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((rc) ? "" : NULL); 103 #endif 104 105 if (rc == NULL) { 106 /* Read our default configuration. */ 107 include("/boot/loader.rc"); 108 } else if (*rc != '\0') 109 include(rc); 110 111 printf("\n"); 112 113 /* 114 * Before interacting, we might want to autoboot. 115 */ 116 autoboot_maybe(); 117 118 /* 119 * Not autobooting, go manual 120 */ 121 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 122 if (getenv("prompt") == NULL) 123 setenv("prompt", "${interpret}", 1); 124 if (getenv("interpret") == NULL) 125 setenv("interpret", "OK", 1); 126 127 128 for (;;) { 129 input[0] = '\0'; 130 prompt(); 131 ngets(input, sizeof(input)); 132 #ifdef BOOT_FORTH 133 bf_vm->sourceID.i = 0; 134 bf_run(input); 135 #else 136 if (!parse(&argc, &argv, input)) { 137 if (perform(argc, argv)) 138 printf("%s: %s\n", argv[0], command_errmsg); 139 free(argv); 140 } else { 141 printf("parse error\n"); 142 } 143 #endif 144 } 145 } 146 147 /* 148 * Read commands from a file, then execute them. 149 * 150 * We store the commands in memory and close the source file so that the media 151 * holding it can safely go away while we are executing. 152 * 153 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 154 * that the script won't stop if they fail). 155 */ 156 COMMAND_SET(include, "include", "read commands from a file", command_include); 157 158 static int 159 command_include(int argc, char *argv[]) 160 { 161 int i; 162 int res; 163 char **argvbuf; 164 165 /* 166 * Since argv is static, we need to save it here. 167 */ 168 argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); 169 for (i = 0; i < argc; i++) 170 argvbuf[i] = strdup(argv[i]); 171 172 res=CMD_OK; 173 for (i = 1; (i < argc) && (res == CMD_OK); i++) 174 res = include(argvbuf[i]); 175 176 for (i = 0; i < argc; i++) 177 free(argvbuf[i]); 178 free(argvbuf); 179 180 return(res); 181 } 182 183 /* 184 * Header prepended to each line. The text immediately follows the header. 185 * We try to make this short in order to save memory -- the loader has 186 * limited memory available, and some of the forth files are very long. 187 */ 188 struct includeline 189 { 190 struct includeline *next; 191 #ifndef BOOT_FORTH 192 int flags; 193 int line; 194 #define SL_QUIET (1<<0) 195 #define SL_IGNOREERR (1<<1) 196 #endif 197 char text[0]; 198 }; 199 200 int 201 include(const char *filename) 202 { 203 struct includeline *script, *se, *sp; 204 char input[256]; /* big enough? */ 205 #ifdef BOOT_FORTH 206 int res; 207 char *cp; 208 int prevsrcid, fd, line; 209 #else 210 int argc,res; 211 char **argv, *cp; 212 int fd, flags, line; 213 #endif 214 215 if (((fd = open(filename, O_RDONLY)) == -1)) { 216 snprintf(command_errbuf, sizeof(command_errbuf), 217 "can't open '%s': %s", filename, strerror(errno)); 218 return(CMD_ERROR); 219 } 220 221 /* 222 * Read the script into memory. 223 */ 224 script = se = NULL; 225 line = 0; 226 227 while (fgetstr(input, sizeof(input), fd) >= 0) { 228 line++; 229 #ifdef BOOT_FORTH 230 cp = input; 231 #else 232 flags = 0; 233 /* Discard comments */ 234 if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0) 235 continue; 236 cp = input; 237 /* Echo? */ 238 if (input[0] == '@') { 239 cp++; 240 flags |= SL_QUIET; 241 } 242 /* Error OK? */ 243 if (input[0] == '-') { 244 cp++; 245 flags |= SL_IGNOREERR; 246 } 247 #endif 248 /* Allocate script line structure and copy line, flags */ 249 if (*cp == '\0') 250 continue; /* ignore empty line, save memory */ 251 sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 252 /* On malloc failure (it happens!), free as much as possible and exit */ 253 if (sp == NULL) { 254 while (script != NULL) { 255 se = script; 256 script = script->next; 257 free(se); 258 } 259 snprintf(command_errbuf, sizeof(command_errbuf), 260 "file '%s' line %d: memory allocation failure - aborting", 261 filename, line); 262 return (CMD_ERROR); 263 } 264 strcpy(sp->text, cp); 265 #ifndef BOOT_FORTH 266 sp->flags = flags; 267 sp->line = line; 268 #endif 269 sp->next = NULL; 270 271 if (script == NULL) { 272 script = sp; 273 } else { 274 se->next = sp; 275 } 276 se = sp; 277 } 278 close(fd); 279 280 /* 281 * Execute the script 282 */ 283 #ifndef BOOT_FORTH 284 argv = NULL; 285 #else 286 prevsrcid = bf_vm->sourceID.i; 287 bf_vm->sourceID.i = fd; 288 #endif 289 res = CMD_OK; 290 for (sp = script; sp != NULL; sp = sp->next) { 291 292 #ifdef BOOT_FORTH 293 res = bf_run(sp->text); 294 if (res != VM_OUTOFTEXT) { 295 snprintf(command_errbuf, sizeof(command_errbuf), 296 "Error while including %s, in the line:\n%s", 297 filename, sp->text); 298 res = CMD_ERROR; 299 break; 300 } else 301 res = CMD_OK; 302 #else 303 /* print if not being quiet */ 304 if (!(sp->flags & SL_QUIET)) { 305 prompt(); 306 printf("%s\n", sp->text); 307 } 308 309 /* Parse the command */ 310 if (!parse(&argc, &argv, sp->text)) { 311 if ((argc > 0) && (perform(argc, argv) != 0)) { 312 /* normal command */ 313 printf("%s: %s\n", argv[0], command_errmsg); 314 if (!(sp->flags & SL_IGNOREERR)) { 315 res=CMD_ERROR; 316 break; 317 } 318 } 319 free(argv); 320 argv = NULL; 321 } else { 322 printf("%s line %d: parse error\n", filename, sp->line); 323 res=CMD_ERROR; 324 break; 325 } 326 #endif 327 } 328 #ifndef BOOT_FORTH 329 if (argv != NULL) 330 free(argv); 331 #else 332 bf_vm->sourceID.i = prevsrcid; 333 #endif 334 while(script != NULL) { 335 se = script; 336 script = script->next; 337 free(se); 338 } 339 return(res); 340 } 341 342 /* 343 * Emit the current prompt; use the same syntax as the parser 344 * for embedding environment variables. 345 */ 346 static void 347 prompt(void) 348 { 349 char *pr, *p, *cp, *ev; 350 351 if ((cp = getenv("prompt")) == NULL) 352 cp = ">"; 353 pr = p = strdup(cp); 354 355 while (*p != 0) { 356 if ((*p == '$') && (*(p+1) == '{')) { 357 for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 358 ; 359 *cp = 0; 360 ev = getenv(p + 2); 361 362 if (ev != NULL) 363 printf("%s", ev); 364 p = cp + 1; 365 continue; 366 } 367 putchar(*p++); 368 } 369 putchar(' '); 370 free(pr); 371 } 372