138465Smsmith/*-
238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
338465Smsmith * All rights reserved.
438465Smsmith *
538465Smsmith * Redistribution and use in source and binary forms, with or without
638465Smsmith * modification, are permitted provided that the following conditions
738465Smsmith * are met:
838465Smsmith * 1. Redistributions of source code must retain the above copyright
938465Smsmith *    notice, this list of conditions and the following disclaimer.
1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138465Smsmith *    notice, this list of conditions and the following disclaimer in the
1238465Smsmith *    documentation and/or other materials provided with the distribution.
1338465Smsmith *
1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738465Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438465Smsmith * SUCH DAMAGE.
2538465Smsmith */
26119483Sobrien
27119483Sobrien#include <sys/cdefs.h>
28119483Sobrien__FBSDID("$FreeBSD$");
29119483Sobrien
3038465Smsmith/*
3138716Smsmith * Simple commandline interpreter, toplevel and misc.
3238465Smsmith *
3340015Smsmith * XXX may be obsoleted by BootFORTH or some other, better, interpreter.
3438465Smsmith */
3538465Smsmith
3638465Smsmith#include <stand.h>
3738465Smsmith#include <string.h>
3838465Smsmith#include "bootstrap.h"
3938465Smsmith
4042000Sabial#ifdef BOOT_FORTH
4142000Sabial#include "ficl.h"
4251786Sdcs#define	RETURN(x)	stackPushINT(bf_vm->pStack,!x); return(x)
4342000Sabial
4442000Sabialextern FICL_VM *bf_vm;
4542000Sabial#else
4642000Sabial#define	RETURN(x)	return(x)
4742000Sabial#endif
4842000Sabial
4938465Smsmith#define	MAXARGS	20			/* maximum number of arguments allowed */
5038465Smsmith
5138465Smsmithstatic void	prompt(void);
5238465Smsmith
5364220Sjhb#ifndef BOOT_FORTH
5464220Sjhbstatic int	perform(int argc, char *argv[]);
5564220Sjhb
5638465Smsmith/*
5764220Sjhb * Perform the command
5864220Sjhb */
5964220Sjhbint
6064220Sjhbperform(int argc, char *argv[])
6164220Sjhb{
6264220Sjhb    int				result;
6364220Sjhb    struct bootblk_command	**cmdp;
6464220Sjhb    bootblk_cmd_t		*cmd;
6564220Sjhb
6664220Sjhb    if (argc < 1)
6764220Sjhb	return(CMD_OK);
6864220Sjhb
6964220Sjhb    /* set return defaults; a successful command will override these */
7064220Sjhb    command_errmsg = command_errbuf;
7164220Sjhb    strcpy(command_errbuf, "no error message");
7264220Sjhb    cmd = NULL;
7364220Sjhb    result = CMD_ERROR;
7464220Sjhb
7564220Sjhb    /* search the command set for the command */
7664220Sjhb    SET_FOREACH(cmdp, Xcommand_set) {
7764220Sjhb	if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name))
7864220Sjhb	    cmd = (*cmdp)->c_fn;
7964220Sjhb    }
8064220Sjhb    if (cmd != NULL) {
8164220Sjhb	result = (cmd)(argc, argv);
8264220Sjhb    } else {
8364220Sjhb	command_errmsg = "unknown command";
8464220Sjhb    }
8564220Sjhb    RETURN(result);
8664220Sjhb}
8764220Sjhb#endif	/* ! BOOT_FORTH */
8864220Sjhb
8964220Sjhb/*
9038465Smsmith * Interactive mode
9138465Smsmith */
9238465Smsmithvoid
9338465Smsmithinteract(void)
9438465Smsmith{
95185132Sluigi    static char	input[256];			/* big enough? */
9642289Speter#ifndef BOOT_FORTH
9738465Smsmith    int		argc;
9838465Smsmith    char	**argv;
9942289Speter#endif
10038465Smsmith
10140875Smsmith#ifdef BOOT_FORTH
10240875Smsmith    bf_init();
10340875Smsmith#endif
10440875Smsmith
10538764Smsmith    /*
10638764Smsmith     * Read our default configuration
10738764Smsmith     */
108221601Savg    if (include("/boot/loader.rc") != CMD_OK)
10943077Smsmith	include("/boot/boot.conf");
11038764Smsmith    printf("\n");
11138764Smsmith    /*
11240015Smsmith     * Before interacting, we might want to autoboot.
11338764Smsmith     */
11440015Smsmith    autoboot_maybe();
11540015Smsmith
11638764Smsmith    /*
11738764Smsmith     * Not autobooting, go manual
11838764Smsmith     */
11938764Smsmith    printf("\nType '?' for a list of commands, 'help' for more detailed help.\n");
12045867Sjoerg    if (getenv("prompt") == NULL)
12153786Sdcs	setenv("prompt", "${interpret}", 1);
12252748Sdcs    if (getenv("interpret") == NULL)
12386608Sobrien        setenv("interpret", "OK", 1);
12438764Smsmith
12538764Smsmith
12638465Smsmith    for (;;) {
12738465Smsmith	input[0] = '\0';
12838465Smsmith	prompt();
12938465Smsmith	ngets(input, sizeof(input));
13040875Smsmith#ifdef BOOT_FORTH
13161693Sdcs	bf_vm->sourceID.i = 0;
13240875Smsmith	bf_run(input);
13340875Smsmith#else
13438716Smsmith	if (!parse(&argc, &argv, input)) {
13538716Smsmith	    if (perform(argc, argv))
13638465Smsmith		printf("%s: %s\n", argv[0], command_errmsg);
13738716Smsmith	    free(argv);
13838716Smsmith	} else {
13938716Smsmith	    printf("parse error\n");
14038716Smsmith	}
14140875Smsmith#endif
14238465Smsmith    }
14338465Smsmith}
14438465Smsmith
14538465Smsmith/*
14640015Smsmith * Read commands from a file, then execute them.
14740015Smsmith *
14840015Smsmith * We store the commands in memory and close the source file so that the media
14940015Smsmith * holding it can safely go away while we are executing.
15040015Smsmith *
15140015Smsmith * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so
15240015Smsmith * that the script won't stop if they fail).
15338465Smsmith */
15443077SmsmithCOMMAND_SET(include, "include", "read commands from a file", command_include);
15538465Smsmith
15638465Smsmithstatic int
15743077Smsmithcommand_include(int argc, char *argv[])
15838465Smsmith{
15938465Smsmith    int		i;
16042682Sabial    int		res;
16144210Sdcs    char	**argvbuf;
16238465Smsmith
16344210Sdcs    /*
16444210Sdcs     * Since argv is static, we need to save it here.
16544210Sdcs     */
16664187Sjhb    argvbuf = (char**) calloc((u_int)argc, sizeof(char*));
16744210Sdcs    for (i = 0; i < argc; i++)
16844210Sdcs	argvbuf[i] = strdup(argv[i]);
16944210Sdcs
17042682Sabial    res=CMD_OK;
17142682Sabial    for (i = 1; (i < argc) && (res == CMD_OK); i++)
17244210Sdcs	res = include(argvbuf[i]);
17344210Sdcs
17444210Sdcs    for (i = 0; i < argc; i++)
17544210Sdcs	free(argvbuf[i]);
17644210Sdcs    free(argvbuf);
17744210Sdcs
17842682Sabial    return(res);
17938465Smsmith}
18038465Smsmith
181185132Sluigi/*
182185132Sluigi * Header prepended to each line. The text immediately follows the header.
183185132Sluigi * We try to make this short in order to save memory -- the loader has
184185132Sluigi * limited memory available, and some of the forth files are very long.
185185132Sluigi */
18643077Smsmithstruct includeline
18740015Smsmith{
188185132Sluigi    struct includeline	*next;
189185132Sluigi#ifndef BOOT_FORTH
19040015Smsmith    int			flags;
19140015Smsmith    int			line;
19240015Smsmith#define SL_QUIET	(1<<0)
19340015Smsmith#define SL_IGNOREERR	(1<<1)
194185132Sluigi#endif
195185132Sluigi    char		text[0];
19640015Smsmith};
19740015Smsmith
19842682Sabialint
19964187Sjhbinclude(const char *filename)
20038465Smsmith{
20143077Smsmith    struct includeline	*script, *se, *sp;
20240015Smsmith    char		input[256];			/* big enough? */
20343614Sdcs#ifdef BOOT_FORTH
20443614Sdcs    int			res;
20543614Sdcs    char		*cp;
20661693Sdcs    int			prevsrcid, fd, line;
20743614Sdcs#else
20842682Sabial    int			argc,res;
20940015Smsmith    char		**argv, *cp;
21040015Smsmith    int			fd, flags, line;
21143614Sdcs#endif
21238465Smsmith
21338465Smsmith    if (((fd = open(filename, O_RDONLY)) == -1)) {
214242084Smav	sprintf(command_errbuf,"can't open '%s': %s", filename, strerror(errno));
21542682Sabial	return(CMD_ERROR);
21640015Smsmith    }
21738465Smsmith
21840015Smsmith    /*
21940015Smsmith     * Read the script into memory.
22040015Smsmith     */
22140015Smsmith    script = se = NULL;
22240015Smsmith    line = 0;
22340015Smsmith
22444427Sdcs    while (fgetstr(input, sizeof(input), fd) >= 0) {
22540015Smsmith	line++;
22643614Sdcs#ifdef BOOT_FORTH
22743614Sdcs	cp = input;
22843614Sdcs#else
22940015Smsmith	flags = 0;
23040015Smsmith	/* Discard comments */
23165683Sdcs	if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0)
23240015Smsmith	    continue;
23340015Smsmith	cp = input;
23440015Smsmith	/* Echo? */
23540015Smsmith	if (input[0] == '@') {
23640015Smsmith	    cp++;
23740015Smsmith	    flags |= SL_QUIET;
23840015Smsmith	}
23940015Smsmith	/* Error OK? */
24040015Smsmith	if (input[0] == '-') {
24140015Smsmith	    cp++;
24240015Smsmith	    flags |= SL_IGNOREERR;
24340015Smsmith	}
24443614Sdcs#endif
24540015Smsmith	/* Allocate script line structure and copy line, flags */
246185132Sluigi	if (*cp == '\0')
247185132Sluigi		continue;	/* ignore empty line, save memory */
24843077Smsmith	sp = malloc(sizeof(struct includeline) + strlen(cp) + 1);
249218974Sbrucec	/* On malloc failure (it happens!), free as much as possible and exit */
250218974Sbrucec	if (sp == NULL) {
251218974Sbrucec		while (script != NULL) {
252218974Sbrucec			se = script;
253218974Sbrucec			script = script->next;
254218974Sbrucec			free(se);
255218974Sbrucec		}
256218974Sbrucec		sprintf(command_errbuf, "file '%s' line %d: memory allocation "
257242084Smav		    "failure - aborting", filename, line);
258218974Sbrucec		return (CMD_ERROR);
259218974Sbrucec	}
26040106Smsmith	strcpy(sp->text, cp);
26143614Sdcs#ifndef BOOT_FORTH
26240015Smsmith	sp->flags = flags;
263185132Sluigi	sp->line = line;
26443614Sdcs#endif
26540015Smsmith	sp->next = NULL;
26640015Smsmith
26740015Smsmith	if (script == NULL) {
26840015Smsmith	    script = sp;
26940015Smsmith	} else {
27040015Smsmith	    se->next = sp;
27140015Smsmith	}
27240015Smsmith	se = sp;
27340015Smsmith    }
27440015Smsmith    close(fd);
27540015Smsmith
27640015Smsmith    /*
27740015Smsmith     * Execute the script
27840015Smsmith     */
27943614Sdcs#ifndef BOOT_FORTH
28040015Smsmith    argv = NULL;
28161693Sdcs#else
28261693Sdcs    prevsrcid = bf_vm->sourceID.i;
28361693Sdcs    bf_vm->sourceID.i = fd;
28443614Sdcs#endif
28542682Sabial    res = CMD_OK;
28640015Smsmith    for (sp = script; sp != NULL; sp = sp->next) {
28740015Smsmith
28843614Sdcs#ifdef BOOT_FORTH
28943614Sdcs	res = bf_run(sp->text);
29043614Sdcs	if (res != VM_OUTOFTEXT) {
29144210Sdcs		sprintf(command_errbuf, "Error while including %s, in the line:\n%s", filename, sp->text);
29243614Sdcs		res = CMD_ERROR;
29343614Sdcs		break;
29443614Sdcs	} else
29543614Sdcs		res = CMD_OK;
29643614Sdcs#else
29740015Smsmith	/* print if not being quiet */
29840015Smsmith	if (!(sp->flags & SL_QUIET)) {
29940015Smsmith	    prompt();
30040015Smsmith	    printf("%s\n", sp->text);
30140015Smsmith	}
30238465Smsmith
30340015Smsmith	/* Parse the command */
30440015Smsmith	if (!parse(&argc, &argv, sp->text)) {
30540106Smsmith	    if ((argc > 0) && (perform(argc, argv) != 0)) {
30640015Smsmith		/* normal command */
30740015Smsmith		printf("%s: %s\n", argv[0], command_errmsg);
30842682Sabial		if (!(sp->flags & SL_IGNOREERR)) {
30942682Sabial		    res=CMD_ERROR;
31040015Smsmith		    break;
31142682Sabial		}
31238716Smsmith	    }
31340015Smsmith	    free(argv);
31440015Smsmith	    argv = NULL;
31540015Smsmith	} else {
31640015Smsmith	    printf("%s line %d: parse error\n", filename, sp->line);
31742682Sabial	    res=CMD_ERROR;
31840015Smsmith	    break;
31938465Smsmith	}
32043614Sdcs#endif
32138465Smsmith    }
32243614Sdcs#ifndef BOOT_FORTH
32340015Smsmith    if (argv != NULL)
32440015Smsmith	free(argv);
32561693Sdcs#else
32661693Sdcs    bf_vm->sourceID.i = prevsrcid;
32743614Sdcs#endif
32840015Smsmith    while(script != NULL) {
32940015Smsmith	se = script;
33040015Smsmith	script = script->next;
33140015Smsmith	free(se);
33240015Smsmith    }
33342682Sabial    return(res);
33438465Smsmith}
33538465Smsmith
33638465Smsmith/*
33738764Smsmith * Emit the current prompt; use the same syntax as the parser
33838764Smsmith * for embedding environment variables.
33938465Smsmith */
34038465Smsmithstatic void
34138465Smsmithprompt(void)
34238465Smsmith{
34344570Sdcs    char	*pr, *p, *cp, *ev;
34438465Smsmith
34538764Smsmith    if ((cp = getenv("prompt")) == NULL)
34638764Smsmith	cp = ">";
34744570Sdcs    pr = p = strdup(cp);
34838465Smsmith
34938465Smsmith    while (*p != 0) {
35038764Smsmith	if ((*p == '$') && (*(p+1) == '{')) {
35138764Smsmith	    for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++)
35238465Smsmith		;
35338465Smsmith	    *cp = 0;
35438764Smsmith	    ev = getenv(p + 2);
35538465Smsmith
35638764Smsmith	    if (ev != NULL)
35762874Skris		printf("%s", ev);
35838764Smsmith	    p = cp + 1;
35938764Smsmith	    continue;
36038465Smsmith	}
36138465Smsmith	putchar(*p++);
36238465Smsmith    }
36338465Smsmith    putchar(' ');
36444570Sdcs    free(pr);
36538465Smsmith}
366