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