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