xref: /freebsd/stand/common/interp.c (revision 6bc860372dd7b2be0227ca213af631bdc51c2339)
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  */
26ba25195eSWarner Losh 
27ca987d46SWarner Losh #include <sys/cdefs.h>
28ca987d46SWarner Losh __FBSDID("$FreeBSD$");
29ca987d46SWarner Losh 
30ca987d46SWarner Losh /*
31ca987d46SWarner Losh  * Simple commandline interpreter, toplevel and misc.
32ca987d46SWarner Losh  *
33ca987d46SWarner Losh  * XXX may be obsoleted by BootFORTH or some other, better, interpreter.
34ca987d46SWarner Losh  */
35ca987d46SWarner Losh 
36ca987d46SWarner Losh #include <stand.h>
37ca987d46SWarner Losh #include <string.h>
38ca987d46SWarner Losh #include "bootstrap.h"
39ca987d46SWarner Losh 
40ba25195eSWarner Losh #ifdef BOOT_FORTH
41ba25195eSWarner Losh #include "ficl.h"
42ba25195eSWarner Losh extern FICL_VM *bf_vm;
43ba25195eSWarner Losh #endif
44ca987d46SWarner Losh 
45ca987d46SWarner Losh #define	MAXARGS	20			/* maximum number of arguments allowed */
46ca987d46SWarner Losh 
47ba25195eSWarner Losh static void	prompt(void);
48ca987d46SWarner Losh 
49ba25195eSWarner Losh #ifndef BOOT_FORTH
50ca987d46SWarner Losh /*
51fb5af39aSWarner Losh  * Perform the command
52ca987d46SWarner Losh  */
530ff3f28bSWarner Losh static int
54fb5af39aSWarner Losh perform(int argc, char *argv[])
55ca987d46SWarner Losh {
56fb5af39aSWarner Losh     int				result;
57fb5af39aSWarner Losh     struct bootblk_command	**cmdp;
58fb5af39aSWarner Losh     bootblk_cmd_t		*cmd;
59ca987d46SWarner Losh 
60fb5af39aSWarner Losh     if (argc < 1)
61fb5af39aSWarner Losh 	return(CMD_OK);
62ca987d46SWarner Losh 
63fb5af39aSWarner Losh     /* set return defaults; a successful command will override these */
64fb5af39aSWarner Losh     command_errmsg = command_errbuf;
65fb5af39aSWarner Losh     strcpy(command_errbuf, "no error message");
66fb5af39aSWarner Losh     cmd = NULL;
67fb5af39aSWarner Losh     result = CMD_ERROR;
68ca987d46SWarner Losh 
69fb5af39aSWarner Losh     /* search the command set for the command */
70fb5af39aSWarner Losh     SET_FOREACH(cmdp, Xcommand_set) {
71fb5af39aSWarner Losh 	if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name))
72fb5af39aSWarner Losh 	    cmd = (*cmdp)->c_fn;
73ca987d46SWarner Losh     }
74fb5af39aSWarner Losh     if (cmd != NULL) {
75fb5af39aSWarner Losh 	result = (cmd)(argc, argv);
76ca987d46SWarner Losh     } else {
77fb5af39aSWarner Losh 	command_errmsg = "unknown command";
78ca987d46SWarner Losh     }
790ff3f28bSWarner Losh     return(result);
80ba25195eSWarner Losh }
81ba25195eSWarner Losh #endif	/* ! BOOT_FORTH */
82ba25195eSWarner Losh 
83ba25195eSWarner Losh /*
84ba25195eSWarner Losh  * Interactive mode
85ba25195eSWarner Losh  */
86ba25195eSWarner Losh void
87*6bc86037SWarner Losh interact(void)
88ba25195eSWarner Losh {
89ba25195eSWarner Losh     static char	input[256];			/* big enough? */
90ba25195eSWarner Losh #ifndef BOOT_FORTH
91ba25195eSWarner Losh     int		argc;
92ba25195eSWarner Losh     char	**argv;
93ba25195eSWarner Losh #endif
94ba25195eSWarner Losh 
95ba25195eSWarner Losh #ifdef BOOT_FORTH
96*6bc86037SWarner Losh     bf_init();
97ba25195eSWarner Losh #endif
98ba25195eSWarner Losh 
99ba25195eSWarner Losh     /* Read our default configuration. */
100ba25195eSWarner Losh     include("/boot/loader.rc");
101ba25195eSWarner Losh 
102ba25195eSWarner Losh     printf("\n");
103ba25195eSWarner Losh 
104ba25195eSWarner Losh     /*
105ba25195eSWarner Losh      * Before interacting, we might want to autoboot.
106ba25195eSWarner Losh      */
107ba25195eSWarner Losh     autoboot_maybe();
108ba25195eSWarner Losh 
109ba25195eSWarner Losh     /*
110ba25195eSWarner Losh      * Not autobooting, go manual
111ba25195eSWarner Losh      */
112ba25195eSWarner Losh     printf("\nType '?' for a list of commands, 'help' for more detailed help.\n");
113ba25195eSWarner Losh     if (getenv("prompt") == NULL)
114ba25195eSWarner Losh 	setenv("prompt", "${interpret}", 1);
115ba25195eSWarner Losh     if (getenv("interpret") == NULL)
116ba25195eSWarner Losh         setenv("interpret", "OK", 1);
117ba25195eSWarner Losh 
118ba25195eSWarner Losh 
119ba25195eSWarner Losh     for (;;) {
120ba25195eSWarner Losh 	input[0] = '\0';
121ba25195eSWarner Losh 	prompt();
122ba25195eSWarner Losh 	ngets(input, sizeof(input));
123ba25195eSWarner Losh #ifdef BOOT_FORTH
124ba25195eSWarner Losh 	bf_vm->sourceID.i = 0;
125ba25195eSWarner Losh 	bf_run(input);
126ba25195eSWarner Losh #else
127ba25195eSWarner Losh 	if (!parse(&argc, &argv, input)) {
128ba25195eSWarner Losh 	    if (perform(argc, argv))
129ba25195eSWarner Losh 		printf("%s: %s\n", argv[0], command_errmsg);
130ba25195eSWarner Losh 	    free(argv);
131ba25195eSWarner Losh 	} else {
132ba25195eSWarner Losh 	    printf("parse error\n");
133ba25195eSWarner Losh 	}
134ba25195eSWarner Losh #endif
135ba25195eSWarner Losh     }
136ba25195eSWarner Losh }
137ba25195eSWarner Losh 
138ba25195eSWarner Losh /*
139ba25195eSWarner Losh  * Read commands from a file, then execute them.
140ba25195eSWarner Losh  *
141ba25195eSWarner Losh  * We store the commands in memory and close the source file so that the media
142ba25195eSWarner Losh  * holding it can safely go away while we are executing.
143ba25195eSWarner Losh  *
144ba25195eSWarner Losh  * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so
145ba25195eSWarner Losh  * that the script won't stop if they fail).
146ba25195eSWarner Losh  */
147ba25195eSWarner Losh COMMAND_SET(include, "include", "read commands from a file", command_include);
148ba25195eSWarner Losh 
149ba25195eSWarner Losh static int
150ba25195eSWarner Losh command_include(int argc, char *argv[])
151ba25195eSWarner Losh {
152ba25195eSWarner Losh     int		i;
153ba25195eSWarner Losh     int		res;
154ba25195eSWarner Losh     char	**argvbuf;
155ba25195eSWarner Losh 
156ba25195eSWarner Losh     /*
157ba25195eSWarner Losh      * Since argv is static, we need to save it here.
158ba25195eSWarner Losh      */
159ba25195eSWarner Losh     argvbuf = (char**) calloc((u_int)argc, sizeof(char*));
160ba25195eSWarner Losh     for (i = 0; i < argc; i++)
161ba25195eSWarner Losh 	argvbuf[i] = strdup(argv[i]);
162ba25195eSWarner Losh 
163ba25195eSWarner Losh     res=CMD_OK;
164ba25195eSWarner Losh     for (i = 1; (i < argc) && (res == CMD_OK); i++)
165ba25195eSWarner Losh 	res = include(argvbuf[i]);
166ba25195eSWarner Losh 
167ba25195eSWarner Losh     for (i = 0; i < argc; i++)
168ba25195eSWarner Losh 	free(argvbuf[i]);
169ba25195eSWarner Losh     free(argvbuf);
170ba25195eSWarner Losh 
171ba25195eSWarner Losh     return(res);
172ba25195eSWarner Losh }
173ba25195eSWarner Losh 
174ba25195eSWarner Losh /*
175ba25195eSWarner Losh  * Header prepended to each line. The text immediately follows the header.
176ba25195eSWarner Losh  * We try to make this short in order to save memory -- the loader has
177ba25195eSWarner Losh  * limited memory available, and some of the forth files are very long.
178ba25195eSWarner Losh  */
179ba25195eSWarner Losh struct includeline
180ba25195eSWarner Losh {
181ba25195eSWarner Losh     struct includeline	*next;
182ba25195eSWarner Losh #ifndef BOOT_FORTH
183ba25195eSWarner Losh     int			flags;
184ba25195eSWarner Losh     int			line;
185ba25195eSWarner Losh #define SL_QUIET	(1<<0)
186ba25195eSWarner Losh #define SL_IGNOREERR	(1<<1)
187ba25195eSWarner Losh #endif
188ba25195eSWarner Losh     char		text[0];
189ba25195eSWarner Losh };
190ba25195eSWarner Losh 
191ba25195eSWarner Losh int
192ba25195eSWarner Losh include(const char *filename)
193ba25195eSWarner Losh {
194ba25195eSWarner Losh     struct includeline	*script, *se, *sp;
195ba25195eSWarner Losh     char		input[256];			/* big enough? */
196ba25195eSWarner Losh #ifdef BOOT_FORTH
197ba25195eSWarner Losh     int			res;
198ba25195eSWarner Losh     char		*cp;
199ba25195eSWarner Losh     int			prevsrcid, fd, line;
200ba25195eSWarner Losh #else
201ba25195eSWarner Losh     int			argc,res;
202ba25195eSWarner Losh     char		**argv, *cp;
203ba25195eSWarner Losh     int			fd, flags, line;
204ba25195eSWarner Losh #endif
205ba25195eSWarner Losh 
206ba25195eSWarner Losh     if (((fd = open(filename, O_RDONLY)) == -1)) {
207ba25195eSWarner Losh 	snprintf(command_errbuf, sizeof(command_errbuf),
208ba25195eSWarner Losh 	    "can't open '%s': %s", filename, strerror(errno));
209ba25195eSWarner Losh 	return(CMD_ERROR);
210ba25195eSWarner Losh     }
211ba25195eSWarner Losh 
212ba25195eSWarner Losh     /*
213ba25195eSWarner Losh      * Read the script into memory.
214ba25195eSWarner Losh      */
215ba25195eSWarner Losh     script = se = NULL;
216ba25195eSWarner Losh     line = 0;
217ba25195eSWarner Losh 
218ba25195eSWarner Losh     while (fgetstr(input, sizeof(input), fd) >= 0) {
219ba25195eSWarner Losh 	line++;
220ba25195eSWarner Losh #ifdef BOOT_FORTH
221ba25195eSWarner Losh 	cp = input;
222ba25195eSWarner Losh #else
223ba25195eSWarner Losh 	flags = 0;
224ba25195eSWarner Losh 	/* Discard comments */
225ba25195eSWarner Losh 	if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0)
226ba25195eSWarner Losh 	    continue;
227ba25195eSWarner Losh 	cp = input;
228ba25195eSWarner Losh 	/* Echo? */
229ba25195eSWarner Losh 	if (input[0] == '@') {
230ba25195eSWarner Losh 	    cp++;
231ba25195eSWarner Losh 	    flags |= SL_QUIET;
232ba25195eSWarner Losh 	}
233ba25195eSWarner Losh 	/* Error OK? */
234ba25195eSWarner Losh 	if (input[0] == '-') {
235ba25195eSWarner Losh 	    cp++;
236ba25195eSWarner Losh 	    flags |= SL_IGNOREERR;
237ba25195eSWarner Losh 	}
238ba25195eSWarner Losh #endif
239ba25195eSWarner Losh 	/* Allocate script line structure and copy line, flags */
240ba25195eSWarner Losh 	if (*cp == '\0')
241ba25195eSWarner Losh 		continue;	/* ignore empty line, save memory */
242ba25195eSWarner Losh 	sp = malloc(sizeof(struct includeline) + strlen(cp) + 1);
243ba25195eSWarner Losh 	/* On malloc failure (it happens!), free as much as possible and exit */
244ba25195eSWarner Losh 	if (sp == NULL) {
245ba25195eSWarner Losh 		while (script != NULL) {
246ba25195eSWarner Losh 			se = script;
247ba25195eSWarner Losh 			script = script->next;
248ba25195eSWarner Losh 			free(se);
249ba25195eSWarner Losh 		}
250ba25195eSWarner Losh 		snprintf(command_errbuf, sizeof(command_errbuf),
251ba25195eSWarner Losh 		    "file '%s' line %d: memory allocation failure - aborting",
252ba25195eSWarner Losh 		    filename, line);
253ba25195eSWarner Losh 		return (CMD_ERROR);
254ba25195eSWarner Losh 	}
255ba25195eSWarner Losh 	strcpy(sp->text, cp);
256ba25195eSWarner Losh #ifndef BOOT_FORTH
257ba25195eSWarner Losh 	sp->flags = flags;
258ba25195eSWarner Losh 	sp->line = line;
259ba25195eSWarner Losh #endif
260ba25195eSWarner Losh 	sp->next = NULL;
261ba25195eSWarner Losh 
262ba25195eSWarner Losh 	if (script == NULL) {
263ba25195eSWarner Losh 	    script = sp;
264ba25195eSWarner Losh 	} else {
265ba25195eSWarner Losh 	    se->next = sp;
266ba25195eSWarner Losh 	}
267ba25195eSWarner Losh 	se = sp;
268ba25195eSWarner Losh     }
269ba25195eSWarner Losh     close(fd);
270ba25195eSWarner Losh 
271ba25195eSWarner Losh     /*
272ba25195eSWarner Losh      * Execute the script
273ba25195eSWarner Losh      */
274ba25195eSWarner Losh #ifndef BOOT_FORTH
275ba25195eSWarner Losh     argv = NULL;
276ba25195eSWarner Losh #else
277ba25195eSWarner Losh     prevsrcid = bf_vm->sourceID.i;
278ba25195eSWarner Losh     bf_vm->sourceID.i = fd;
279ba25195eSWarner Losh #endif
280ba25195eSWarner Losh     res = CMD_OK;
281ba25195eSWarner Losh     for (sp = script; sp != NULL; sp = sp->next) {
282ba25195eSWarner Losh 
283ba25195eSWarner Losh #ifdef BOOT_FORTH
284ba25195eSWarner Losh 	res = bf_run(sp->text);
285ba25195eSWarner Losh 	if (res != VM_OUTOFTEXT) {
286ba25195eSWarner Losh 		snprintf(command_errbuf, sizeof(command_errbuf),
287ba25195eSWarner Losh 		    "Error while including %s, in the line:\n%s",
288ba25195eSWarner Losh 		    filename, sp->text);
289ba25195eSWarner Losh 		res = CMD_ERROR;
290ba25195eSWarner Losh 		break;
291ba25195eSWarner Losh 	} else
292ba25195eSWarner Losh 		res = CMD_OK;
293ba25195eSWarner Losh #else
294ba25195eSWarner Losh 	/* print if not being quiet */
295ba25195eSWarner Losh 	if (!(sp->flags & SL_QUIET)) {
296ba25195eSWarner Losh 	    prompt();
297ba25195eSWarner Losh 	    printf("%s\n", sp->text);
298ba25195eSWarner Losh 	}
299ba25195eSWarner Losh 
300ba25195eSWarner Losh 	/* Parse the command */
301ba25195eSWarner Losh 	if (!parse(&argc, &argv, sp->text)) {
302ba25195eSWarner Losh 	    if ((argc > 0) && (perform(argc, argv) != 0)) {
303ba25195eSWarner Losh 		/* normal command */
304ba25195eSWarner Losh 		printf("%s: %s\n", argv[0], command_errmsg);
305ba25195eSWarner Losh 		if (!(sp->flags & SL_IGNOREERR)) {
306ba25195eSWarner Losh 		    res=CMD_ERROR;
307ba25195eSWarner Losh 		    break;
308ba25195eSWarner Losh 		}
309ba25195eSWarner Losh 	    }
310ba25195eSWarner Losh 	    free(argv);
311ba25195eSWarner Losh 	    argv = NULL;
312ba25195eSWarner Losh 	} else {
313ba25195eSWarner Losh 	    printf("%s line %d: parse error\n", filename, sp->line);
314ba25195eSWarner Losh 	    res=CMD_ERROR;
315ba25195eSWarner Losh 	    break;
316ba25195eSWarner Losh 	}
317ba25195eSWarner Losh #endif
318ba25195eSWarner Losh     }
319ba25195eSWarner Losh #ifndef BOOT_FORTH
320ba25195eSWarner Losh     if (argv != NULL)
321ba25195eSWarner Losh 	free(argv);
322ba25195eSWarner Losh #else
323ba25195eSWarner Losh     bf_vm->sourceID.i = prevsrcid;
324ba25195eSWarner Losh #endif
325ba25195eSWarner Losh     while(script != NULL) {
326ba25195eSWarner Losh 	se = script;
327ba25195eSWarner Losh 	script = script->next;
328ba25195eSWarner Losh 	free(se);
329ba25195eSWarner Losh     }
330ba25195eSWarner Losh     return(res);
331ca987d46SWarner Losh }
332ca987d46SWarner Losh 
333ca987d46SWarner Losh /*
334ca987d46SWarner Losh  * Emit the current prompt; use the same syntax as the parser
335ca987d46SWarner Losh  * for embedding environment variables.
336ca987d46SWarner Losh  */
337ba25195eSWarner Losh static void
338ca987d46SWarner Losh prompt(void)
339ca987d46SWarner Losh {
340ca987d46SWarner Losh     char	*pr, *p, *cp, *ev;
341ca987d46SWarner Losh 
342ca987d46SWarner Losh     if ((cp = getenv("prompt")) == NULL)
343ca987d46SWarner Losh 	cp = ">";
344ca987d46SWarner Losh     pr = p = strdup(cp);
345ca987d46SWarner Losh 
346ca987d46SWarner Losh     while (*p != 0) {
347ca987d46SWarner Losh 	if ((*p == '$') && (*(p+1) == '{')) {
348ca987d46SWarner Losh 	    for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++)
349ca987d46SWarner Losh 		;
350ca987d46SWarner Losh 	    *cp = 0;
351ca987d46SWarner Losh 	    ev = getenv(p + 2);
352ca987d46SWarner Losh 
353ca987d46SWarner Losh 	    if (ev != NULL)
354ca987d46SWarner Losh 		printf("%s", ev);
355ca987d46SWarner Losh 	    p = cp + 1;
356ca987d46SWarner Losh 	    continue;
357ca987d46SWarner Losh 	}
358ca987d46SWarner Losh 	putchar(*p++);
359ca987d46SWarner Losh     }
360ca987d46SWarner Losh     putchar(' ');
361ca987d46SWarner Losh     free(pr);
362ca987d46SWarner Losh }
363