1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3ca987d46SWarner Losh * All rights reserved. 4ca987d46SWarner Losh * 5ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without 6ca987d46SWarner Losh * modification, are permitted provided that the following conditions 7ca987d46SWarner Losh * are met: 8ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright 9ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer. 10ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 11ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the 12ca987d46SWarner Losh * documentation and/or other materials provided with the distribution. 13ca987d46SWarner Losh * 14ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ca987d46SWarner Losh * SUCH DAMAGE. 25ca987d46SWarner Losh */ 26ca987d46SWarner Losh 27ca987d46SWarner Losh #include <sys/cdefs.h> 28ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 29ca987d46SWarner Losh 30ca987d46SWarner Losh #include <sys/param.h> /* to pick up __FreeBSD_version */ 31ca987d46SWarner Losh #include <string.h> 32ca987d46SWarner Losh #include <stand.h> 33ca987d46SWarner Losh #include "bootstrap.h" 34ca987d46SWarner Losh #include "ficl.h" 35ca987d46SWarner Losh 36ca987d46SWarner Losh extern unsigned bootprog_rev; 37ca987d46SWarner Losh 38ca987d46SWarner Losh /* #define BFORTH_DEBUG */ 39ca987d46SWarner Losh 40ca987d46SWarner Losh #ifdef BFORTH_DEBUG 41ca987d46SWarner Losh #define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 42ca987d46SWarner Losh #else 43ca987d46SWarner Losh #define DEBUG(fmt, args...) 44ca987d46SWarner Losh #endif 45ca987d46SWarner Losh 46ca987d46SWarner Losh /* 47ca987d46SWarner Losh * Eventually, all builtin commands throw codes must be defined 48ca987d46SWarner Losh * elsewhere, possibly bootstrap.h. For now, just this code, used 49ca987d46SWarner Losh * just in this file, it is getting defined. 50ca987d46SWarner Losh */ 51ca987d46SWarner Losh #define BF_PARSE 100 52ca987d46SWarner Losh 53ca987d46SWarner Losh /* 54ca987d46SWarner Losh * FreeBSD loader default dictionary cells 55ca987d46SWarner Losh */ 56ca987d46SWarner Losh #ifndef BF_DICTSIZE 57ca987d46SWarner Losh #define BF_DICTSIZE 10000 58ca987d46SWarner Losh #endif 59ca987d46SWarner Losh 60ca987d46SWarner Losh /* 61ca987d46SWarner Losh * BootForth Interface to Ficl Forth interpreter. 62ca987d46SWarner Losh */ 63ba25195eSWarner Losh 64ca987d46SWarner Losh FICL_SYSTEM *bf_sys; 65ca987d46SWarner Losh FICL_VM *bf_vm; 66ca987d46SWarner Losh 67ca987d46SWarner Losh /* 68ca987d46SWarner Losh * Shim for taking commands from BF and passing them out to 'standard' 69ca987d46SWarner Losh * argv/argc command functions. 70ca987d46SWarner Losh */ 71ca987d46SWarner Losh static void 72ca987d46SWarner Losh bf_command(FICL_VM *vm) 73ca987d46SWarner Losh { 74ca987d46SWarner Losh char *name, *line, *tail, *cp; 75ca987d46SWarner Losh size_t len; 76ca987d46SWarner Losh struct bootblk_command **cmdp; 77ca987d46SWarner Losh bootblk_cmd_t *cmd; 78ca987d46SWarner Losh int nstrings, i; 79ca987d46SWarner Losh int argc, result; 80ca987d46SWarner Losh char **argv; 81ca987d46SWarner Losh 82ca987d46SWarner Losh /* Get the name of the current word */ 83ca987d46SWarner Losh name = vm->runningWord->name; 84ca987d46SWarner Losh 85ca987d46SWarner Losh /* Find our command structure */ 86ca987d46SWarner Losh cmd = NULL; 87ca987d46SWarner Losh SET_FOREACH(cmdp, Xcommand_set) { 88ca987d46SWarner Losh if (((*cmdp)->c_name != NULL) && !strcmp(name, (*cmdp)->c_name)) 89ca987d46SWarner Losh cmd = (*cmdp)->c_fn; 90ca987d46SWarner Losh } 91ca987d46SWarner Losh if (cmd == NULL) 92ca987d46SWarner Losh panic("callout for unknown command '%s'", name); 93ca987d46SWarner Losh 94ca987d46SWarner Losh /* Check whether we have been compiled or are being interpreted */ 95ca987d46SWarner Losh if (stackPopINT(vm->pStack)) { 96ca987d46SWarner Losh /* 97ca987d46SWarner Losh * Get parameters from stack, in the format: 98ca987d46SWarner Losh * an un ... a2 u2 a1 u1 n -- 99ca987d46SWarner Losh * Where n is the number of strings, a/u are pairs of 100ca987d46SWarner Losh * address/size for strings, and they will be concatenated 101ca987d46SWarner Losh * in LIFO order. 102ca987d46SWarner Losh */ 103ca987d46SWarner Losh nstrings = stackPopINT(vm->pStack); 104ca987d46SWarner Losh for (i = 0, len = 0; i < nstrings; i++) 105ca987d46SWarner Losh len += stackFetch(vm->pStack, i * 2).i + 1; 106ca987d46SWarner Losh line = malloc(strlen(name) + len + 1); 107ca987d46SWarner Losh strcpy(line, name); 108ca987d46SWarner Losh 109ca987d46SWarner Losh if (nstrings) 110ca987d46SWarner Losh for (i = 0; i < nstrings; i++) { 111ca987d46SWarner Losh len = stackPopINT(vm->pStack); 112ca987d46SWarner Losh cp = stackPopPtr(vm->pStack); 113ca987d46SWarner Losh strcat(line, " "); 114ca987d46SWarner Losh strncat(line, cp, len); 115ca987d46SWarner Losh } 116ca987d46SWarner Losh } else { 117ca987d46SWarner Losh /* Get remainder of invocation */ 118ca987d46SWarner Losh tail = vmGetInBuf(vm); 119ca987d46SWarner Losh for (cp = tail, len = 0; cp != vm->tib.end && *cp != 0 && *cp != '\n'; cp++, len++) 120ca987d46SWarner Losh ; 121ca987d46SWarner Losh 122ca987d46SWarner Losh line = malloc(strlen(name) + len + 2); 123ca987d46SWarner Losh strcpy(line, name); 124ca987d46SWarner Losh if (len > 0) { 125ca987d46SWarner Losh strcat(line, " "); 126ca987d46SWarner Losh strncat(line, tail, len); 127ca987d46SWarner Losh vmUpdateTib(vm, tail + len); 128ca987d46SWarner Losh } 129ca987d46SWarner Losh } 130ca987d46SWarner Losh DEBUG("cmd '%s'", line); 131ca987d46SWarner Losh 132ca987d46SWarner Losh command_errmsg = command_errbuf; 133ca987d46SWarner Losh command_errbuf[0] = 0; 134ca987d46SWarner Losh if (!parse(&argc, &argv, line)) { 135ca987d46SWarner Losh result = (cmd)(argc, argv); 136ca987d46SWarner Losh free(argv); 137ca987d46SWarner Losh } else { 138ca987d46SWarner Losh result=BF_PARSE; 139ca987d46SWarner Losh } 140ca987d46SWarner Losh 141ca987d46SWarner Losh switch (result) { 142ca987d46SWarner Losh case CMD_CRIT: 143ca987d46SWarner Losh printf("%s\n", command_errmsg); 144ca987d46SWarner Losh break; 145ca987d46SWarner Losh case CMD_FATAL: 146ca987d46SWarner Losh panic("%s\n", command_errmsg); 147ca987d46SWarner Losh } 148ca987d46SWarner Losh 149ca987d46SWarner Losh free(line); 150ca987d46SWarner Losh /* 151ca987d46SWarner Losh * If there was error during nested ficlExec(), we may no longer have 152ca987d46SWarner Losh * valid environment to return. Throw all exceptions from here. 153ca987d46SWarner Losh */ 154ca987d46SWarner Losh if (result != CMD_OK) 155ca987d46SWarner Losh vmThrow(vm, result); 156ca987d46SWarner Losh 157ca987d46SWarner Losh /* This is going to be thrown!!! */ 158ca987d46SWarner Losh stackPushINT(vm->pStack,result); 159ca987d46SWarner Losh } 160ca987d46SWarner Losh 161ca987d46SWarner Losh /* 162ca987d46SWarner Losh * Replace a word definition (a builtin command) with another 163ca987d46SWarner Losh * one that: 164ca987d46SWarner Losh * 165ca987d46SWarner Losh * - Throw error results instead of returning them on the stack 166ca987d46SWarner Losh * - Pass a flag indicating whether the word was compiled or is 167ca987d46SWarner Losh * being interpreted. 168ca987d46SWarner Losh * 169ca987d46SWarner Losh * There is one major problem with builtins that cannot be overcome 170ca987d46SWarner Losh * in anyway, except by outlawing it. We want builtins to behave 171ca987d46SWarner Losh * differently depending on whether they have been compiled or they 172ca987d46SWarner Losh * are being interpreted. Notice that this is *not* the interpreter's 173ca987d46SWarner Losh * current state. For example: 174ca987d46SWarner Losh * 175ca987d46SWarner Losh * : example ls ; immediate 176ca987d46SWarner Losh * : problem example ; \ "ls" gets executed while compiling 177ca987d46SWarner Losh * example \ "ls" gets executed while interpreting 178ca987d46SWarner Losh * 179ca987d46SWarner Losh * Notice that, though the current state is different in the two 180ca987d46SWarner Losh * invocations of "example", in both cases "ls" has been 181ca987d46SWarner Losh * *compiled in*, which is what we really want. 182ca987d46SWarner Losh * 183ca987d46SWarner Losh * The problem arises when you tick the builtin. For example: 184ca987d46SWarner Losh * 185ca987d46SWarner Losh * : example-1 ['] ls postpone literal ; immediate 186ca987d46SWarner Losh * : example-2 example-1 execute ; immediate 187ca987d46SWarner Losh * : problem example-2 ; 188ca987d46SWarner Losh * example-2 189ca987d46SWarner Losh * 190ca987d46SWarner Losh * We have no way, when we get EXECUTEd, of knowing what our behavior 191ca987d46SWarner Losh * should be. Thus, our only alternative is to "outlaw" this. See RFI 192ca987d46SWarner Losh * 0007, and ANS Forth Standard's appendix D, item 6.7 for a related 193ca987d46SWarner Losh * problem, concerning compile semantics. 194ca987d46SWarner Losh * 195ca987d46SWarner Losh * The problem is compounded by the fact that "' builtin CATCH" is valid 196ca987d46SWarner Losh * and desirable. The only solution is to create an intermediary word. 197ca987d46SWarner Losh * For example: 198ca987d46SWarner Losh * 199ca987d46SWarner Losh * : my-ls ls ; 200ca987d46SWarner Losh * : example ['] my-ls catch ; 201ca987d46SWarner Losh * 202ca987d46SWarner Losh * So, with the below implementation, here is a summary of the behavior 203ca987d46SWarner Losh * of builtins: 204ca987d46SWarner Losh * 205ca987d46SWarner Losh * ls -l \ "interpret" behavior, ie, 206ca987d46SWarner Losh * \ takes parameters from TIB 207ca987d46SWarner Losh * : ex-1 s" -l" 1 ls ; \ "compile" behavior, ie, 208ca987d46SWarner Losh * \ takes parameters from the stack 209ca987d46SWarner Losh * : ex-2 ['] ls catch ; immediate \ undefined behavior 210ca987d46SWarner Losh * : ex-3 ['] ls catch ; \ undefined behavior 211ca987d46SWarner Losh * ex-2 ex-3 \ "interpret" behavior, 212ca987d46SWarner Losh * \ catch works 213ca987d46SWarner Losh * : ex-4 ex-2 ; \ "compile" behavior, 214ca987d46SWarner Losh * \ catch does not work 215ca987d46SWarner Losh * : ex-5 ex-3 ; immediate \ same as ex-2 216ca987d46SWarner Losh * : ex-6 ex-3 ; \ same as ex-3 217ca987d46SWarner Losh * : ex-7 ['] ex-1 catch ; \ "compile" behavior, 218ca987d46SWarner Losh * \ catch works 219ca987d46SWarner Losh * : ex-8 postpone ls ; immediate \ same as ex-2 220ca987d46SWarner Losh * : ex-9 postpone ls ; \ same as ex-3 221ca987d46SWarner Losh * 222ca987d46SWarner Losh * As the definition below is particularly tricky, and it's side effects 223ca987d46SWarner Losh * must be well understood by those playing with it, I'll be heavy on 224ca987d46SWarner Losh * the comments. 225ca987d46SWarner Losh * 226ca987d46SWarner Losh * (if you edit this definition, pay attention to trailing spaces after 227ca987d46SWarner Losh * each word -- I warned you! :-) ) 228ca987d46SWarner Losh */ 229ca987d46SWarner Losh #define BUILTIN_CONSTRUCTOR \ 230ca987d46SWarner Losh ": builtin: " \ 231ca987d46SWarner Losh ">in @ " /* save the tib index pointer */ \ 232ca987d46SWarner Losh "' " /* get next word's xt */ \ 233ca987d46SWarner Losh "swap >in ! " /* point again to next word */ \ 234ca987d46SWarner Losh "create " /* create a new definition of the next word */ \ 235ca987d46SWarner Losh ", " /* save previous definition's xt */ \ 236ca987d46SWarner Losh "immediate " /* make the new definition an immediate word */ \ 237ca987d46SWarner Losh \ 238ca987d46SWarner Losh "does> " /* Now, the *new* definition will: */ \ 239ca987d46SWarner Losh "state @ if " /* if in compiling state: */ \ 240ca987d46SWarner Losh "1 postpone literal " /* pass 1 flag to indicate compile */ \ 241ca987d46SWarner Losh "@ compile, " /* compile in previous definition */ \ 242ca987d46SWarner Losh "postpone throw " /* throw stack-returned result */ \ 243ca987d46SWarner Losh "else " /* if in interpreting state: */ \ 244ca987d46SWarner Losh "0 swap " /* pass 0 flag to indicate interpret */ \ 245ca987d46SWarner Losh "@ execute " /* call previous definition */ \ 246ca987d46SWarner Losh "throw " /* throw stack-returned result */ \ 247ca987d46SWarner Losh "then ; " 248ca987d46SWarner Losh 249ca987d46SWarner Losh /* 250ca987d46SWarner Losh * Initialise the Forth interpreter, create all our commands as words. 251ca987d46SWarner Losh */ 252ba25195eSWarner Losh void 2536bc86037SWarner Losh bf_init(void) 254ca987d46SWarner Losh { 255ca987d46SWarner Losh struct bootblk_command **cmdp; 256ca987d46SWarner Losh char create_buf[41]; /* 31 characters-long builtins */ 257ba25195eSWarner Losh int fd; 258ca987d46SWarner Losh 259ba25195eSWarner Losh bf_sys = ficlInitSystem(BF_DICTSIZE); 260ba25195eSWarner Losh bf_vm = ficlNewVM(bf_sys); 261ca987d46SWarner Losh 262ca987d46SWarner Losh /* Put all private definitions in a "builtins" vocabulary */ 263ca987d46SWarner Losh ficlExec(bf_vm, "vocabulary builtins also builtins definitions"); 264ca987d46SWarner Losh 265ca987d46SWarner Losh /* Builtin constructor word */ 266ca987d46SWarner Losh ficlExec(bf_vm, BUILTIN_CONSTRUCTOR); 267ca987d46SWarner Losh 268ca987d46SWarner Losh /* make all commands appear as Forth words */ 269ca987d46SWarner Losh SET_FOREACH(cmdp, Xcommand_set) { 270ca987d46SWarner Losh ficlBuild(bf_sys, (char *)(*cmdp)->c_name, bf_command, FW_DEFAULT); 271ca987d46SWarner Losh ficlExec(bf_vm, "forth definitions builtins"); 272ca987d46SWarner Losh sprintf(create_buf, "builtin: %s", (*cmdp)->c_name); 273ca987d46SWarner Losh ficlExec(bf_vm, create_buf); 274ca987d46SWarner Losh ficlExec(bf_vm, "builtins definitions"); 275ca987d46SWarner Losh } 276ca987d46SWarner Losh ficlExec(bf_vm, "only forth definitions"); 277ca987d46SWarner Losh 278ca987d46SWarner Losh /* Export some version numbers so that code can detect the loader/host version */ 279ca987d46SWarner Losh ficlSetEnv(bf_sys, "FreeBSD_version", __FreeBSD_version); 280ca987d46SWarner Losh ficlSetEnv(bf_sys, "loader_version", bootprog_rev); 281ba25195eSWarner Losh 282ba25195eSWarner Losh /* try to load and run init file if present */ 2836bc86037SWarner Losh if ((fd = open("/boot/boot.4th", O_RDONLY)) != -1) { 284ba25195eSWarner Losh (void)ficlExecFD(bf_vm, fd); 285ba25195eSWarner Losh close(fd); 286ba25195eSWarner Losh } 287ba25195eSWarner Losh } 288ca987d46SWarner Losh 289ca987d46SWarner Losh /* 290ca987d46SWarner Losh * Feed a line of user input to the Forth interpreter 291ca987d46SWarner Losh */ 29279a6a17aSWarner Losh static int 29379a6a17aSWarner Losh bf_run(const char *line) 294ca987d46SWarner Losh { 295ca987d46SWarner Losh int result; 296ca987d46SWarner Losh 29779a6a17aSWarner Losh /* 29879a6a17aSWarner Losh * ficl would require extensive changes to accept a const char * 29979a6a17aSWarner Losh * interface. Instead, cast it away here and hope for the best. 30079a6a17aSWarner Losh * We know at the present time the caller for us in the boot 30179a6a17aSWarner Losh * forth loader can tolerate the string being modified because 30279a6a17aSWarner Losh * the string is passed in here and then not touched again. 30379a6a17aSWarner Losh */ 30479a6a17aSWarner Losh result = ficlExec(bf_vm, __DECONST(char *, line)); 305ca987d46SWarner Losh 306ca987d46SWarner Losh DEBUG("ficlExec '%s' = %d", line, result); 307ca987d46SWarner Losh switch (result) { 308ca987d46SWarner Losh case VM_OUTOFTEXT: 309ca987d46SWarner Losh case VM_ABORTQ: 310ca987d46SWarner Losh case VM_QUIT: 311ca987d46SWarner Losh case VM_ERREXIT: 312ca987d46SWarner Losh break; 313ca987d46SWarner Losh case VM_USEREXIT: 314ca987d46SWarner Losh printf("No where to leave to!\n"); 315ca987d46SWarner Losh break; 316ca987d46SWarner Losh case VM_ABORT: 317ca987d46SWarner Losh printf("Aborted!\n"); 318ca987d46SWarner Losh break; 319ca987d46SWarner Losh case BF_PARSE: 320ca987d46SWarner Losh printf("Parse error!\n"); 321ca987d46SWarner Losh break; 322ca987d46SWarner Losh default: 323ca987d46SWarner Losh if (command_errmsg != NULL) { 324ca987d46SWarner Losh printf("%s\n", command_errmsg); 325ca987d46SWarner Losh command_errmsg = NULL; 326ca987d46SWarner Losh } 327ca987d46SWarner Losh } 328ca987d46SWarner Losh 329ca987d46SWarner Losh if (result == VM_USEREXIT) 330ca987d46SWarner Losh panic("interpreter exit"); 331ba25195eSWarner Losh setenv("interpret", bf_vm->state ? "" : "OK", 1); 332ca987d46SWarner Losh 333ca987d46SWarner Losh return (result); 334ca987d46SWarner Losh } 33579a6a17aSWarner Losh 33679a6a17aSWarner Losh void 33779a6a17aSWarner Losh interp_init(void) 33879a6a17aSWarner Losh { 33979a6a17aSWarner Losh 340*86411ec1SWarner Losh setenv("script.lang", "forth", 1); 34179a6a17aSWarner Losh bf_init(); 34279a6a17aSWarner Losh /* Read our default configuration. */ 34379a6a17aSWarner Losh interp_include("/boot/loader.rc"); 34479a6a17aSWarner Losh } 34579a6a17aSWarner Losh 34679a6a17aSWarner Losh int 34779a6a17aSWarner Losh interp_run(const char *input) 34879a6a17aSWarner Losh { 34979a6a17aSWarner Losh 35079a6a17aSWarner Losh bf_vm->sourceID.i = 0; 35179a6a17aSWarner Losh return bf_run(input); 35279a6a17aSWarner Losh } 35379a6a17aSWarner Losh 35479a6a17aSWarner Losh /* 35579a6a17aSWarner Losh * Header prepended to each line. The text immediately follows the header. 35679a6a17aSWarner Losh * We try to make this short in order to save memory -- the loader has 35779a6a17aSWarner Losh * limited memory available, and some of the forth files are very long. 35879a6a17aSWarner Losh */ 35979a6a17aSWarner Losh struct includeline 36079a6a17aSWarner Losh { 36179a6a17aSWarner Losh struct includeline *next; 36279a6a17aSWarner Losh char text[0]; 36379a6a17aSWarner Losh }; 36479a6a17aSWarner Losh 36579a6a17aSWarner Losh int 36679a6a17aSWarner Losh interp_include(const char *filename) 36779a6a17aSWarner Losh { 36879a6a17aSWarner Losh struct includeline *script, *se, *sp; 36979a6a17aSWarner Losh char input[256]; /* big enough? */ 37079a6a17aSWarner Losh int res; 37179a6a17aSWarner Losh char *cp; 37279a6a17aSWarner Losh int prevsrcid, fd, line; 37379a6a17aSWarner Losh 37479a6a17aSWarner Losh if (((fd = open(filename, O_RDONLY)) == -1)) { 37579a6a17aSWarner Losh snprintf(command_errbuf, sizeof(command_errbuf), 37679a6a17aSWarner Losh "can't open '%s': %s", filename, strerror(errno)); 37779a6a17aSWarner Losh return(CMD_ERROR); 37879a6a17aSWarner Losh } 37979a6a17aSWarner Losh 38079a6a17aSWarner Losh /* 38179a6a17aSWarner Losh * Read the script into memory. 38279a6a17aSWarner Losh */ 38379a6a17aSWarner Losh script = se = NULL; 38479a6a17aSWarner Losh line = 0; 38579a6a17aSWarner Losh 38679a6a17aSWarner Losh while (fgetstr(input, sizeof(input), fd) >= 0) { 38779a6a17aSWarner Losh line++; 38879a6a17aSWarner Losh cp = input; 38979a6a17aSWarner Losh /* Allocate script line structure and copy line, flags */ 39079a6a17aSWarner Losh if (*cp == '\0') 39179a6a17aSWarner Losh continue; /* ignore empty line, save memory */ 39279a6a17aSWarner Losh sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 39379a6a17aSWarner Losh /* On malloc failure (it happens!), free as much as possible and exit */ 39479a6a17aSWarner Losh if (sp == NULL) { 39579a6a17aSWarner Losh while (script != NULL) { 39679a6a17aSWarner Losh se = script; 39779a6a17aSWarner Losh script = script->next; 39879a6a17aSWarner Losh free(se); 39979a6a17aSWarner Losh } 40079a6a17aSWarner Losh snprintf(command_errbuf, sizeof(command_errbuf), 40179a6a17aSWarner Losh "file '%s' line %d: memory allocation failure - aborting", 40279a6a17aSWarner Losh filename, line); 40379a6a17aSWarner Losh close(fd); 40479a6a17aSWarner Losh return (CMD_ERROR); 40579a6a17aSWarner Losh } 40679a6a17aSWarner Losh strcpy(sp->text, cp); 40779a6a17aSWarner Losh sp->next = NULL; 40879a6a17aSWarner Losh 40979a6a17aSWarner Losh if (script == NULL) { 41079a6a17aSWarner Losh script = sp; 41179a6a17aSWarner Losh } else { 41279a6a17aSWarner Losh se->next = sp; 41379a6a17aSWarner Losh } 41479a6a17aSWarner Losh se = sp; 41579a6a17aSWarner Losh } 41679a6a17aSWarner Losh close(fd); 41779a6a17aSWarner Losh 41879a6a17aSWarner Losh /* 41979a6a17aSWarner Losh * Execute the script 42079a6a17aSWarner Losh */ 42179a6a17aSWarner Losh prevsrcid = bf_vm->sourceID.i; 42279a6a17aSWarner Losh bf_vm->sourceID.i = fd; 42379a6a17aSWarner Losh res = CMD_OK; 42479a6a17aSWarner Losh for (sp = script; sp != NULL; sp = sp->next) { 42579a6a17aSWarner Losh res = bf_run(sp->text); 42679a6a17aSWarner Losh if (res != VM_OUTOFTEXT) { 42779a6a17aSWarner Losh snprintf(command_errbuf, sizeof(command_errbuf), 42879a6a17aSWarner Losh "Error while including %s, in the line:\n%s", 42979a6a17aSWarner Losh filename, sp->text); 43079a6a17aSWarner Losh res = CMD_ERROR; 43179a6a17aSWarner Losh break; 43279a6a17aSWarner Losh } else 43379a6a17aSWarner Losh res = CMD_OK; 43479a6a17aSWarner Losh } 43579a6a17aSWarner Losh bf_vm->sourceID.i = prevsrcid; 43679a6a17aSWarner Losh 43779a6a17aSWarner Losh while (script != NULL) { 43879a6a17aSWarner Losh se = script; 43979a6a17aSWarner Losh script = script->next; 44079a6a17aSWarner Losh free(se); 44179a6a17aSWarner Losh } 44279a6a17aSWarner Losh return(res); 44379a6a17aSWarner Losh } 444