2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 * Simple commandline interpreter, toplevel and misc.
33 * XXX may be obsoleted by BootFORTH or some other, better, interpreter.
38 #include "bootstrap.h"
42 #define RETURN(x) ficlStackPushInteger(ficlVmGetDataStack(bf_vm),!x); return(x)
46 #define RETURN(x) return(x)
49 #include "linenoise/linenoise.h"
51 #define MAXARGS 20 /* maximum number of arguments allowed */
53 static char *prompt(void);
56 static int perform(int argc
, char *argv
[]);
62 perform(int argc
, char *argv
[])
65 struct bootblk_command
**cmdp
;
71 /* set return defaults; a successful command will override these */
72 command_errmsg
= command_errbuf
;
73 strcpy(command_errbuf
, "no error message");
77 /* search the command set for the command */
78 SET_FOREACH(cmdp
, Xcommand_set
) {
79 if (((*cmdp
)->c_name
!= NULL
) && !strcmp(argv
[0], (*cmdp
)->c_name
))
83 result
= (cmd
)(argc
, argv
);
85 command_errmsg
= "unknown command";
89 #endif /* ! BOOT_FORTH */
95 interact(const char *rc
)
99 bf_init((rc
) ? "" : NULL
);
102 /* Read our default configuration. */
103 include("/boot/loader.rc");
104 } else if (*rc
!= '\0')
110 * Before interacting, we might want to autoboot.
115 * Not autobooting, go manual
117 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n");
118 if (getenv("prompt") == NULL
)
119 setenv("prompt", "${interpret}", 1);
120 if (getenv("interpret") == NULL
)
121 setenv("interpret", "ok", 1);
124 while ((input
= linenoise(prompt())) != NULL
) {
125 bf_vm
->sourceId
.i
= 0;
127 linenoiseHistoryAdd(input
);
133 * Read commands from a file, then execute them.
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.
138 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so
139 * that the script won't stop if they fail).
141 COMMAND_SET(include
, "include", "read commands from a file", command_include
);
144 command_include(int argc
, char *argv
[])
151 * Since argv is static, we need to save it here.
153 argvbuf
= (char**) calloc((u_int
)argc
, sizeof(char*));
154 for (i
= 0; i
< argc
; i
++)
155 argvbuf
[i
] = strdup(argv
[i
]);
158 for (i
= 1; (i
< argc
) && (res
== CMD_OK
); i
++)
159 res
= include(argvbuf
[i
]);
161 for (i
= 0; i
< argc
; i
++)
169 * Header prepended to each line. The text immediately follows the header.
170 * We try to make this short in order to save memory -- the loader has
171 * limited memory available, and some of the forth files are very long.
175 struct includeline
*next
;
181 * The PXE TFTP service allows opening exactly one connection at the time,
182 * so we need to read included file into memory, then process line by line
183 * as it may contain embedded include commands.
186 include(const char *filename
)
188 struct includeline
*script
, *se
, *sp
;
190 int prevsrcid
, fd
, line
;
191 char *cp
, input
[256]; /* big enough? */
193 if (((fd
= open(filename
, O_RDONLY
)) == -1)) {
194 snprintf(command_errbuf
, sizeof (command_errbuf
), "can't open '%s': %s",
195 filename
, strerror(errno
));
199 * Read the script into memory.
204 while (fgetstr(input
, sizeof(input
), fd
) >= 0) {
207 /* Allocate script line structure and copy line, flags */
209 continue; /* ignore empty line, save memory */
210 if (cp
[0] == '\\' && cp
[1] == ' ')
211 continue; /* ignore comment */
213 sp
= malloc(sizeof(struct includeline
) + strlen(cp
) + 1);
214 /* On malloc failure (it happens!), free as much as possible and exit */
216 while (script
!= NULL
) {
218 script
= script
->next
;
221 snprintf(command_errbuf
, sizeof (command_errbuf
),
222 "file '%s' line %d: memory allocation failure - aborting",
227 strcpy(sp
->text
, cp
);
231 if (script
== NULL
) {
244 prevsrcid
= bf_vm
->sourceId
.i
;
245 bf_vm
->sourceId
.i
= fd
+1; /* 0 is user input device */
249 for (sp
= script
; sp
!= NULL
; sp
= sp
->next
) {
250 res
= bf_run(sp
->text
);
251 if (res
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
252 snprintf(command_errbuf
, sizeof (command_errbuf
),
253 "Error while including %s, in the line %d:\n%s",
254 filename
, sp
->line
, sp
->text
);
261 bf_vm
->sourceId
.i
= -1;
263 bf_vm
->sourceId
.i
= prevsrcid
;
265 while(script
!= NULL
) {
267 script
= script
->next
;
275 * Emit the current prompt; use the same syntax as the parser
276 * for embedding environment variables.
281 static char promptbuf
[20]; /* probably too large, but well... */
282 char *pr
, *p
, *cp
, *ev
;
285 if ((cp
= getenv("prompt")) == NULL
)
286 cp
= (char *)(uintptr_t)">";
290 if ((*p
== '$') && (*(p
+1) == '{')) {
291 for (cp
= p
+ 2; (*cp
!= 0) && (*cp
!= '}'); cp
++)
297 n
= sprintf(promptbuf
+n
, "%s", ev
);
304 if (promptbuf
[n
- 1] != ' ')
305 promptbuf
[n
++] = ' ';