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 if (include("/boot/loader.rc") != CMD_OK
)
104 include("/boot/boot.conf");
105 } else if (*rc
!= '\0')
111 * Before interacting, we might want to autoboot.
116 * Not autobooting, go manual
118 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n");
119 if (getenv("prompt") == NULL
)
120 setenv("prompt", "${interpret}", 1);
121 if (getenv("interpret") == NULL
)
122 setenv("interpret", "ok", 1);
125 while ((input
= linenoise(prompt())) != NULL
) {
126 bf_vm
->sourceId
.i
= 0;
128 linenoiseHistoryAdd(input
);
134 * Read commands from a file, then execute them.
136 * We store the commands in memory and close the source file so that the media
137 * holding it can safely go away while we are executing.
139 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so
140 * that the script won't stop if they fail).
142 COMMAND_SET(include
, "include", "read commands from a file", command_include
);
145 command_include(int argc
, char *argv
[])
152 * Since argv is static, we need to save it here.
154 argvbuf
= (char**) calloc((u_int
)argc
, sizeof(char*));
155 for (i
= 0; i
< argc
; i
++)
156 argvbuf
[i
] = strdup(argv
[i
]);
159 for (i
= 1; (i
< argc
) && (res
== CMD_OK
); i
++)
160 res
= include(argvbuf
[i
]);
162 for (i
= 0; i
< argc
; i
++)
170 * Header prepended to each line. The text immediately follows the header.
171 * We try to make this short in order to save memory -- the loader has
172 * limited memory available, and some of the forth files are very long.
176 struct includeline
*next
;
182 * the PXE tftp service allows to open exactly one connection at the time,
183 * so we need to read included file to memory, then process line by line
184 * as it may contain embedded include commands.
187 include(const char *filename
)
189 struct includeline
*script
, *se
, *sp
;
191 int prevsrcid
, fd
, line
;
192 char *cp
, input
[256]; /* big enough? */
194 if (((fd
= open(filename
, O_RDONLY
)) == -1)) {
195 snprintf(command_errbuf
, sizeof (command_errbuf
), "can't open '%s': %s",
196 filename
, strerror(errno
));
200 * Read the script into memory.
205 while (fgetstr(input
, sizeof(input
), fd
) >= 0) {
208 /* Allocate script line structure and copy line, flags */
210 continue; /* ignore empty line, save memory */
211 if (cp
[0] == '\\' && cp
[1] == ' ')
212 continue; /* ignore comment */
214 sp
= malloc(sizeof(struct includeline
) + strlen(cp
) + 1);
215 /* On malloc failure (it happens!), free as much as possible and exit */
217 while (script
!= NULL
) {
219 script
= script
->next
;
222 snprintf(command_errbuf
, sizeof (command_errbuf
),
223 "file '%s' line %d: memory allocation failure - aborting",
228 strcpy(sp
->text
, cp
);
232 if (script
== NULL
) {
245 prevsrcid
= bf_vm
->sourceId
.i
;
246 bf_vm
->sourceId
.i
= fd
+1; /* 0 is user input device */
250 for (sp
= script
; sp
!= NULL
; sp
= sp
->next
) {
251 res
= bf_run(sp
->text
);
252 if (res
!= FICL_VM_STATUS_OUT_OF_TEXT
) {
253 snprintf(command_errbuf
, sizeof (command_errbuf
),
254 "Error while including %s, in the line %d:\n%s",
255 filename
, sp
->line
, sp
->text
);
262 bf_vm
->sourceId
.i
= -1;
264 bf_vm
->sourceId
.i
= prevsrcid
;
266 while(script
!= NULL
) {
268 script
= script
->next
;
276 * Emit the current prompt; use the same syntax as the parser
277 * for embedding environment variables.
282 static char promptbuf
[20]; /* probably too large, but well... */
283 char *pr
, *p
, *cp
, *ev
;
286 if ((cp
= getenv("prompt")) == NULL
)
287 cp
= (char *)(uintptr_t)">";
291 if ((*p
== '$') && (*(p
+1) == '{')) {
292 for (cp
= p
+ 2; (*cp
!= 0) && (*cp
!= '}'); cp
++)
298 n
= sprintf(promptbuf
+n
, "%s", ev
);
305 if (promptbuf
[n
- 1] != ' ')
306 promptbuf
[n
++] = ' ';