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 extern FICL_VM *bf_vm; 43 #endif 44 45 #define MAXARGS 20 /* maximum number of arguments allowed */ 46 47 static void prompt(void); 48 49 #ifndef BOOT_FORTH 50 /* 51 * Perform the command 52 */ 53 static int 54 perform(int argc, char *argv[]) 55 { 56 int result; 57 struct bootblk_command **cmdp; 58 bootblk_cmd_t *cmd; 59 60 if (argc < 1) 61 return(CMD_OK); 62 63 /* set return defaults; a successful command will override these */ 64 command_errmsg = command_errbuf; 65 strcpy(command_errbuf, "no error message"); 66 cmd = NULL; 67 result = CMD_ERROR; 68 69 /* search the command set for the command */ 70 SET_FOREACH(cmdp, Xcommand_set) { 71 if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name)) 72 cmd = (*cmdp)->c_fn; 73 } 74 if (cmd != NULL) { 75 result = (cmd)(argc, argv); 76 } else { 77 command_errmsg = "unknown command"; 78 } 79 return(result); 80 } 81 #endif /* ! BOOT_FORTH */ 82 83 /* 84 * Interactive mode 85 */ 86 void 87 interact(void) 88 { 89 static 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 /* Read our default configuration. */ 100 include("/boot/loader.rc"); 101 102 printf("\n"); 103 104 /* 105 * Before interacting, we might want to autoboot. 106 */ 107 autoboot_maybe(); 108 109 /* 110 * Not autobooting, go manual 111 */ 112 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 113 if (getenv("prompt") == NULL) 114 setenv("prompt", "${interpret}", 1); 115 if (getenv("interpret") == NULL) 116 setenv("interpret", "OK", 1); 117 118 119 for (;;) { 120 input[0] = '\0'; 121 prompt(); 122 ngets(input, sizeof(input)); 123 #ifdef BOOT_FORTH 124 bf_vm->sourceID.i = 0; 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 */ 147 COMMAND_SET(include, "include", "read commands from a file", command_include); 148 149 static int 150 command_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((u_int)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 174 /* 175 * Header prepended to each line. The text immediately follows the header. 176 * We try to make this short in order to save memory -- the loader has 177 * limited memory available, and some of the forth files are very long. 178 */ 179 struct includeline 180 { 181 struct includeline *next; 182 #ifndef BOOT_FORTH 183 int flags; 184 int line; 185 #define SL_QUIET (1<<0) 186 #define SL_IGNOREERR (1<<1) 187 #endif 188 char text[0]; 189 }; 190 191 int 192 include(const char *filename) 193 { 194 struct includeline *script, *se, *sp; 195 char input[256]; /* big enough? */ 196 #ifdef BOOT_FORTH 197 int res; 198 char *cp; 199 int prevsrcid, fd, line; 200 #else 201 int argc,res; 202 char **argv, *cp; 203 int fd, flags, line; 204 #endif 205 206 if (((fd = open(filename, O_RDONLY)) == -1)) { 207 snprintf(command_errbuf, sizeof(command_errbuf), 208 "can't open '%s': %s", filename, strerror(errno)); 209 return(CMD_ERROR); 210 } 211 212 /* 213 * Read the script into memory. 214 */ 215 script = se = NULL; 216 line = 0; 217 218 while (fgetstr(input, sizeof(input), fd) >= 0) { 219 line++; 220 #ifdef BOOT_FORTH 221 cp = input; 222 #else 223 flags = 0; 224 /* Discard comments */ 225 if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0) 226 continue; 227 cp = input; 228 /* Echo? */ 229 if (input[0] == '@') { 230 cp++; 231 flags |= SL_QUIET; 232 } 233 /* Error OK? */ 234 if (input[0] == '-') { 235 cp++; 236 flags |= SL_IGNOREERR; 237 } 238 #endif 239 /* Allocate script line structure and copy line, flags */ 240 if (*cp == '\0') 241 continue; /* ignore empty line, save memory */ 242 sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 243 /* On malloc failure (it happens!), free as much as possible and exit */ 244 if (sp == NULL) { 245 while (script != NULL) { 246 se = script; 247 script = script->next; 248 free(se); 249 } 250 snprintf(command_errbuf, sizeof(command_errbuf), 251 "file '%s' line %d: memory allocation failure - aborting", 252 filename, line); 253 return (CMD_ERROR); 254 } 255 strcpy(sp->text, cp); 256 #ifndef BOOT_FORTH 257 sp->flags = flags; 258 sp->line = line; 259 #endif 260 sp->next = NULL; 261 262 if (script == NULL) { 263 script = sp; 264 } else { 265 se->next = sp; 266 } 267 se = sp; 268 } 269 close(fd); 270 271 /* 272 * Execute the script 273 */ 274 #ifndef BOOT_FORTH 275 argv = NULL; 276 #else 277 prevsrcid = bf_vm->sourceID.i; 278 bf_vm->sourceID.i = fd; 279 #endif 280 res = CMD_OK; 281 for (sp = script; sp != NULL; sp = sp->next) { 282 283 #ifdef BOOT_FORTH 284 res = bf_run(sp->text); 285 if (res != VM_OUTOFTEXT) { 286 snprintf(command_errbuf, sizeof(command_errbuf), 287 "Error while including %s, in the line:\n%s", 288 filename, sp->text); 289 res = CMD_ERROR; 290 break; 291 } else 292 res = CMD_OK; 293 #else 294 /* print if not being quiet */ 295 if (!(sp->flags & SL_QUIET)) { 296 prompt(); 297 printf("%s\n", sp->text); 298 } 299 300 /* Parse the command */ 301 if (!parse(&argc, &argv, sp->text)) { 302 if ((argc > 0) && (perform(argc, argv) != 0)) { 303 /* normal command */ 304 printf("%s: %s\n", argv[0], command_errmsg); 305 if (!(sp->flags & SL_IGNOREERR)) { 306 res=CMD_ERROR; 307 break; 308 } 309 } 310 free(argv); 311 argv = NULL; 312 } else { 313 printf("%s line %d: parse error\n", filename, sp->line); 314 res=CMD_ERROR; 315 break; 316 } 317 #endif 318 } 319 #ifndef BOOT_FORTH 320 if (argv != NULL) 321 free(argv); 322 #else 323 bf_vm->sourceID.i = prevsrcid; 324 #endif 325 while(script != NULL) { 326 se = script; 327 script = script->next; 328 free(se); 329 } 330 return(res); 331 } 332 333 /* 334 * Emit the current prompt; use the same syntax as the parser 335 * for embedding environment variables. 336 */ 337 static void 338 prompt(void) 339 { 340 char *pr, *p, *cp, *ev; 341 342 if ((cp = getenv("prompt")) == NULL) 343 cp = ">"; 344 pr = p = strdup(cp); 345 346 while (*p != 0) { 347 if ((*p == '$') && (*(p+1) == '{')) { 348 for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 349 ; 350 *cp = 0; 351 ev = getenv(p + 2); 352 353 if (ev != NULL) 354 printf("%s", ev); 355 p = cp + 1; 356 continue; 357 } 358 putchar(*p++); 359 } 360 putchar(' '); 361 free(pr); 362 } 363