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