xref: /titanic_52/usr/src/common/ficl/emu/loader_emu.c (revision 3451cb4f9fb6b3e806579a500b64debb920f7a9a)
1a1bf3f78SToomas Soome /*
2a1bf3f78SToomas Soome  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3a1bf3f78SToomas Soome  * All rights reserved.
4a1bf3f78SToomas Soome  *
5a1bf3f78SToomas Soome  * Redistribution and use in source and binary forms, with or without
6a1bf3f78SToomas Soome  * modification, are permitted provided that the following conditions
7a1bf3f78SToomas Soome  * are met:
8a1bf3f78SToomas Soome  * 1. Redistributions of source code must retain the above copyright
9a1bf3f78SToomas Soome  *    notice, this list of conditions and the following disclaimer.
10a1bf3f78SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11a1bf3f78SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
12a1bf3f78SToomas Soome  *    documentation and/or other materials provided with the distribution.
13a1bf3f78SToomas Soome  *
14a1bf3f78SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a1bf3f78SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a1bf3f78SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a1bf3f78SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a1bf3f78SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a1bf3f78SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a1bf3f78SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a1bf3f78SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a1bf3f78SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a1bf3f78SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a1bf3f78SToomas Soome  * SUCH DAMAGE.
25a1bf3f78SToomas Soome  */
26a1bf3f78SToomas Soome 
27a1bf3f78SToomas Soome #include <sys/types.h>
28a1bf3f78SToomas Soome #include <sys/stat.h>
29a1bf3f78SToomas Soome #include <fcntl.h>
30a1bf3f78SToomas Soome #include <errno.h>
31a1bf3f78SToomas Soome #include <stdlib.h>
32a1bf3f78SToomas Soome #include <stdio.h>
33a1bf3f78SToomas Soome #include <string.h>
34a1bf3f78SToomas Soome #include <strings.h>
35a1bf3f78SToomas Soome #include <limits.h>
36a1bf3f78SToomas Soome #include <unistd.h>
37a1bf3f78SToomas Soome #include <dirent.h>
38a1bf3f78SToomas Soome #include <macros.h>
39a1bf3f78SToomas Soome #include <sys/systeminfo.h>
40a1bf3f78SToomas Soome #include <sys/queue.h>
41a1bf3f78SToomas Soome #include <sys/mnttab.h>
42a1bf3f78SToomas Soome #include "ficl.h"
43a1bf3f78SToomas Soome 
44a1bf3f78SToomas Soome /* Commands and return values; nonzero return sets command_errmsg != NULL */
45a1bf3f78SToomas Soome typedef int (bootblk_cmd_t)(int argc, char *argv[]);
46a1bf3f78SToomas Soome #define	CMD_OK		0
47a1bf3f78SToomas Soome #define	CMD_ERROR	1
48a1bf3f78SToomas Soome 
49a1bf3f78SToomas Soome /*
50a1bf3f78SToomas Soome  * Support for commands
51a1bf3f78SToomas Soome  */
52a1bf3f78SToomas Soome struct bootblk_command
53a1bf3f78SToomas Soome {
54a1bf3f78SToomas Soome 	const char *c_name;
55a1bf3f78SToomas Soome 	const char *c_desc;
56a1bf3f78SToomas Soome 	bootblk_cmd_t *c_fn;
57a1bf3f78SToomas Soome 	STAILQ_ENTRY(bootblk_command) next;
58a1bf3f78SToomas Soome };
59a1bf3f78SToomas Soome 
60a1bf3f78SToomas Soome #define	MDIR_REMOVED	0x0001
61a1bf3f78SToomas Soome #define	MDIR_NOHINTS	0x0002
62a1bf3f78SToomas Soome 
63a1bf3f78SToomas Soome struct moduledir {
64a1bf3f78SToomas Soome 	char	*d_path;	/* path of modules directory */
65a1bf3f78SToomas Soome 	uchar_t	*d_hints;	/* content of linker.hints file */
66a1bf3f78SToomas Soome 	int	d_hintsz;	/* size of hints data */
67a1bf3f78SToomas Soome 	int	d_flags;
68a1bf3f78SToomas Soome 	STAILQ_ENTRY(moduledir) d_link;
69a1bf3f78SToomas Soome };
70a1bf3f78SToomas Soome static STAILQ_HEAD(, moduledir) moduledir_list =
71a1bf3f78SToomas Soome     STAILQ_HEAD_INITIALIZER(moduledir_list);
72a1bf3f78SToomas Soome 
73a1bf3f78SToomas Soome static const char *default_searchpath = "/platform/i86pc";
74a1bf3f78SToomas Soome 
75a1bf3f78SToomas Soome static char typestr[] = "?fc?d?b? ?l?s?w";
76a1bf3f78SToomas Soome static int	ls_getdir(char **pathp);
77a1bf3f78SToomas Soome extern char **_environ;
78a1bf3f78SToomas Soome 
79a1bf3f78SToomas Soome char	*command_errmsg;
80a1bf3f78SToomas Soome char	command_errbuf[256];
81a1bf3f78SToomas Soome 
82a1bf3f78SToomas Soome extern void pager_open(void);
83a1bf3f78SToomas Soome extern void pager_close(void);
84a1bf3f78SToomas Soome extern int pager_output(const char *);
85a1bf3f78SToomas Soome extern int pager_file(const char *);
86a1bf3f78SToomas Soome static int page_file(char *);
87a1bf3f78SToomas Soome static int include(const char *);
88a1bf3f78SToomas Soome 
89a1bf3f78SToomas Soome static int command_help(int argc, char *argv[]);
90a1bf3f78SToomas Soome static int command_commandlist(int argc, char *argv[]);
91a1bf3f78SToomas Soome static int command_show(int argc, char *argv[]);
92a1bf3f78SToomas Soome static int command_set(int argc, char *argv[]);
93a1bf3f78SToomas Soome static int command_setprop(int argc, char *argv[]);
94a1bf3f78SToomas Soome static int command_unset(int argc, char *argv[]);
95a1bf3f78SToomas Soome static int command_echo(int argc, char *argv[]);
96a1bf3f78SToomas Soome static int command_read(int argc, char *argv[]);
97a1bf3f78SToomas Soome static int command_more(int argc, char *argv[]);
98a1bf3f78SToomas Soome static int command_ls(int argc, char *argv[]);
99a1bf3f78SToomas Soome static int command_include(int argc, char *argv[]);
100a1bf3f78SToomas Soome static int command_autoboot(int argc, char *argv[]);
101a1bf3f78SToomas Soome static int command_boot(int argc, char *argv[]);
102a1bf3f78SToomas Soome static int command_unload(int argc, char *argv[]);
103a1bf3f78SToomas Soome static int command_load(int argc, char *argv[]);
104a1bf3f78SToomas Soome static int command_reboot(int argc, char *argv[]);
105a1bf3f78SToomas Soome 
106a1bf3f78SToomas Soome #define	BF_PARSE	100
107a1bf3f78SToomas Soome #define	BF_DICTSIZE	30000
108a1bf3f78SToomas Soome 
109a1bf3f78SToomas Soome /* update when loader version will change */
110a1bf3f78SToomas Soome static const char bootprog_rev[] = "1.1";
111a1bf3f78SToomas Soome STAILQ_HEAD(cmdh, bootblk_command) commands;
112a1bf3f78SToomas Soome 
113a1bf3f78SToomas Soome /*
114a1bf3f78SToomas Soome  * BootForth   Interface to Ficl Forth interpreter.
115a1bf3f78SToomas Soome  */
116a1bf3f78SToomas Soome 
117a1bf3f78SToomas Soome ficlSystem *bf_sys;
118a1bf3f78SToomas Soome ficlVm	*bf_vm;
119a1bf3f78SToomas Soome 
120a1bf3f78SToomas Soome /*
121a1bf3f78SToomas Soome  * Redistribution and use in source and binary forms, with or without
122a1bf3f78SToomas Soome  * modification, are permitted provided that the following conditions
123a1bf3f78SToomas Soome  * are met:
124a1bf3f78SToomas Soome  * 1. Redistributions of source code must retain the above copyright
125a1bf3f78SToomas Soome  *    notice, this list of conditions and the following disclaimer.
126a1bf3f78SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
127a1bf3f78SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
128a1bf3f78SToomas Soome  *    documentation and/or other materials provided with the distribution.
129a1bf3f78SToomas Soome  *
130a1bf3f78SToomas Soome  * Jordan K. Hubbard
131a1bf3f78SToomas Soome  * 29 August 1998
132a1bf3f78SToomas Soome  *
133a1bf3f78SToomas Soome  * The meat of the simple parser.
134a1bf3f78SToomas Soome  */
135a1bf3f78SToomas Soome 
136a1bf3f78SToomas Soome static void	 clean(void);
137a1bf3f78SToomas Soome static int	 insert(int *argcp, char *buf);
138a1bf3f78SToomas Soome 
139a1bf3f78SToomas Soome #define	PARSE_BUFSIZE	1024	/* maximum size of one element */
140a1bf3f78SToomas Soome #define	MAXARGS		20	/* maximum number of elements */
141a1bf3f78SToomas Soome static	char		*args[MAXARGS];
142a1bf3f78SToomas Soome 
143a1bf3f78SToomas Soome #define	DIGIT(x)	\
144a1bf3f78SToomas Soome 	(isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
145a1bf3f78SToomas Soome 
146a1bf3f78SToomas Soome /*
147a1bf3f78SToomas Soome  * backslash: Return malloc'd copy of str with all standard "backslash
148a1bf3f78SToomas Soome  * processing" done on it.  Original can be free'd if desired.
149a1bf3f78SToomas Soome  */
150a1bf3f78SToomas Soome char *
151a1bf3f78SToomas Soome backslash(char *str)
152a1bf3f78SToomas Soome {
153a1bf3f78SToomas Soome 	/*
154a1bf3f78SToomas Soome 	 * Remove backslashes from the strings. Turn \040 etc. into a single
155a1bf3f78SToomas Soome 	 * character (we allow eight bit values). Currently NUL is not
156a1bf3f78SToomas Soome 	 * allowed.
157a1bf3f78SToomas Soome 	 *
158a1bf3f78SToomas Soome 	 * Turn "\n" and "\t" into '\n' and '\t' characters. Etc.
159a1bf3f78SToomas Soome 	 */
160a1bf3f78SToomas Soome 	char *new_str;
161a1bf3f78SToomas Soome 	int seenbs = 0;
162a1bf3f78SToomas Soome 	int i = 0;
163a1bf3f78SToomas Soome 
164a1bf3f78SToomas Soome 	if ((new_str = strdup(str)) == NULL)
165a1bf3f78SToomas Soome 		return (NULL);
166a1bf3f78SToomas Soome 
167a1bf3f78SToomas Soome 	while (*str) {
168a1bf3f78SToomas Soome 		if (seenbs) {
169a1bf3f78SToomas Soome 			seenbs = 0;
170a1bf3f78SToomas Soome 			switch (*str) {
171a1bf3f78SToomas Soome 			case '\\':
172a1bf3f78SToomas Soome 				new_str[i++] = '\\';
173a1bf3f78SToomas Soome 				str++;
174a1bf3f78SToomas Soome 			break;
175a1bf3f78SToomas Soome 
176a1bf3f78SToomas Soome 			/* preserve backslashed quotes, dollar signs */
177a1bf3f78SToomas Soome 			case '\'':
178a1bf3f78SToomas Soome 			case '"':
179a1bf3f78SToomas Soome 			case '$':
180a1bf3f78SToomas Soome 				new_str[i++] = '\\';
181a1bf3f78SToomas Soome 				new_str[i++] = *str++;
182a1bf3f78SToomas Soome 			break;
183a1bf3f78SToomas Soome 
184a1bf3f78SToomas Soome 			case 'b':
185a1bf3f78SToomas Soome 				new_str[i++] = '\b';
186a1bf3f78SToomas Soome 				str++;
187a1bf3f78SToomas Soome 			break;
188a1bf3f78SToomas Soome 
189a1bf3f78SToomas Soome 			case 'f':
190a1bf3f78SToomas Soome 				new_str[i++] = '\f';
191a1bf3f78SToomas Soome 				str++;
192a1bf3f78SToomas Soome 			break;
193a1bf3f78SToomas Soome 
194a1bf3f78SToomas Soome 			case 'r':
195a1bf3f78SToomas Soome 				new_str[i++] = '\r';
196a1bf3f78SToomas Soome 				str++;
197a1bf3f78SToomas Soome 			break;
198a1bf3f78SToomas Soome 
199a1bf3f78SToomas Soome 			case 'n':
200a1bf3f78SToomas Soome 				new_str[i++] = '\n';
201a1bf3f78SToomas Soome 				str++;
202a1bf3f78SToomas Soome 			break;
203a1bf3f78SToomas Soome 
204a1bf3f78SToomas Soome 			case 's':
205a1bf3f78SToomas Soome 				new_str[i++] = ' ';
206a1bf3f78SToomas Soome 				str++;
207a1bf3f78SToomas Soome 			break;
208a1bf3f78SToomas Soome 
209a1bf3f78SToomas Soome 			case 't':
210a1bf3f78SToomas Soome 				new_str[i++] = '\t';
211a1bf3f78SToomas Soome 				str++;
212a1bf3f78SToomas Soome 			break;
213a1bf3f78SToomas Soome 
214a1bf3f78SToomas Soome 			case 'v':
215a1bf3f78SToomas Soome 				new_str[i++] = '\13';
216a1bf3f78SToomas Soome 				str++;
217a1bf3f78SToomas Soome 			break;
218a1bf3f78SToomas Soome 
219a1bf3f78SToomas Soome 			case 'z':
220a1bf3f78SToomas Soome 				str++;
221a1bf3f78SToomas Soome 			break;
222a1bf3f78SToomas Soome 
223a1bf3f78SToomas Soome 			case '0': case '1': case '2': case '3': case '4':
224a1bf3f78SToomas Soome 			case '5': case '6': case '7': case '8': case '9': {
225a1bf3f78SToomas Soome 				char val;
226a1bf3f78SToomas Soome 
227a1bf3f78SToomas Soome 				/* Three digit octal constant? */
228a1bf3f78SToomas Soome 				if (*str >= '0' && *str <= '3' &&
229a1bf3f78SToomas Soome 				    *(str + 1) >= '0' && *(str + 1) <= '7' &&
230a1bf3f78SToomas Soome 				    *(str + 2) >= '0' && *(str + 2) <= '7') {
231a1bf3f78SToomas Soome 
232a1bf3f78SToomas Soome 					val = (DIGIT(*str) << 6) +
233a1bf3f78SToomas Soome 					    (DIGIT(*(str + 1)) << 3) +
234a1bf3f78SToomas Soome 					    DIGIT(*(str + 2));
235a1bf3f78SToomas Soome 
236a1bf3f78SToomas Soome 					/*
237a1bf3f78SToomas Soome 					 * Allow null value if user really
238a1bf3f78SToomas Soome 					 * wants to shoot at feet, but beware!
239a1bf3f78SToomas Soome 					 */
240a1bf3f78SToomas Soome 					new_str[i++] = val;
241a1bf3f78SToomas Soome 					str += 3;
242a1bf3f78SToomas Soome 					break;
243a1bf3f78SToomas Soome 				}
244a1bf3f78SToomas Soome 
245a1bf3f78SToomas Soome 				/*
246a1bf3f78SToomas Soome 				 * One or two digit hex constant?
247a1bf3f78SToomas Soome 				 * If two are there they will both be taken.
248a1bf3f78SToomas Soome 				 * Use \z to split them up if this is not
249a1bf3f78SToomas Soome 				 * wanted.
250a1bf3f78SToomas Soome 				 */
251a1bf3f78SToomas Soome 				if (*str == '0' &&
252a1bf3f78SToomas Soome 				    (*(str + 1) == 'x' || *(str + 1) == 'X') &&
253a1bf3f78SToomas Soome 				    isxdigit(*(str + 2))) {
254a1bf3f78SToomas Soome 					val = DIGIT(*(str + 2));
255a1bf3f78SToomas Soome 					if (isxdigit(*(str + 3))) {
256a1bf3f78SToomas Soome 						val = (val << 4) +
257a1bf3f78SToomas Soome 						    DIGIT(*(str + 3));
258a1bf3f78SToomas Soome 						str += 4;
259a1bf3f78SToomas Soome 					} else
260a1bf3f78SToomas Soome 						str += 3;
261a1bf3f78SToomas Soome 					/* Yep, allow null value here too */
262a1bf3f78SToomas Soome 					new_str[i++] = val;
263a1bf3f78SToomas Soome 					break;
264a1bf3f78SToomas Soome 				}
265a1bf3f78SToomas Soome 			}
266a1bf3f78SToomas Soome 			break;
267a1bf3f78SToomas Soome 
268a1bf3f78SToomas Soome 			default:
269a1bf3f78SToomas Soome 				new_str[i++] = *str++;
270a1bf3f78SToomas Soome 			break;
271a1bf3f78SToomas Soome 			}
272a1bf3f78SToomas Soome 		} else {
273a1bf3f78SToomas Soome 			if (*str == '\\') {
274a1bf3f78SToomas Soome 				seenbs = 1;
275a1bf3f78SToomas Soome 				str++;
276a1bf3f78SToomas Soome 			} else
277a1bf3f78SToomas Soome 				new_str[i++] = *str++;
278a1bf3f78SToomas Soome 		}
279a1bf3f78SToomas Soome 	}
280a1bf3f78SToomas Soome 
281a1bf3f78SToomas Soome 	if (seenbs) {
282a1bf3f78SToomas Soome 		/*
283a1bf3f78SToomas Soome 		 * The final character was a '\'.
284a1bf3f78SToomas Soome 		 * Put it in as a single backslash.
285a1bf3f78SToomas Soome 		 */
286a1bf3f78SToomas Soome 		new_str[i++] = '\\';
287a1bf3f78SToomas Soome 	}
288a1bf3f78SToomas Soome 	new_str[i] = '\0';
289a1bf3f78SToomas Soome 	return (new_str);
290a1bf3f78SToomas Soome }
291a1bf3f78SToomas Soome 
292a1bf3f78SToomas Soome /*
293a1bf3f78SToomas Soome  * parse: accept a string of input and "parse" it for backslash
294a1bf3f78SToomas Soome  * substitutions and environment variable expansions (${var}),
295a1bf3f78SToomas Soome  * returning an argc/argv style vector of whitespace separated
296a1bf3f78SToomas Soome  * arguments.  Returns 0 on success, 1 on failure (ok, ok, so I
297a1bf3f78SToomas Soome  * wimped-out on the error codes! :).
298a1bf3f78SToomas Soome  *
299a1bf3f78SToomas Soome  * Note that the argv array returned must be freed by the caller, but
300a1bf3f78SToomas Soome  * we own the space allocated for arguments and will free that on next
301a1bf3f78SToomas Soome  * invocation.  This allows argv consumers to modify the array if
302a1bf3f78SToomas Soome  * required.
303a1bf3f78SToomas Soome  *
304a1bf3f78SToomas Soome  * NB: environment variables that expand to more than one whitespace
305a1bf3f78SToomas Soome  * separated token will be returned as a single argv[] element, not
306a1bf3f78SToomas Soome  * split in turn.  Expanded text is also immune to further backslash
307a1bf3f78SToomas Soome  * elimination or expansion since this is a one-pass, non-recursive
308a1bf3f78SToomas Soome  * parser.  You didn't specify more than this so if you want more, ask
309a1bf3f78SToomas Soome  * me. - jkh
310a1bf3f78SToomas Soome  */
311a1bf3f78SToomas Soome 
312a1bf3f78SToomas Soome #define	PARSE_FAIL(expr)	\
313a1bf3f78SToomas Soome if (expr) { \
314a1bf3f78SToomas Soome     printf("fail at line %d\n", __LINE__); \
315a1bf3f78SToomas Soome     clean(); \
316a1bf3f78SToomas Soome     free(copy); \
317a1bf3f78SToomas Soome     free(buf); \
318a1bf3f78SToomas Soome     return (1); \
319a1bf3f78SToomas Soome }
320a1bf3f78SToomas Soome 
321a1bf3f78SToomas Soome /* Accept the usual delimiters for a variable, returning counterpart */
322a1bf3f78SToomas Soome static char
323a1bf3f78SToomas Soome isdelim(int ch)
324a1bf3f78SToomas Soome {
325a1bf3f78SToomas Soome 	if (ch == '{')
326a1bf3f78SToomas Soome 		return ('}');
327a1bf3f78SToomas Soome 	else if (ch == '(')
328a1bf3f78SToomas Soome 		return (')');
329a1bf3f78SToomas Soome 	return ('\0');
330a1bf3f78SToomas Soome }
331a1bf3f78SToomas Soome 
332a1bf3f78SToomas Soome static int
333a1bf3f78SToomas Soome isquote(int ch)
334a1bf3f78SToomas Soome {
335a1bf3f78SToomas Soome 	return (ch == '\'');
336a1bf3f78SToomas Soome }
337a1bf3f78SToomas Soome 
338a1bf3f78SToomas Soome static int
339a1bf3f78SToomas Soome isdquote(int ch)
340a1bf3f78SToomas Soome {
341a1bf3f78SToomas Soome 	return (ch == '"');
342a1bf3f78SToomas Soome }
343a1bf3f78SToomas Soome 
344a1bf3f78SToomas Soome int
345a1bf3f78SToomas Soome parse(int *argc, char ***argv, char *str)
346a1bf3f78SToomas Soome {
347a1bf3f78SToomas Soome 	int ac;
348a1bf3f78SToomas Soome 	char *val, *p, *q, *copy = NULL;
349a1bf3f78SToomas Soome 	size_t i = 0;
350a1bf3f78SToomas Soome 	char token, tmp, quote, dquote, *buf;
351a1bf3f78SToomas Soome 	enum { STR, VAR, WHITE } state;
352a1bf3f78SToomas Soome 
353a1bf3f78SToomas Soome 	ac = *argc = 0;
354a1bf3f78SToomas Soome 	dquote = quote = 0;
355a1bf3f78SToomas Soome 	if (!str || (p = copy = backslash(str)) == NULL)
356a1bf3f78SToomas Soome 		return (1);
357a1bf3f78SToomas Soome 
358a1bf3f78SToomas Soome 	/* Initialize vector and state */
359a1bf3f78SToomas Soome 	clean();
360a1bf3f78SToomas Soome 	state = STR;
361a1bf3f78SToomas Soome 	buf = (char *)malloc(PARSE_BUFSIZE);
362a1bf3f78SToomas Soome 	token = 0;
363a1bf3f78SToomas Soome 
364a1bf3f78SToomas Soome 	/* And awaaaaaaaaay we go! */
365a1bf3f78SToomas Soome 	while (*p) {
366a1bf3f78SToomas Soome 		switch (state) {
367a1bf3f78SToomas Soome 		case STR:
368a1bf3f78SToomas Soome 			if ((*p == '\\') && p[1]) {
369a1bf3f78SToomas Soome 				p++;
370a1bf3f78SToomas Soome 				PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
371a1bf3f78SToomas Soome 				buf[i++] = *p++;
372a1bf3f78SToomas Soome 			} else if (isquote(*p)) {
373a1bf3f78SToomas Soome 				quote = quote ? 0 : *p;
374a1bf3f78SToomas Soome 				if (dquote) { /* keep quote */
375a1bf3f78SToomas Soome 					PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
376a1bf3f78SToomas Soome 					buf[i++] = *p++;
377a1bf3f78SToomas Soome 				} else
378a1bf3f78SToomas Soome 					++p;
379a1bf3f78SToomas Soome 			} else if (isdquote(*p)) {
380a1bf3f78SToomas Soome 				dquote = dquote ? 0 : *p;
381a1bf3f78SToomas Soome 				if (quote) { /* keep dquote */
382a1bf3f78SToomas Soome 					PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
383a1bf3f78SToomas Soome 					buf[i++] = *p++;
384a1bf3f78SToomas Soome 				} else
385a1bf3f78SToomas Soome 					++p;
386a1bf3f78SToomas Soome 			} else if (isspace(*p) && !quote && !dquote) {
387a1bf3f78SToomas Soome 				state = WHITE;
388a1bf3f78SToomas Soome 				if (i) {
389a1bf3f78SToomas Soome 					buf[i] = '\0';
390a1bf3f78SToomas Soome 					PARSE_FAIL(insert(&ac, buf));
391a1bf3f78SToomas Soome 					i = 0;
392a1bf3f78SToomas Soome 				}
393a1bf3f78SToomas Soome 				++p;
394a1bf3f78SToomas Soome 			} else if (*p == '$' && !quote) {
395a1bf3f78SToomas Soome 				token = isdelim(*(p + 1));
396a1bf3f78SToomas Soome 				if (token)
397a1bf3f78SToomas Soome 					p += 2;
398a1bf3f78SToomas Soome 				else
399a1bf3f78SToomas Soome 					++p;
400a1bf3f78SToomas Soome 				state = VAR;
401a1bf3f78SToomas Soome 			} else {
402a1bf3f78SToomas Soome 				PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
403a1bf3f78SToomas Soome 				buf[i++] = *p++;
404a1bf3f78SToomas Soome 			}
405a1bf3f78SToomas Soome 		break;
406a1bf3f78SToomas Soome 
407a1bf3f78SToomas Soome 		case WHITE:
408a1bf3f78SToomas Soome 			if (isspace(*p))
409a1bf3f78SToomas Soome 				++p;
410a1bf3f78SToomas Soome 			else
411a1bf3f78SToomas Soome 				state = STR;
412a1bf3f78SToomas Soome 		break;
413a1bf3f78SToomas Soome 
414a1bf3f78SToomas Soome 		case VAR:
415a1bf3f78SToomas Soome 			if (token) {
416a1bf3f78SToomas Soome 				PARSE_FAIL((q = strchr(p, token)) == NULL);
417a1bf3f78SToomas Soome 			} else {
418a1bf3f78SToomas Soome 				q = p;
419a1bf3f78SToomas Soome 				while (*q && !isspace(*q))
420a1bf3f78SToomas Soome 				++q;
421a1bf3f78SToomas Soome 			}
422a1bf3f78SToomas Soome 			tmp = *q;
423a1bf3f78SToomas Soome 			*q = '\0';
424a1bf3f78SToomas Soome 			if ((val = getenv(p)) != NULL) {
425a1bf3f78SToomas Soome 				size_t len = strlen(val);
426a1bf3f78SToomas Soome 
427a1bf3f78SToomas Soome 				strncpy(buf + i, val, PARSE_BUFSIZE - (i + 1));
428a1bf3f78SToomas Soome 				i += min(len, PARSE_BUFSIZE - 1);
429a1bf3f78SToomas Soome 			}
430a1bf3f78SToomas Soome 			*q = tmp;	/* restore value */
431a1bf3f78SToomas Soome 			p = q + (token ? 1 : 0);
432a1bf3f78SToomas Soome 			state = STR;
433a1bf3f78SToomas Soome 		break;
434a1bf3f78SToomas Soome 		}
435a1bf3f78SToomas Soome 	}
436a1bf3f78SToomas Soome 	/* missing terminating ' or " */
437a1bf3f78SToomas Soome 	PARSE_FAIL(quote || dquote);
438a1bf3f78SToomas Soome 	/* If at end of token, add it */
439a1bf3f78SToomas Soome 	if (i && state == STR) {
440a1bf3f78SToomas Soome 		buf[i] = '\0';
441a1bf3f78SToomas Soome 		PARSE_FAIL(insert(&ac, buf));
442a1bf3f78SToomas Soome 	}
443a1bf3f78SToomas Soome 	args[ac] = NULL;
444a1bf3f78SToomas Soome 	*argc = ac;
445a1bf3f78SToomas Soome 	*argv = (char **)malloc((sizeof (char *) * ac + 1));
446a1bf3f78SToomas Soome 	bcopy(args, *argv, sizeof (char *) * ac + 1);
447a1bf3f78SToomas Soome 	free(buf);
448a1bf3f78SToomas Soome 	free(copy);
449a1bf3f78SToomas Soome 	return (0);
450a1bf3f78SToomas Soome }
451a1bf3f78SToomas Soome 
452a1bf3f78SToomas Soome #define	MAXARGS	20
453a1bf3f78SToomas Soome 
454a1bf3f78SToomas Soome /* Clean vector space */
455a1bf3f78SToomas Soome static void
456a1bf3f78SToomas Soome clean(void)
457a1bf3f78SToomas Soome {
458a1bf3f78SToomas Soome 	int i;
459a1bf3f78SToomas Soome 
460a1bf3f78SToomas Soome 	for (i = 0; i < MAXARGS; i++) {
461a1bf3f78SToomas Soome 		if (args[i] != NULL) {
462a1bf3f78SToomas Soome 			free(args[i]);
463a1bf3f78SToomas Soome 			args[i] = NULL;
464a1bf3f78SToomas Soome 		}
465a1bf3f78SToomas Soome 	}
466a1bf3f78SToomas Soome }
467a1bf3f78SToomas Soome 
468a1bf3f78SToomas Soome static int
469a1bf3f78SToomas Soome insert(int *argcp, char *buf)
470a1bf3f78SToomas Soome {
471a1bf3f78SToomas Soome 	if (*argcp >= MAXARGS)
472a1bf3f78SToomas Soome 		return (1);
473a1bf3f78SToomas Soome 	args[(*argcp)++] = strdup(buf);
474a1bf3f78SToomas Soome 	return (0);
475a1bf3f78SToomas Soome }
476a1bf3f78SToomas Soome 
477a1bf3f78SToomas Soome static char *
478a1bf3f78SToomas Soome isadir(void)
479a1bf3f78SToomas Soome {
480a1bf3f78SToomas Soome 	char *buf;
481a1bf3f78SToomas Soome 	size_t bufsize = 20;
482a1bf3f78SToomas Soome 	int ret;
483a1bf3f78SToomas Soome 
484a1bf3f78SToomas Soome 	if ((buf = malloc(bufsize)) == NULL)
485a1bf3f78SToomas Soome 		return (NULL);
486a1bf3f78SToomas Soome 	ret = sysinfo(SI_ARCHITECTURE_K, buf, bufsize);
487a1bf3f78SToomas Soome 	if (ret == -1) {
488a1bf3f78SToomas Soome 		free(buf);
489a1bf3f78SToomas Soome 		return (NULL);
490a1bf3f78SToomas Soome 	}
491a1bf3f78SToomas Soome 	return (buf);
492a1bf3f78SToomas Soome }
493a1bf3f78SToomas Soome 
494a1bf3f78SToomas Soome /*
495a1bf3f78SToomas Soome  * Shim for taking commands from BF and passing them out to 'standard'
496a1bf3f78SToomas Soome  * argv/argc command functions.
497a1bf3f78SToomas Soome  */
498a1bf3f78SToomas Soome static void
499a1bf3f78SToomas Soome bf_command(ficlVm *vm)
500a1bf3f78SToomas Soome {
501a1bf3f78SToomas Soome 	char *name, *line, *tail, *cp;
502a1bf3f78SToomas Soome 	size_t len;
503a1bf3f78SToomas Soome 	struct bootblk_command *cmdp;
504a1bf3f78SToomas Soome 	bootblk_cmd_t *cmd;
505a1bf3f78SToomas Soome 	int nstrings, i;
506a1bf3f78SToomas Soome 	int argc, result;
507a1bf3f78SToomas Soome 	char **argv;
508a1bf3f78SToomas Soome 
509a1bf3f78SToomas Soome 	/* Get the name of the current word */
510a1bf3f78SToomas Soome 	name = vm->runningWord->name;
511a1bf3f78SToomas Soome 
512a1bf3f78SToomas Soome 	/* Find our command structure */
513a1bf3f78SToomas Soome 	cmd = NULL;
514a1bf3f78SToomas Soome 	STAILQ_FOREACH(cmdp, &commands, next) {
515a1bf3f78SToomas Soome 		if ((cmdp->c_name != NULL) && strcmp(name, cmdp->c_name) == 0)
516a1bf3f78SToomas Soome 			cmd = cmdp->c_fn;
517a1bf3f78SToomas Soome 	}
518a1bf3f78SToomas Soome 	if (cmd == NULL)
519a1bf3f78SToomas Soome 		printf("callout for unknown command '%s'\n", name);
520a1bf3f78SToomas Soome 
521a1bf3f78SToomas Soome 	/* Check whether we have been compiled or are being interpreted */
522a1bf3f78SToomas Soome 	if (ficlStackPopInteger(ficlVmGetDataStack(vm))) {
523a1bf3f78SToomas Soome 		/*
524a1bf3f78SToomas Soome 		 * Get parameters from stack, in the format:
525a1bf3f78SToomas Soome 		 * an un ... a2 u2 a1 u1 n --
526a1bf3f78SToomas Soome 		 * Where n is the number of strings, a/u are pairs of
527a1bf3f78SToomas Soome 		 * address/size for strings, and they will be concatenated
528a1bf3f78SToomas Soome 		 * in LIFO order.
529a1bf3f78SToomas Soome 		 */
530a1bf3f78SToomas Soome 		nstrings = ficlStackPopInteger(ficlVmGetDataStack(vm));
531a1bf3f78SToomas Soome 		for (i = 0, len = 0; i < nstrings; i++)
532a1bf3f78SToomas Soome 		len += ficlStackFetch(ficlVmGetDataStack(vm), i * 2).i + 1;
533a1bf3f78SToomas Soome 		line = malloc(strlen(name) + len + 1);
534a1bf3f78SToomas Soome 		strcpy(line, name);
535a1bf3f78SToomas Soome 
536a1bf3f78SToomas Soome 		if (nstrings)
537a1bf3f78SToomas Soome 			for (i = 0; i < nstrings; i++) {
538a1bf3f78SToomas Soome 				len = ficlStackPopInteger(
539a1bf3f78SToomas Soome 				    ficlVmGetDataStack(vm));
540a1bf3f78SToomas Soome 				cp = ficlStackPopPointer(
541a1bf3f78SToomas Soome 				    ficlVmGetDataStack(vm));
542a1bf3f78SToomas Soome 				strcat(line, " ");
543a1bf3f78SToomas Soome 				strncat(line, cp, len);
544a1bf3f78SToomas Soome 			}
545a1bf3f78SToomas Soome 	} else {
546a1bf3f78SToomas Soome 		/* Get remainder of invocation */
547a1bf3f78SToomas Soome 		tail = ficlVmGetInBuf(vm);
548a1bf3f78SToomas Soome 		for (cp = tail, len = 0;
549a1bf3f78SToomas Soome 		    cp != vm->tib.end && *cp != 0 && *cp != '\n'; cp++, len++)
550a1bf3f78SToomas Soome 			;
551a1bf3f78SToomas Soome 
552a1bf3f78SToomas Soome 		line = malloc(strlen(name) + len + 2);
553a1bf3f78SToomas Soome 		strcpy(line, name);
554a1bf3f78SToomas Soome 		if (len > 0) {
555a1bf3f78SToomas Soome 			strcat(line, " ");
556a1bf3f78SToomas Soome 			strncat(line, tail, len);
557a1bf3f78SToomas Soome 			ficlVmUpdateTib(vm, tail + len);
558a1bf3f78SToomas Soome 		}
559a1bf3f78SToomas Soome 	}
560a1bf3f78SToomas Soome 
561a1bf3f78SToomas Soome 	command_errmsg = command_errbuf;
562a1bf3f78SToomas Soome 	command_errbuf[0] = 0;
563a1bf3f78SToomas Soome 	if (!parse(&argc, &argv, line)) {
564a1bf3f78SToomas Soome 		result = (cmd)(argc, argv);
565a1bf3f78SToomas Soome 		free(argv);
566a1bf3f78SToomas Soome 	} else {
567a1bf3f78SToomas Soome 		result = BF_PARSE;
568a1bf3f78SToomas Soome 	}
569a1bf3f78SToomas Soome 	free(line);
570a1bf3f78SToomas Soome 	/*
571a1bf3f78SToomas Soome 	 * If there was error during nested ficlExec(), we may no longer have
572a1bf3f78SToomas Soome 	 * valid environment to return.  Throw all exceptions from here.
573a1bf3f78SToomas Soome 	 */
574a1bf3f78SToomas Soome 	if (result != 0)
575a1bf3f78SToomas Soome 		ficlVmThrow(vm, result);
576a1bf3f78SToomas Soome 	/* This is going to be thrown!!! */
577a1bf3f78SToomas Soome 	ficlStackPushInteger(ficlVmGetDataStack(vm), result);
578a1bf3f78SToomas Soome }
579a1bf3f78SToomas Soome 
580a1bf3f78SToomas Soome static char *
581a1bf3f78SToomas Soome get_currdev(void)
582a1bf3f78SToomas Soome {
583a1bf3f78SToomas Soome 	int ret;
584a1bf3f78SToomas Soome 	char *currdev;
585a1bf3f78SToomas Soome 	FILE *fp;
586a1bf3f78SToomas Soome 	struct mnttab mpref = {0};
587a1bf3f78SToomas Soome 	struct mnttab mp = {0};
588a1bf3f78SToomas Soome 
589a1bf3f78SToomas Soome 	mpref.mnt_mountp = "/";
590a1bf3f78SToomas Soome 	fp = fopen(MNTTAB, "r");
591a1bf3f78SToomas Soome 
592a1bf3f78SToomas Soome 	/* do the best we can to return something... */
593a1bf3f78SToomas Soome 	if (fp == NULL)
594a1bf3f78SToomas Soome 		return (strdup(":"));
595a1bf3f78SToomas Soome 
596a1bf3f78SToomas Soome 	ret = getmntany(fp, &mp, &mpref);
597a1bf3f78SToomas Soome 	(void) fclose(fp);
598a1bf3f78SToomas Soome 	if (ret == 0)
599a1bf3f78SToomas Soome 		(void) asprintf(&currdev, "zfs:%s:", mp.mnt_special);
600a1bf3f78SToomas Soome 	else
601a1bf3f78SToomas Soome 		return (strdup(":"));
602a1bf3f78SToomas Soome 
603a1bf3f78SToomas Soome 	return (currdev);
604a1bf3f78SToomas Soome }
605a1bf3f78SToomas Soome 
606a1bf3f78SToomas Soome /*
607a1bf3f78SToomas Soome  * Replace a word definition (a builtin command) with another
608a1bf3f78SToomas Soome  * one that:
609a1bf3f78SToomas Soome  *
610a1bf3f78SToomas Soome  *        - Throw error results instead of returning them on the stack
611a1bf3f78SToomas Soome  *        - Pass a flag indicating whether the word was compiled or is
612a1bf3f78SToomas Soome  *          being interpreted.
613a1bf3f78SToomas Soome  *
614a1bf3f78SToomas Soome  * There is one major problem with builtins that cannot be overcome
615a1bf3f78SToomas Soome  * in anyway, except by outlawing it. We want builtins to behave
616a1bf3f78SToomas Soome  * differently depending on whether they have been compiled or they
617a1bf3f78SToomas Soome  * are being interpreted. Notice that this is *not* the interpreter's
618a1bf3f78SToomas Soome  * current state. For example:
619a1bf3f78SToomas Soome  *
620a1bf3f78SToomas Soome  * : example ls ; immediate
621a1bf3f78SToomas Soome  * : problem example ;		\ "ls" gets executed while compiling
622a1bf3f78SToomas Soome  * example			\ "ls" gets executed while interpreting
623a1bf3f78SToomas Soome  *
624a1bf3f78SToomas Soome  * Notice that, though the current state is different in the two
625a1bf3f78SToomas Soome  * invocations of "example", in both cases "ls" has been
626a1bf3f78SToomas Soome  * *compiled in*, which is what we really want.
627a1bf3f78SToomas Soome  *
628a1bf3f78SToomas Soome  * The problem arises when you tick the builtin. For example:
629a1bf3f78SToomas Soome  *
630a1bf3f78SToomas Soome  * : example-1 ['] ls postpone literal ; immediate
631a1bf3f78SToomas Soome  * : example-2 example-1 execute ; immediate
632a1bf3f78SToomas Soome  * : problem example-2 ;
633a1bf3f78SToomas Soome  * example-2
634a1bf3f78SToomas Soome  *
635a1bf3f78SToomas Soome  * We have no way, when we get EXECUTEd, of knowing what our behavior
636a1bf3f78SToomas Soome  * should be. Thus, our only alternative is to "outlaw" this. See RFI
637a1bf3f78SToomas Soome  * 0007, and ANS Forth Standard's appendix D, item 6.7 for a related
638a1bf3f78SToomas Soome  * problem, concerning compile semantics.
639a1bf3f78SToomas Soome  *
640a1bf3f78SToomas Soome  * The problem is compounded by the fact that "' builtin CATCH" is valid
641a1bf3f78SToomas Soome  * and desirable. The only solution is to create an intermediary word.
642a1bf3f78SToomas Soome  * For example:
643a1bf3f78SToomas Soome  *
644a1bf3f78SToomas Soome  * : my-ls ls ;
645a1bf3f78SToomas Soome  * : example ['] my-ls catch ;
646a1bf3f78SToomas Soome  *
647a1bf3f78SToomas Soome  * So, with the below implementation, here is a summary of the behavior
648a1bf3f78SToomas Soome  * of builtins:
649a1bf3f78SToomas Soome  *
650a1bf3f78SToomas Soome  * ls -l				\ "interpret" behavior, ie,
651a1bf3f78SToomas Soome  *					\ takes parameters from TIB
652a1bf3f78SToomas Soome  * : ex-1 s" -l" 1 ls ;			\ "compile" behavior, ie,
653a1bf3f78SToomas Soome  *					\ takes parameters from the stack
654a1bf3f78SToomas Soome  * : ex-2 ['] ls catch ; immediate	\ undefined behavior
655a1bf3f78SToomas Soome  * : ex-3 ['] ls catch ;		\ undefined behavior
656a1bf3f78SToomas Soome  * ex-2 ex-3				\ "interpret" behavior,
657a1bf3f78SToomas Soome  *					\ catch works
658a1bf3f78SToomas Soome  * : ex-4 ex-2 ;			\ "compile" behavior,
659a1bf3f78SToomas Soome  *					\ catch does not work
660a1bf3f78SToomas Soome  * : ex-5 ex-3 ; immediate		\ same as ex-2
661a1bf3f78SToomas Soome  * : ex-6 ex-3 ;			\ same as ex-3
662a1bf3f78SToomas Soome  * : ex-7 ['] ex-1 catch ;		\ "compile" behavior,
663a1bf3f78SToomas Soome  *					\ catch works
664a1bf3f78SToomas Soome  * : ex-8 postpone ls ;	immediate	\ same as ex-2
665a1bf3f78SToomas Soome  * : ex-9 postpone ls ;			\ same as ex-3
666a1bf3f78SToomas Soome  *
667a1bf3f78SToomas Soome  * As the definition below is particularly tricky, and it's side effects
668a1bf3f78SToomas Soome  * must be well understood by those playing with it, I'll be heavy on
669a1bf3f78SToomas Soome  * the comments.
670a1bf3f78SToomas Soome  *
671a1bf3f78SToomas Soome  * (if you edit this definition, pay attention to trailing spaces after
672a1bf3f78SToomas Soome  *  each word -- I warned you! :-) )
673a1bf3f78SToomas Soome  */
674a1bf3f78SToomas Soome #define	BUILTIN_CONSTRUCTOR \
675a1bf3f78SToomas Soome ": builtin: "		\
676a1bf3f78SToomas Soome ">in @ "		/* save the tib index pointer */ \
677a1bf3f78SToomas Soome "' "			/* get next word's xt */ \
678a1bf3f78SToomas Soome "swap >in ! "		/* point again to next word */ \
679a1bf3f78SToomas Soome "create "		/* create a new definition of the next word */ \
680a1bf3f78SToomas Soome ", "			/* save previous definition's xt */ \
681a1bf3f78SToomas Soome "immediate "		/* make the new definition an immediate word */ \
682a1bf3f78SToomas Soome 			\
683a1bf3f78SToomas Soome "does> "		/* Now, the *new* definition will: */ \
684a1bf3f78SToomas Soome "state @ if "		/* if in compiling state: */ \
685a1bf3f78SToomas Soome "1 postpone literal "	/* pass 1 flag to indicate compile */ \
686a1bf3f78SToomas Soome "@ compile, "		/* compile in previous definition */ \
687a1bf3f78SToomas Soome "postpone throw "		/* throw stack-returned result */ \
688a1bf3f78SToomas Soome "else "		/* if in interpreting state: */ \
689a1bf3f78SToomas Soome "0 swap "			/* pass 0 flag to indicate interpret */ \
690a1bf3f78SToomas Soome "@ execute "		/* call previous definition */ \
691a1bf3f78SToomas Soome "throw "			/* throw stack-returned result */ \
692a1bf3f78SToomas Soome "then ; "
693a1bf3f78SToomas Soome 
694a1bf3f78SToomas Soome extern int ficlExecFD(ficlVm *, int);
695a1bf3f78SToomas Soome #define	COMMAND_SET(ptr, name, desc, fn)		\
696a1bf3f78SToomas Soome 	ptr = malloc(sizeof (struct bootblk_command));	\
697a1bf3f78SToomas Soome 	ptr->c_name = (name);				\
698a1bf3f78SToomas Soome 	ptr->c_desc = (desc);				\
699a1bf3f78SToomas Soome 	ptr->c_fn = (fn);
700a1bf3f78SToomas Soome 
701a1bf3f78SToomas Soome /*
702a1bf3f78SToomas Soome  * Initialise the Forth interpreter, create all our commands as words.
703a1bf3f78SToomas Soome  */
704a1bf3f78SToomas Soome ficlVm *
705a1bf3f78SToomas Soome bf_init(const char *rc, ficlOutputFunction out)
706a1bf3f78SToomas Soome {
707a1bf3f78SToomas Soome 	struct bootblk_command *cmdp;
708a1bf3f78SToomas Soome 	char create_buf[41];	/* 31 characters-long builtins */
709a1bf3f78SToomas Soome 	char *buf;
710a1bf3f78SToomas Soome 	int fd, rv;
711a1bf3f78SToomas Soome 	ficlSystemInformation *fsi;
712a1bf3f78SToomas Soome 	ficlDictionary *dict;
713a1bf3f78SToomas Soome 	ficlDictionary *env;
714a1bf3f78SToomas Soome 
715a1bf3f78SToomas Soome 	/* set up commands list */
716a1bf3f78SToomas Soome 	STAILQ_INIT(&commands);
717a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "help", "detailed help", command_help);
718a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
719a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "?", "list commands", command_commandlist);
720a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
721a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "show", "show variable(s)", command_show);
722a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
723a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "printenv", "show variable(s)", command_show);
724a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
725a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "set", "set a variable", command_set);
726a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
727a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "setprop", "set a variable", command_setprop);
728a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
729a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "unset", "unset a variable", command_unset);
730a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
731a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "echo", "echo arguments", command_echo);
732a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
733a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "read", "read input from the terminal", command_read);
734a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
735a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "more", "show contents of a file", command_more);
736a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
737a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "ls", "list files", command_ls);
738a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
739a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "include", "read commands from a file",
740a1bf3f78SToomas Soome 	    command_include);
741a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
742a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "boot", "boot a file or loaded kernel", command_boot);
743a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
744a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "autoboot", "boot automatically after a delay",
745a1bf3f78SToomas Soome 	    command_autoboot);
746a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
747a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "load", "load a kernel or module", command_load);
748a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
749a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "unload", "unload all modules", command_unload);
750a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
751a1bf3f78SToomas Soome 	COMMAND_SET(cmdp, "reboot", "reboot the system", command_reboot);
752a1bf3f78SToomas Soome 	STAILQ_INSERT_TAIL(&commands, cmdp, next);
753a1bf3f78SToomas Soome 
754a1bf3f78SToomas Soome 	fsi = malloc(sizeof (ficlSystemInformation));
755a1bf3f78SToomas Soome 	ficlSystemInformationInitialize(fsi);
756a1bf3f78SToomas Soome 	fsi->textOut = out;
757a1bf3f78SToomas Soome 	fsi->dictionarySize = BF_DICTSIZE;
758a1bf3f78SToomas Soome 
759a1bf3f78SToomas Soome 	bf_sys = ficlSystemCreate(fsi);
760a1bf3f78SToomas Soome 	free(fsi);
761a1bf3f78SToomas Soome 	ficlSystemCompileExtras(bf_sys);
762a1bf3f78SToomas Soome 	bf_vm = ficlSystemCreateVm(bf_sys);
763a1bf3f78SToomas Soome 
764a1bf3f78SToomas Soome 	buf = isadir();
765a1bf3f78SToomas Soome 	if (buf == NULL || strcmp(buf, "amd64") != 0) {
766a1bf3f78SToomas Soome 		(void) setenv("ISADIR", "", 1);
767a1bf3f78SToomas Soome 	} else {
768a1bf3f78SToomas Soome 		(void) setenv("ISADIR", buf, 1);
769a1bf3f78SToomas Soome 	}
770a1bf3f78SToomas Soome 	if (buf != NULL)
771a1bf3f78SToomas Soome 		free(buf);
772a1bf3f78SToomas Soome 	buf = get_currdev();
773a1bf3f78SToomas Soome 	(void) setenv("currdev", buf, 1);
774a1bf3f78SToomas Soome 	free(buf);
775a1bf3f78SToomas Soome 
776a1bf3f78SToomas Soome 	/* Put all private definitions in a "builtins" vocabulary */
777a1bf3f78SToomas Soome 	rv = ficlVmEvaluate(bf_vm,
778a1bf3f78SToomas Soome 	    "vocabulary builtins also builtins definitions");
779a1bf3f78SToomas Soome 	if (rv != FICL_VM_STATUS_OUT_OF_TEXT) {
780a1bf3f78SToomas Soome 		printf("error interpreting forth: %d\n", rv);
781a1bf3f78SToomas Soome 		exit(1);
782a1bf3f78SToomas Soome 	}
783a1bf3f78SToomas Soome 
784a1bf3f78SToomas Soome 	/* Builtin constructor word  */
785a1bf3f78SToomas Soome 	rv = ficlVmEvaluate(bf_vm, BUILTIN_CONSTRUCTOR);
786a1bf3f78SToomas Soome 	if (rv != FICL_VM_STATUS_OUT_OF_TEXT) {
787a1bf3f78SToomas Soome 		printf("error interpreting forth: %d\n", rv);
788a1bf3f78SToomas Soome 		exit(1);
789a1bf3f78SToomas Soome 	}
790a1bf3f78SToomas Soome 
791a1bf3f78SToomas Soome 	/* make all commands appear as Forth words */
792a1bf3f78SToomas Soome 	dict = ficlSystemGetDictionary(bf_sys);
793a1bf3f78SToomas Soome 	cmdp = NULL;
794a1bf3f78SToomas Soome 	STAILQ_FOREACH(cmdp, &commands, next) {
795a1bf3f78SToomas Soome 		ficlDictionaryAppendPrimitive(dict, (char *)cmdp->c_name,
796a1bf3f78SToomas Soome 		    bf_command, FICL_WORD_DEFAULT);
797a1bf3f78SToomas Soome 		rv = ficlVmEvaluate(bf_vm, "forth definitions builtins");
798a1bf3f78SToomas Soome 		if (rv != FICL_VM_STATUS_OUT_OF_TEXT) {
799a1bf3f78SToomas Soome 			printf("error interpreting forth: %d\n", rv);
800a1bf3f78SToomas Soome 			exit(1);
801a1bf3f78SToomas Soome 		}
802a1bf3f78SToomas Soome 		sprintf(create_buf, "builtin: %s", cmdp->c_name);
803a1bf3f78SToomas Soome 		rv = ficlVmEvaluate(bf_vm, create_buf);
804a1bf3f78SToomas Soome 		if (rv != FICL_VM_STATUS_OUT_OF_TEXT) {
805a1bf3f78SToomas Soome 			printf("error interpreting forth: %d\n", rv);
806a1bf3f78SToomas Soome 			exit(1);
807a1bf3f78SToomas Soome 		}
808a1bf3f78SToomas Soome 		rv = ficlVmEvaluate(bf_vm, "builtins definitions");
809a1bf3f78SToomas Soome 		if (rv != FICL_VM_STATUS_OUT_OF_TEXT) {
810a1bf3f78SToomas Soome 			printf("error interpreting forth: %d\n", rv);
811a1bf3f78SToomas Soome 			exit(1);
812a1bf3f78SToomas Soome 		}
813a1bf3f78SToomas Soome 	}
814a1bf3f78SToomas Soome 	rv = ficlVmEvaluate(bf_vm, "only forth definitions");
815a1bf3f78SToomas Soome 	if (rv != FICL_VM_STATUS_OUT_OF_TEXT) {
816a1bf3f78SToomas Soome 		printf("error interpreting forth: %d\n", rv);
817a1bf3f78SToomas Soome 		exit(1);
818a1bf3f78SToomas Soome 	}
819a1bf3f78SToomas Soome 
820a1bf3f78SToomas Soome 	/*
821a1bf3f78SToomas Soome 	 * Export some version numbers so that code can detect the
822a1bf3f78SToomas Soome 	 * loader/host version
823a1bf3f78SToomas Soome 	 */
824a1bf3f78SToomas Soome 	env = ficlSystemGetEnvironment(bf_sys);
825a1bf3f78SToomas Soome 	ficlDictionarySetConstant(env, "loader_version",
826a1bf3f78SToomas Soome 	    (bootprog_rev[0] - '0') * 10 + (bootprog_rev[2] - '0'));
827a1bf3f78SToomas Soome 
828a1bf3f78SToomas Soome 	/* try to load and run init file if present */
829a1bf3f78SToomas Soome 	if (rc == NULL)
830a1bf3f78SToomas Soome 		rc = "/boot/forth/boot.4th";
831a1bf3f78SToomas Soome 	if (*rc != '\0') {
832a1bf3f78SToomas Soome 		fd = open(rc, O_RDONLY);
833a1bf3f78SToomas Soome 		if (fd != -1) {
834a1bf3f78SToomas Soome 			(void) ficlExecFD(bf_vm, fd);
835a1bf3f78SToomas Soome 			close(fd);
836a1bf3f78SToomas Soome 		}
837a1bf3f78SToomas Soome 	}
838a1bf3f78SToomas Soome 
839a1bf3f78SToomas Soome 	return (bf_vm);
840a1bf3f78SToomas Soome }
841a1bf3f78SToomas Soome 
842a1bf3f78SToomas Soome void
843a1bf3f78SToomas Soome bf_fini(void)
844a1bf3f78SToomas Soome {
845a1bf3f78SToomas Soome 	ficlSystemDestroy(bf_sys);
846a1bf3f78SToomas Soome }
847a1bf3f78SToomas Soome 
848a1bf3f78SToomas Soome /*
849a1bf3f78SToomas Soome  * Feed a line of user input to the Forth interpreter
850a1bf3f78SToomas Soome  */
851a1bf3f78SToomas Soome int
852a1bf3f78SToomas Soome bf_run(char *line)
853a1bf3f78SToomas Soome {
854a1bf3f78SToomas Soome 	int result;
855a1bf3f78SToomas Soome 	ficlString s;
856a1bf3f78SToomas Soome 
857a1bf3f78SToomas Soome 	FICL_STRING_SET_FROM_CSTRING(s, line);
858a1bf3f78SToomas Soome 	result = ficlVmExecuteString(bf_vm, s);
859a1bf3f78SToomas Soome 
860a1bf3f78SToomas Soome 	switch (result) {
861a1bf3f78SToomas Soome 	case FICL_VM_STATUS_OUT_OF_TEXT:
862a1bf3f78SToomas Soome 	case FICL_VM_STATUS_ABORTQ:
863a1bf3f78SToomas Soome 	case FICL_VM_STATUS_QUIT:
864a1bf3f78SToomas Soome 	case FICL_VM_STATUS_ERROR_EXIT:
865a1bf3f78SToomas Soome 	break;
866a1bf3f78SToomas Soome 	case FICL_VM_STATUS_USER_EXIT:
867a1bf3f78SToomas Soome 	break;
868a1bf3f78SToomas Soome 	case FICL_VM_STATUS_ABORT:
869a1bf3f78SToomas Soome 		printf("Aborted!\n");
870a1bf3f78SToomas Soome 	break;
871a1bf3f78SToomas Soome 	case BF_PARSE:
872a1bf3f78SToomas Soome 		printf("Parse error!\n");
873a1bf3f78SToomas Soome 	break;
874a1bf3f78SToomas Soome 	default:
875af7edebeSToomas Soome 		if (command_errmsg != NULL) {
876a1bf3f78SToomas Soome 			printf("%s\n", command_errmsg);
877af7edebeSToomas Soome 			command_errmsg = NULL;
878af7edebeSToomas Soome 		}
879a1bf3f78SToomas Soome 	}
880a1bf3f78SToomas Soome 
881a1bf3f78SToomas Soome 	setenv("interpret", bf_vm->state ? "" : "ok", 1);
882a1bf3f78SToomas Soome 
883a1bf3f78SToomas Soome 	return (result);
884a1bf3f78SToomas Soome }
885a1bf3f78SToomas Soome 
886a1bf3f78SToomas Soome char *
887a1bf3f78SToomas Soome get_dev(const char *path)
888a1bf3f78SToomas Soome {
889a1bf3f78SToomas Soome 	FILE *fp;
890a1bf3f78SToomas Soome 	struct mnttab mpref = {0};
891a1bf3f78SToomas Soome 	struct mnttab mp = {0};
892a1bf3f78SToomas Soome 	char *currdev;
893a1bf3f78SToomas Soome 	int ret;
894a1bf3f78SToomas Soome 	char *buf;
895a1bf3f78SToomas Soome 	char *tmppath;
896a1bf3f78SToomas Soome 	char *tmpdev;
897a1bf3f78SToomas Soome 	char *cwd = NULL;
898a1bf3f78SToomas Soome 
899a1bf3f78SToomas Soome 	fp = fopen(MNTTAB, "r");
900a1bf3f78SToomas Soome 
901a1bf3f78SToomas Soome 	/* do the best we can to return something... */
902a1bf3f78SToomas Soome 	if (fp == NULL)
903a1bf3f78SToomas Soome 		return (strdup(path));
904a1bf3f78SToomas Soome 
905a1bf3f78SToomas Soome 	/*
906a1bf3f78SToomas Soome 	 * the path can have device provided, check for it
907a1bf3f78SToomas Soome 	 * and extract it.
908a1bf3f78SToomas Soome 	 */
909a1bf3f78SToomas Soome 	buf = strrchr(path, ':');
910a1bf3f78SToomas Soome 	if (buf != NULL) {
911a1bf3f78SToomas Soome 		tmppath = buf+1;		/* real path */
912a1bf3f78SToomas Soome 		buf = strchr(path, ':');	/* skip zfs: */
913a1bf3f78SToomas Soome 		buf++;
914a1bf3f78SToomas Soome 		tmpdev = strdup(buf);
915a1bf3f78SToomas Soome 		buf = strchr(tmpdev, ':');	/* get ending : */
916a1bf3f78SToomas Soome 		*buf = '\0';
917a1bf3f78SToomas Soome 	} else {
918a1bf3f78SToomas Soome 		tmppath = (char *)path;
919a1bf3f78SToomas Soome 		if (tmppath[0] != '/')
920a1bf3f78SToomas Soome 			if ((cwd = getcwd(NULL, PATH_MAX)) == NULL) {
921a1bf3f78SToomas Soome 				(void) fclose(fp);
922a1bf3f78SToomas Soome 				return (strdup(path));
923a1bf3f78SToomas Soome 			}
924a1bf3f78SToomas Soome 
925a1bf3f78SToomas Soome 		currdev = getenv("currdev");
926a1bf3f78SToomas Soome 		buf = strchr(currdev, ':');	/* skip zfs: */
927a1bf3f78SToomas Soome 		if (buf == NULL) {
928a1bf3f78SToomas Soome 			(void) fclose(fp);
929a1bf3f78SToomas Soome 			return (strdup(path));
930a1bf3f78SToomas Soome 		}
931a1bf3f78SToomas Soome 		buf++;
932a1bf3f78SToomas Soome 		tmpdev = strdup(buf);
933a1bf3f78SToomas Soome 		buf = strchr(tmpdev, ':');	/* get ending : */
934a1bf3f78SToomas Soome 		*buf = '\0';
935a1bf3f78SToomas Soome 	}
936a1bf3f78SToomas Soome 
937a1bf3f78SToomas Soome 	mpref.mnt_special = tmpdev;
938a1bf3f78SToomas Soome 	ret = getmntany(fp, &mp, &mpref);
939a1bf3f78SToomas Soome 	(void) fclose(fp);
940a1bf3f78SToomas Soome 	free(tmpdev);
941a1bf3f78SToomas Soome 
942a1bf3f78SToomas Soome 	if (cwd == NULL)
943a1bf3f78SToomas Soome 		(void) asprintf(&buf, "%s/%s", ret? "":mp.mnt_mountp, tmppath);
944a1bf3f78SToomas Soome 	else {
945a1bf3f78SToomas Soome 		(void) asprintf(&buf, "%s/%s/%s", ret? "":mp.mnt_mountp, cwd,
946a1bf3f78SToomas Soome 		    tmppath);
947a1bf3f78SToomas Soome 		free(cwd);
948a1bf3f78SToomas Soome 	}
949a1bf3f78SToomas Soome 	return (buf);
950a1bf3f78SToomas Soome }
951a1bf3f78SToomas Soome 
952a1bf3f78SToomas Soome static void
953a1bf3f78SToomas Soome ngets(char *buf, int n)
954a1bf3f78SToomas Soome {
955a1bf3f78SToomas Soome 	int c;
956a1bf3f78SToomas Soome 	char *lp;
957a1bf3f78SToomas Soome 
958a1bf3f78SToomas Soome 	for (lp = buf; ; )
959a1bf3f78SToomas Soome 		switch (c = getchar() & 0177) {
960a1bf3f78SToomas Soome 		case '\n':
961a1bf3f78SToomas Soome 		case '\r':
962a1bf3f78SToomas Soome 			*lp = '\0';
963a1bf3f78SToomas Soome 			putchar('\n');
964a1bf3f78SToomas Soome 		return;
965a1bf3f78SToomas Soome 		case '\b':
966a1bf3f78SToomas Soome 		case '\177':
967a1bf3f78SToomas Soome 			if (lp > buf) {
968a1bf3f78SToomas Soome 				lp--;
969a1bf3f78SToomas Soome 				putchar('\b');
970a1bf3f78SToomas Soome 				putchar(' ');
971a1bf3f78SToomas Soome 				putchar('\b');
972a1bf3f78SToomas Soome 			}
973a1bf3f78SToomas Soome 		break;
974a1bf3f78SToomas Soome 		case 'r'&037: {
975a1bf3f78SToomas Soome 			char *p;
976a1bf3f78SToomas Soome 
977a1bf3f78SToomas Soome 			putchar('\n');
978a1bf3f78SToomas Soome 			for (p = buf; p < lp; ++p)
979a1bf3f78SToomas Soome 				putchar(*p);
980a1bf3f78SToomas Soome 		break;
981a1bf3f78SToomas Soome 		}
982a1bf3f78SToomas Soome 		case 'u'&037:
983a1bf3f78SToomas Soome 		case 'w'&037:
984a1bf3f78SToomas Soome 			lp = buf;
985a1bf3f78SToomas Soome 			putchar('\n');
986a1bf3f78SToomas Soome 		break;
987a1bf3f78SToomas Soome 		default:
988a1bf3f78SToomas Soome 			if ((n < 1) || ((lp - buf) < n - 1)) {
989a1bf3f78SToomas Soome 				*lp++ = c;
990a1bf3f78SToomas Soome 				putchar(c);
991a1bf3f78SToomas Soome 			}
992a1bf3f78SToomas Soome 		}
993a1bf3f78SToomas Soome 	/*NOTREACHED*/
994a1bf3f78SToomas Soome }
995a1bf3f78SToomas Soome 
996a1bf3f78SToomas Soome static int
997a1bf3f78SToomas Soome fgetstr(char *buf, int size, int fd)
998a1bf3f78SToomas Soome {
999a1bf3f78SToomas Soome 	char c;
1000a1bf3f78SToomas Soome 	int err, len;
1001a1bf3f78SToomas Soome 
1002a1bf3f78SToomas Soome 	size--;			/* leave space for terminator */
1003a1bf3f78SToomas Soome 	len = 0;
1004a1bf3f78SToomas Soome 	while (size != 0) {
1005a1bf3f78SToomas Soome 		err = read(fd, &c, sizeof (c));
1006a1bf3f78SToomas Soome 		if (err < 0)			/* read error */
1007a1bf3f78SToomas Soome 			return (-1);
1008a1bf3f78SToomas Soome 
1009a1bf3f78SToomas Soome 		if (err == 0) {	/* EOF */
1010a1bf3f78SToomas Soome 			if (len == 0)
1011a1bf3f78SToomas Soome 				return (-1);	/* nothing to read */
1012a1bf3f78SToomas Soome 			break;
1013a1bf3f78SToomas Soome 		}
1014a1bf3f78SToomas Soome 		if ((c == '\r') || (c == '\n'))	/* line terminators */
1015a1bf3f78SToomas Soome 			break;
1016a1bf3f78SToomas Soome 		*buf++ = c;			/* keep char */
1017a1bf3f78SToomas Soome 		size--;
1018a1bf3f78SToomas Soome 		len++;
1019a1bf3f78SToomas Soome 	}
1020a1bf3f78SToomas Soome 	*buf = 0;
1021a1bf3f78SToomas Soome 	return (len);
1022a1bf3f78SToomas Soome }
1023a1bf3f78SToomas Soome 
1024a1bf3f78SToomas Soome static char *
1025a1bf3f78SToomas Soome unargv(int argc, char *argv[])
1026a1bf3f78SToomas Soome {
1027a1bf3f78SToomas Soome 	size_t hlong;
1028a1bf3f78SToomas Soome 	int i;
1029a1bf3f78SToomas Soome 	char *cp;
1030a1bf3f78SToomas Soome 
1031a1bf3f78SToomas Soome 	for (i = 0, hlong = 0; i < argc; i++)
1032a1bf3f78SToomas Soome 		hlong += strlen(argv[i]) + 2;
1033a1bf3f78SToomas Soome 
1034a1bf3f78SToomas Soome 	if (hlong == 0)
1035a1bf3f78SToomas Soome 		return (NULL);
1036a1bf3f78SToomas Soome 
1037a1bf3f78SToomas Soome 	cp = malloc(hlong);
1038a1bf3f78SToomas Soome 	cp[0] = 0;
1039a1bf3f78SToomas Soome 	for (i = 0; i < argc; i++) {
1040a1bf3f78SToomas Soome 		strcat(cp, argv[i]);
1041a1bf3f78SToomas Soome 		if (i < (argc - 1))
1042a1bf3f78SToomas Soome 			strcat(cp, " ");
1043a1bf3f78SToomas Soome 	}
1044a1bf3f78SToomas Soome 
1045a1bf3f78SToomas Soome 	return (cp);
1046a1bf3f78SToomas Soome }
1047a1bf3f78SToomas Soome 
1048a1bf3f78SToomas Soome /*
1049a1bf3f78SToomas Soome  * Help is read from a formatted text file.
1050a1bf3f78SToomas Soome  *
1051a1bf3f78SToomas Soome  * Entries in the file are formatted as:
1052a1bf3f78SToomas Soome  * # Ttopic [Ssubtopic] Ddescription
1053a1bf3f78SToomas Soome  * help
1054a1bf3f78SToomas Soome  * text
1055a1bf3f78SToomas Soome  * here
1056a1bf3f78SToomas Soome  * #
1057a1bf3f78SToomas Soome  *
1058a1bf3f78SToomas Soome  * Note that for code simplicity's sake, the above format must be followed
1059a1bf3f78SToomas Soome  * exactly.
1060a1bf3f78SToomas Soome  *
1061a1bf3f78SToomas Soome  * Subtopic entries must immediately follow the topic (this is used to
1062a1bf3f78SToomas Soome  * produce the listing of subtopics).
1063a1bf3f78SToomas Soome  *
1064a1bf3f78SToomas Soome  * If no argument(s) are supplied by the user, the help for 'help' is displayed.
1065a1bf3f78SToomas Soome  */
1066a1bf3f78SToomas Soome static int
1067a1bf3f78SToomas Soome help_getnext(int fd, char **topic, char **subtopic, char **desc)
1068a1bf3f78SToomas Soome {
1069a1bf3f78SToomas Soome 	char line[81], *cp, *ep;
1070a1bf3f78SToomas Soome 
1071a1bf3f78SToomas Soome 	for (;;) {
1072a1bf3f78SToomas Soome 		if (fgetstr(line, 80, fd) < 0)
1073a1bf3f78SToomas Soome 			return (0);
1074a1bf3f78SToomas Soome 
1075a1bf3f78SToomas Soome 		if ((strlen(line) < 3) || (line[0] != '#') || (line[1] != ' '))
1076a1bf3f78SToomas Soome 			continue;
1077a1bf3f78SToomas Soome 
1078a1bf3f78SToomas Soome 		*topic = *subtopic = *desc = NULL;
1079a1bf3f78SToomas Soome 		cp = line + 2;
1080a1bf3f78SToomas Soome 		while ((cp != NULL) && (*cp != 0)) {
1081a1bf3f78SToomas Soome 			ep = strchr(cp, ' ');
1082a1bf3f78SToomas Soome 			if ((*cp == 'T') && (*topic == NULL)) {
1083a1bf3f78SToomas Soome 				if (ep != NULL)
1084a1bf3f78SToomas Soome 					*ep++ = 0;
1085a1bf3f78SToomas Soome 				*topic = strdup(cp + 1);
1086a1bf3f78SToomas Soome 			} else if ((*cp == 'S') && (*subtopic == NULL)) {
1087a1bf3f78SToomas Soome 				if (ep != NULL)
1088a1bf3f78SToomas Soome 					*ep++ = 0;
1089a1bf3f78SToomas Soome 				*subtopic = strdup(cp + 1);
1090a1bf3f78SToomas Soome 			} else if (*cp == 'D') {
1091a1bf3f78SToomas Soome 				*desc = strdup(cp + 1);
1092a1bf3f78SToomas Soome 				ep = NULL;
1093a1bf3f78SToomas Soome 			}
1094a1bf3f78SToomas Soome 			cp = ep;
1095a1bf3f78SToomas Soome 		}
1096a1bf3f78SToomas Soome 		if (*topic == NULL) {
1097a1bf3f78SToomas Soome 			if (*subtopic != NULL)
1098a1bf3f78SToomas Soome 				free(*subtopic);
1099a1bf3f78SToomas Soome 			if (*desc != NULL)
1100a1bf3f78SToomas Soome 				free(*desc);
1101a1bf3f78SToomas Soome 			continue;
1102a1bf3f78SToomas Soome 		}
1103a1bf3f78SToomas Soome 		return (1);
1104a1bf3f78SToomas Soome 	}
1105a1bf3f78SToomas Soome }
1106a1bf3f78SToomas Soome 
1107a1bf3f78SToomas Soome static int
1108a1bf3f78SToomas Soome help_emitsummary(char *topic, char *subtopic, char *desc)
1109a1bf3f78SToomas Soome {
1110a1bf3f78SToomas Soome 	int i;
1111a1bf3f78SToomas Soome 
1112a1bf3f78SToomas Soome 	pager_output("    ");
1113a1bf3f78SToomas Soome 	pager_output(topic);
1114a1bf3f78SToomas Soome 	i = strlen(topic);
1115a1bf3f78SToomas Soome 	if (subtopic != NULL) {
1116a1bf3f78SToomas Soome 		pager_output(" ");
1117a1bf3f78SToomas Soome 		pager_output(subtopic);
1118a1bf3f78SToomas Soome 		i += strlen(subtopic) + 1;
1119a1bf3f78SToomas Soome 	}
1120a1bf3f78SToomas Soome 	if (desc != NULL) {
1121a1bf3f78SToomas Soome 		do {
1122a1bf3f78SToomas Soome 			pager_output(" ");
1123a1bf3f78SToomas Soome 		} while (i++ < 30);
1124a1bf3f78SToomas Soome 		pager_output(desc);
1125a1bf3f78SToomas Soome 	}
1126a1bf3f78SToomas Soome 	return (pager_output("\n"));
1127a1bf3f78SToomas Soome }
1128a1bf3f78SToomas Soome 
1129a1bf3f78SToomas Soome static int
1130a1bf3f78SToomas Soome command_help(int argc, char *argv[])
1131a1bf3f78SToomas Soome {
1132a1bf3f78SToomas Soome 	char buf[81];	/* XXX buffer size? */
1133a1bf3f78SToomas Soome 	int hfd, matched, doindex;
1134a1bf3f78SToomas Soome 	char *topic, *subtopic, *t, *s, *d;
1135a1bf3f78SToomas Soome 
1136a1bf3f78SToomas Soome 	/* page the help text from our load path */
1137a1bf3f78SToomas Soome 	sprintf(buf, "/boot/loader.help");
1138a1bf3f78SToomas Soome 	if ((hfd = open(buf, O_RDONLY)) < 0) {
1139a1bf3f78SToomas Soome 		printf("Verbose help not available, "
1140a1bf3f78SToomas Soome 		    "use '?' to list commands\n");
1141a1bf3f78SToomas Soome 		return (CMD_OK);
1142a1bf3f78SToomas Soome 	}
1143a1bf3f78SToomas Soome 
1144a1bf3f78SToomas Soome 	/* pick up request from arguments */
1145a1bf3f78SToomas Soome 	topic = subtopic = NULL;
1146a1bf3f78SToomas Soome 	switch (argc) {
1147a1bf3f78SToomas Soome 	case 3:
1148a1bf3f78SToomas Soome 		subtopic = strdup(argv[2]);
1149*3451cb4fSToomas Soome 		/* FALLTHROUGH */
1150a1bf3f78SToomas Soome 	case 2:
1151a1bf3f78SToomas Soome 		topic = strdup(argv[1]);
1152a1bf3f78SToomas Soome 	break;
1153a1bf3f78SToomas Soome 	case 1:
1154a1bf3f78SToomas Soome 		topic = strdup("help");
1155a1bf3f78SToomas Soome 	break;
1156a1bf3f78SToomas Soome 	default:
1157a1bf3f78SToomas Soome 		command_errmsg = "usage is 'help <topic> [<subtopic>]";
1158a1bf3f78SToomas Soome 		close(hfd);
1159a1bf3f78SToomas Soome 		return (CMD_ERROR);
1160a1bf3f78SToomas Soome 	}
1161a1bf3f78SToomas Soome 
1162a1bf3f78SToomas Soome 	/* magic "index" keyword */
1163a1bf3f78SToomas Soome 	doindex = strcmp(topic, "index") == 0;
1164a1bf3f78SToomas Soome 	matched = doindex;
1165a1bf3f78SToomas Soome 
1166a1bf3f78SToomas Soome 	/* Scan the helpfile looking for help matching the request */
1167a1bf3f78SToomas Soome 	pager_open();
1168a1bf3f78SToomas Soome 	while (help_getnext(hfd, &t, &s, &d)) {
1169a1bf3f78SToomas Soome 		if (doindex) {		/* dink around formatting */
1170a1bf3f78SToomas Soome 			if (help_emitsummary(t, s, d))
1171a1bf3f78SToomas Soome 				break;
1172a1bf3f78SToomas Soome 
1173a1bf3f78SToomas Soome 		} else if (strcmp(topic, t)) {
1174a1bf3f78SToomas Soome 			/* topic mismatch */
1175a1bf3f78SToomas Soome 			/* nothing more on this topic, stop scanning */
1176a1bf3f78SToomas Soome 			if (matched)
1177a1bf3f78SToomas Soome 				break;
1178a1bf3f78SToomas Soome 		} else {
1179a1bf3f78SToomas Soome 			/* topic matched */
1180a1bf3f78SToomas Soome 			matched = 1;
1181a1bf3f78SToomas Soome 			if (((subtopic == NULL) && (s == NULL)) ||
1182a1bf3f78SToomas Soome 			    ((subtopic != NULL) && (s != NULL) &&
1183a1bf3f78SToomas Soome 			    strcmp(subtopic, s) == 0)) {
1184a1bf3f78SToomas Soome 				/* exact match, print text */
1185a1bf3f78SToomas Soome 				while ((fgetstr(buf, 80, hfd) >= 0) &&
1186a1bf3f78SToomas Soome 				    (buf[0] != '#')) {
1187a1bf3f78SToomas Soome 					if (pager_output(buf))
1188a1bf3f78SToomas Soome 						break;
1189a1bf3f78SToomas Soome 					if (pager_output("\n"))
1190a1bf3f78SToomas Soome 						break;
1191a1bf3f78SToomas Soome 				}
1192a1bf3f78SToomas Soome 			} else if ((subtopic == NULL) && (s != NULL)) {
1193a1bf3f78SToomas Soome 				/* topic match, list subtopics */
1194a1bf3f78SToomas Soome 				if (help_emitsummary(t, s, d))
1195a1bf3f78SToomas Soome 					break;
1196a1bf3f78SToomas Soome 			}
1197a1bf3f78SToomas Soome 		}
1198a1bf3f78SToomas Soome 		free(t);
1199a1bf3f78SToomas Soome 		free(s);
1200a1bf3f78SToomas Soome 		free(d);
1201a1bf3f78SToomas Soome 	}
1202a1bf3f78SToomas Soome 	pager_close();
1203a1bf3f78SToomas Soome 	close(hfd);
1204a1bf3f78SToomas Soome 	if (!matched) {
1205a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1206a1bf3f78SToomas Soome 		    "no help available for '%s'", topic);
1207a1bf3f78SToomas Soome 		free(topic);
1208a1bf3f78SToomas Soome 		if (subtopic)
1209a1bf3f78SToomas Soome 			free(subtopic);
1210a1bf3f78SToomas Soome 		return (CMD_ERROR);
1211a1bf3f78SToomas Soome 	}
1212a1bf3f78SToomas Soome 	free(topic);
1213a1bf3f78SToomas Soome 	if (subtopic)
1214a1bf3f78SToomas Soome 		free(subtopic);
1215a1bf3f78SToomas Soome 	return (CMD_OK);
1216a1bf3f78SToomas Soome }
1217a1bf3f78SToomas Soome 
1218a1bf3f78SToomas Soome static int
1219a1bf3f78SToomas Soome command_commandlist(int argc, char *argv[])
1220a1bf3f78SToomas Soome {
1221a1bf3f78SToomas Soome 	struct bootblk_command *cmdp;
1222a1bf3f78SToomas Soome 	int res;
1223a1bf3f78SToomas Soome 	char name[20];
1224a1bf3f78SToomas Soome 
1225a1bf3f78SToomas Soome 	res = 0;
1226a1bf3f78SToomas Soome 	pager_open();
1227a1bf3f78SToomas Soome 	res = pager_output("Available commands:\n");
1228a1bf3f78SToomas Soome 	cmdp = NULL;
1229a1bf3f78SToomas Soome 	STAILQ_FOREACH(cmdp, &commands, next) {
1230a1bf3f78SToomas Soome 		if (res)
1231a1bf3f78SToomas Soome 			break;
1232a1bf3f78SToomas Soome 		if ((cmdp->c_name != NULL) && (cmdp->c_desc != NULL)) {
1233a1bf3f78SToomas Soome 			sprintf(name, "  %-15s  ", cmdp->c_name);
1234a1bf3f78SToomas Soome 			pager_output(name);
1235a1bf3f78SToomas Soome 			pager_output(cmdp->c_desc);
1236a1bf3f78SToomas Soome 			res = pager_output("\n");
1237a1bf3f78SToomas Soome 		}
1238a1bf3f78SToomas Soome 	}
1239a1bf3f78SToomas Soome 	pager_close();
1240a1bf3f78SToomas Soome 	return (CMD_OK);
1241a1bf3f78SToomas Soome }
1242a1bf3f78SToomas Soome 
1243a1bf3f78SToomas Soome /*
1244a1bf3f78SToomas Soome  * XXX set/show should become set/echo if we have variable
1245a1bf3f78SToomas Soome  * substitution happening.
1246a1bf3f78SToomas Soome  */
1247a1bf3f78SToomas Soome static int
1248a1bf3f78SToomas Soome command_show(int argc, char *argv[])
1249a1bf3f78SToomas Soome {
1250a1bf3f78SToomas Soome 	char **ev;
1251a1bf3f78SToomas Soome 	char *cp;
1252a1bf3f78SToomas Soome 
1253a1bf3f78SToomas Soome 	if (argc < 2) {
1254a1bf3f78SToomas Soome 		/*
1255a1bf3f78SToomas Soome 		 * With no arguments, print everything.
1256a1bf3f78SToomas Soome 		 */
1257a1bf3f78SToomas Soome 		pager_open();
1258a1bf3f78SToomas Soome 		for (ev = _environ; *ev != NULL; ev++) {
1259a1bf3f78SToomas Soome 			pager_output(*ev);
1260a1bf3f78SToomas Soome 			cp = getenv(*ev);
1261a1bf3f78SToomas Soome 			if (cp != NULL) {
1262a1bf3f78SToomas Soome 				pager_output("=");
1263a1bf3f78SToomas Soome 				pager_output(cp);
1264a1bf3f78SToomas Soome 			}
1265a1bf3f78SToomas Soome 			if (pager_output("\n"))
1266a1bf3f78SToomas Soome 				break;
1267a1bf3f78SToomas Soome 		}
1268a1bf3f78SToomas Soome 		pager_close();
1269a1bf3f78SToomas Soome 	} else {
1270a1bf3f78SToomas Soome 		if ((cp = getenv(argv[1])) != NULL) {
1271a1bf3f78SToomas Soome 			printf("%s\n", cp);
1272a1bf3f78SToomas Soome 		} else {
1273a1bf3f78SToomas Soome 			snprintf(command_errbuf, sizeof (command_errbuf),
1274a1bf3f78SToomas Soome 			    "variable '%s' not found", argv[1]);
1275a1bf3f78SToomas Soome 			return (CMD_ERROR);
1276a1bf3f78SToomas Soome 		}
1277a1bf3f78SToomas Soome 	}
1278a1bf3f78SToomas Soome 	return (CMD_OK);
1279a1bf3f78SToomas Soome }
1280a1bf3f78SToomas Soome 
1281a1bf3f78SToomas Soome static int
1282a1bf3f78SToomas Soome command_set(int argc, char *argv[])
1283a1bf3f78SToomas Soome {
1284a1bf3f78SToomas Soome 	int	err;
1285a1bf3f78SToomas Soome 	char	*value, *copy;
1286a1bf3f78SToomas Soome 
1287a1bf3f78SToomas Soome 	if (argc != 2) {
1288a1bf3f78SToomas Soome 		command_errmsg = "wrong number of arguments";
1289a1bf3f78SToomas Soome 		return (CMD_ERROR);
1290a1bf3f78SToomas Soome 	} else {
1291a1bf3f78SToomas Soome 		copy = strdup(argv[1]);
1292a1bf3f78SToomas Soome 		if (copy == NULL) {
1293a1bf3f78SToomas Soome 			command_errmsg = strerror(errno);
1294a1bf3f78SToomas Soome 			return (CMD_ERROR);
1295a1bf3f78SToomas Soome 		}
1296a1bf3f78SToomas Soome 		if ((value = strchr(copy, '=')) != NULL)
1297a1bf3f78SToomas Soome 			*(value++) = 0;
1298a1bf3f78SToomas Soome 		else
1299a1bf3f78SToomas Soome 			value = "";
1300a1bf3f78SToomas Soome 		if ((err = setenv(copy, value, 1)) != 0) {
1301a1bf3f78SToomas Soome 			free(copy);
1302a1bf3f78SToomas Soome 			command_errmsg = strerror(errno);
1303a1bf3f78SToomas Soome 			return (CMD_ERROR);
1304a1bf3f78SToomas Soome 		}
1305a1bf3f78SToomas Soome 		free(copy);
1306a1bf3f78SToomas Soome 	}
1307a1bf3f78SToomas Soome 	return (CMD_OK);
1308a1bf3f78SToomas Soome }
1309a1bf3f78SToomas Soome 
1310a1bf3f78SToomas Soome static int
1311a1bf3f78SToomas Soome command_setprop(int argc, char *argv[])
1312a1bf3f78SToomas Soome {
1313a1bf3f78SToomas Soome 	int err;
1314a1bf3f78SToomas Soome 
1315a1bf3f78SToomas Soome 	if (argc != 3) {
1316a1bf3f78SToomas Soome 		command_errmsg = "wrong number of arguments";
1317a1bf3f78SToomas Soome 		return (CMD_ERROR);
1318a1bf3f78SToomas Soome 	} else {
1319a1bf3f78SToomas Soome 		if ((err = setenv(argv[1], argv[2], 1)) != 0) {
1320a1bf3f78SToomas Soome 			command_errmsg = strerror(err);
1321a1bf3f78SToomas Soome 			return (CMD_ERROR);
1322a1bf3f78SToomas Soome 		}
1323a1bf3f78SToomas Soome 	}
1324a1bf3f78SToomas Soome 	return (CMD_OK);
1325a1bf3f78SToomas Soome }
1326a1bf3f78SToomas Soome 
1327a1bf3f78SToomas Soome static int
1328a1bf3f78SToomas Soome command_unset(int argc, char *argv[])
1329a1bf3f78SToomas Soome {
1330a1bf3f78SToomas Soome 	int err;
1331a1bf3f78SToomas Soome 
1332a1bf3f78SToomas Soome 	if (argc != 2) {
1333a1bf3f78SToomas Soome 		command_errmsg = "wrong number of arguments";
1334a1bf3f78SToomas Soome 		return (CMD_ERROR);
1335a1bf3f78SToomas Soome 	} else {
1336a1bf3f78SToomas Soome 		if ((err = unsetenv(argv[1])) != 0) {
1337a1bf3f78SToomas Soome 			command_errmsg = strerror(err);
1338a1bf3f78SToomas Soome 			return (CMD_ERROR);
1339a1bf3f78SToomas Soome 		}
1340a1bf3f78SToomas Soome 	}
1341a1bf3f78SToomas Soome 	return (CMD_OK);
1342a1bf3f78SToomas Soome }
1343a1bf3f78SToomas Soome 
1344a1bf3f78SToomas Soome static int
1345a1bf3f78SToomas Soome command_echo(int argc, char *argv[])
1346a1bf3f78SToomas Soome {
1347a1bf3f78SToomas Soome 	char *s;
1348a1bf3f78SToomas Soome 	int nl, ch;
1349a1bf3f78SToomas Soome 
1350a1bf3f78SToomas Soome 	nl = 0;
1351a1bf3f78SToomas Soome 	optind = 1;
1352a1bf3f78SToomas Soome 	opterr = 1;
1353a1bf3f78SToomas Soome 	while ((ch = getopt(argc, argv, "n")) != -1) {
1354a1bf3f78SToomas Soome 		switch (ch) {
1355a1bf3f78SToomas Soome 		case 'n':
1356a1bf3f78SToomas Soome 			nl = 1;
1357a1bf3f78SToomas Soome 		break;
1358a1bf3f78SToomas Soome 		case '?':
1359a1bf3f78SToomas Soome 		default:
1360a1bf3f78SToomas Soome 			/* getopt has already reported an error */
1361a1bf3f78SToomas Soome 		return (CMD_OK);
1362a1bf3f78SToomas Soome 		}
1363a1bf3f78SToomas Soome 	}
1364a1bf3f78SToomas Soome 	argv += (optind);
1365a1bf3f78SToomas Soome 	argc -= (optind);
1366a1bf3f78SToomas Soome 
1367a1bf3f78SToomas Soome 	s = unargv(argc, argv);
1368a1bf3f78SToomas Soome 	if (s != NULL) {
1369a1bf3f78SToomas Soome 		printf("%s", s);
1370a1bf3f78SToomas Soome 		free(s);
1371a1bf3f78SToomas Soome 	}
1372a1bf3f78SToomas Soome 	if (!nl)
1373a1bf3f78SToomas Soome 		printf("\n");
1374a1bf3f78SToomas Soome 	return (CMD_OK);
1375a1bf3f78SToomas Soome }
1376a1bf3f78SToomas Soome 
1377a1bf3f78SToomas Soome /*
1378a1bf3f78SToomas Soome  * A passable emulation of the sh(1) command of the same name.
1379a1bf3f78SToomas Soome  */
1380a1bf3f78SToomas Soome static int
1381a1bf3f78SToomas Soome ischar(void)
1382a1bf3f78SToomas Soome {
1383a1bf3f78SToomas Soome 	return (1);
1384a1bf3f78SToomas Soome }
1385a1bf3f78SToomas Soome 
1386a1bf3f78SToomas Soome static int
1387a1bf3f78SToomas Soome command_read(int argc, char *argv[])
1388a1bf3f78SToomas Soome {
1389a1bf3f78SToomas Soome 	char *prompt;
1390a1bf3f78SToomas Soome 	int timeout;
1391a1bf3f78SToomas Soome 	time_t when;
1392a1bf3f78SToomas Soome 	char *cp;
1393a1bf3f78SToomas Soome 	char *name;
1394a1bf3f78SToomas Soome 	char buf[256];		/* XXX size? */
1395a1bf3f78SToomas Soome 	int c;
1396a1bf3f78SToomas Soome 
1397a1bf3f78SToomas Soome 	timeout = -1;
1398a1bf3f78SToomas Soome 	prompt = NULL;
1399a1bf3f78SToomas Soome 	optind = 1;
1400a1bf3f78SToomas Soome 	opterr = 1;
1401a1bf3f78SToomas Soome 	while ((c = getopt(argc, argv, "p:t:")) != -1) {
1402a1bf3f78SToomas Soome 		switch (c) {
1403a1bf3f78SToomas Soome 		case 'p':
1404a1bf3f78SToomas Soome 			prompt = optarg;
1405a1bf3f78SToomas Soome 		break;
1406a1bf3f78SToomas Soome 		case 't':
1407a1bf3f78SToomas Soome 			timeout = strtol(optarg, &cp, 0);
1408a1bf3f78SToomas Soome 			if (cp == optarg) {
1409a1bf3f78SToomas Soome 				snprintf(command_errbuf,
1410a1bf3f78SToomas Soome 				    sizeof (command_errbuf),
1411a1bf3f78SToomas Soome 				    "bad timeout '%s'", optarg);
1412a1bf3f78SToomas Soome 				return (CMD_ERROR);
1413a1bf3f78SToomas Soome 			}
1414a1bf3f78SToomas Soome 		break;
1415a1bf3f78SToomas Soome 		default:
1416a1bf3f78SToomas Soome 		return (CMD_OK);
1417a1bf3f78SToomas Soome 		}
1418a1bf3f78SToomas Soome 	}
1419a1bf3f78SToomas Soome 
1420a1bf3f78SToomas Soome 	argv += (optind);
1421a1bf3f78SToomas Soome 	argc -= (optind);
1422a1bf3f78SToomas Soome 	name = (argc > 0) ? argv[0]: NULL;
1423a1bf3f78SToomas Soome 
1424a1bf3f78SToomas Soome 	if (prompt != NULL)
1425a1bf3f78SToomas Soome 		printf("%s", prompt);
1426a1bf3f78SToomas Soome 	if (timeout >= 0) {
1427a1bf3f78SToomas Soome 		when = time(NULL) + timeout;
1428a1bf3f78SToomas Soome 		while (!ischar())
1429a1bf3f78SToomas Soome 			if (time(NULL) >= when)
1430a1bf3f78SToomas Soome 				return (CMD_OK); /* is timeout an error? */
1431a1bf3f78SToomas Soome 	}
1432a1bf3f78SToomas Soome 
1433a1bf3f78SToomas Soome 	ngets(buf, sizeof (buf));
1434a1bf3f78SToomas Soome 
1435a1bf3f78SToomas Soome 	if (name != NULL)
1436a1bf3f78SToomas Soome 		setenv(name, buf, 1);
1437a1bf3f78SToomas Soome 	return (CMD_OK);
1438a1bf3f78SToomas Soome }
1439a1bf3f78SToomas Soome 
1440a1bf3f78SToomas Soome /*
1441a1bf3f78SToomas Soome  * File pager
1442a1bf3f78SToomas Soome  */
1443a1bf3f78SToomas Soome static int
1444a1bf3f78SToomas Soome command_more(int argc, char *argv[])
1445a1bf3f78SToomas Soome {
1446a1bf3f78SToomas Soome 	int i;
1447a1bf3f78SToomas Soome 	int res;
1448a1bf3f78SToomas Soome 	char line[80];
1449a1bf3f78SToomas Soome 	char *name;
1450a1bf3f78SToomas Soome 
1451a1bf3f78SToomas Soome 	res = 0;
1452a1bf3f78SToomas Soome 	pager_open();
1453a1bf3f78SToomas Soome 	for (i = 1; (i < argc) && (res == 0); i++) {
1454a1bf3f78SToomas Soome 		sprintf(line, "*** FILE %s BEGIN ***\n", argv[i]);
1455a1bf3f78SToomas Soome 		if (pager_output(line))
1456a1bf3f78SToomas Soome 			break;
1457a1bf3f78SToomas Soome 		name = get_dev(argv[i]);
1458a1bf3f78SToomas Soome 		res = page_file(name);
1459a1bf3f78SToomas Soome 		free(name);
1460a1bf3f78SToomas Soome 		if (!res) {
1461a1bf3f78SToomas Soome 			sprintf(line, "*** FILE %s END ***\n", argv[i]);
1462a1bf3f78SToomas Soome 			res = pager_output(line);
1463a1bf3f78SToomas Soome 		}
1464a1bf3f78SToomas Soome 	}
1465a1bf3f78SToomas Soome 	pager_close();
1466a1bf3f78SToomas Soome 
1467a1bf3f78SToomas Soome 	if (res == 0)
1468a1bf3f78SToomas Soome 		return (CMD_OK);
1469a1bf3f78SToomas Soome 	return (CMD_ERROR);
1470a1bf3f78SToomas Soome }
1471a1bf3f78SToomas Soome 
1472a1bf3f78SToomas Soome static int
1473a1bf3f78SToomas Soome page_file(char *filename)
1474a1bf3f78SToomas Soome {
1475a1bf3f78SToomas Soome 	int result;
1476a1bf3f78SToomas Soome 
1477a1bf3f78SToomas Soome 	result = pager_file(filename);
1478a1bf3f78SToomas Soome 
1479a1bf3f78SToomas Soome 	if (result == -1) {
1480a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1481a1bf3f78SToomas Soome 		    "error showing %s", filename);
1482a1bf3f78SToomas Soome 	}
1483a1bf3f78SToomas Soome 
1484a1bf3f78SToomas Soome 	return (result);
1485a1bf3f78SToomas Soome }
1486a1bf3f78SToomas Soome 
1487a1bf3f78SToomas Soome static int
1488a1bf3f78SToomas Soome command_ls(int argc, char *argv[])
1489a1bf3f78SToomas Soome {
1490a1bf3f78SToomas Soome 	DIR *dir;
1491a1bf3f78SToomas Soome 	int fd;
1492a1bf3f78SToomas Soome 	struct stat sb;
1493a1bf3f78SToomas Soome 	struct dirent *d;
1494a1bf3f78SToomas Soome 	char *buf, *path;
1495a1bf3f78SToomas Soome 	char lbuf[128];	/* one line */
1496a1bf3f78SToomas Soome 	int result, ch;
1497a1bf3f78SToomas Soome 	int verbose;
1498a1bf3f78SToomas Soome 
1499a1bf3f78SToomas Soome 	result = CMD_OK;
1500a1bf3f78SToomas Soome 	fd = -1;
1501a1bf3f78SToomas Soome 	verbose = 0;
1502a1bf3f78SToomas Soome 	optind = 1;
1503a1bf3f78SToomas Soome 	opterr = 1;
1504a1bf3f78SToomas Soome 	while ((ch = getopt(argc, argv, "l")) != -1) {
1505a1bf3f78SToomas Soome 		switch (ch) {
1506a1bf3f78SToomas Soome 		case 'l':
1507a1bf3f78SToomas Soome 			verbose = 1;
1508a1bf3f78SToomas Soome 		break;
1509a1bf3f78SToomas Soome 		case '?':
1510a1bf3f78SToomas Soome 		default:
1511a1bf3f78SToomas Soome 			/* getopt has already reported an error */
1512a1bf3f78SToomas Soome 		return (CMD_OK);
1513a1bf3f78SToomas Soome 		}
1514a1bf3f78SToomas Soome 	}
1515a1bf3f78SToomas Soome 	argv += (optind - 1);
1516a1bf3f78SToomas Soome 	argc -= (optind - 1);
1517a1bf3f78SToomas Soome 
1518a1bf3f78SToomas Soome 	if (argc < 2) {
1519a1bf3f78SToomas Soome 		path = "";
1520a1bf3f78SToomas Soome 	} else {
1521a1bf3f78SToomas Soome 		path = argv[1];
1522a1bf3f78SToomas Soome 	}
1523a1bf3f78SToomas Soome 
1524a1bf3f78SToomas Soome 	fd = ls_getdir(&path);
1525a1bf3f78SToomas Soome 	if (fd == -1) {
1526a1bf3f78SToomas Soome 		result = CMD_ERROR;
1527a1bf3f78SToomas Soome 		goto out;
1528a1bf3f78SToomas Soome 	}
1529a1bf3f78SToomas Soome 	dir = fdopendir(fd);
1530a1bf3f78SToomas Soome 	pager_open();
1531a1bf3f78SToomas Soome 	pager_output(path);
1532a1bf3f78SToomas Soome 	pager_output("\n");
1533a1bf3f78SToomas Soome 
1534a1bf3f78SToomas Soome 	while ((d = readdir(dir)) != NULL) {
1535a1bf3f78SToomas Soome 		if (strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) {
1536a1bf3f78SToomas Soome 			/* stat the file, if possible */
1537a1bf3f78SToomas Soome 			sb.st_size = 0;
1538a1bf3f78SToomas Soome 			sb.st_mode = 0;
1539a1bf3f78SToomas Soome 			buf = malloc(strlen(path) + strlen(d->d_name) + 2);
1540a1bf3f78SToomas Soome 			if (path[0] == '\0')
1541a1bf3f78SToomas Soome 				sprintf(buf, "%s", d->d_name);
1542a1bf3f78SToomas Soome 			else
1543a1bf3f78SToomas Soome 				sprintf(buf, "%s/%s", path, d->d_name);
1544a1bf3f78SToomas Soome 			/* ignore return, could be symlink, etc. */
1545a1bf3f78SToomas Soome 			if (stat(buf, &sb))
1546a1bf3f78SToomas Soome 				sb.st_size = 0;
1547a1bf3f78SToomas Soome 			free(buf);
1548a1bf3f78SToomas Soome 			if (verbose) {
1549a1bf3f78SToomas Soome 				sprintf(lbuf, " %c %8d %s\n",
1550a1bf3f78SToomas Soome 				    typestr[sb.st_mode >> 12],
1551a1bf3f78SToomas Soome 				    (int)sb.st_size, d->d_name);
1552a1bf3f78SToomas Soome 			} else {
1553a1bf3f78SToomas Soome 				sprintf(lbuf, " %c  %s\n",
1554a1bf3f78SToomas Soome 				    typestr[sb.st_mode >> 12], d->d_name);
1555a1bf3f78SToomas Soome 			}
1556a1bf3f78SToomas Soome 			if (pager_output(lbuf))
1557a1bf3f78SToomas Soome 				goto out;
1558a1bf3f78SToomas Soome 		}
1559a1bf3f78SToomas Soome 	}
1560a1bf3f78SToomas Soome out:
1561a1bf3f78SToomas Soome 	pager_close();
1562a1bf3f78SToomas Soome 	if (fd != -1)
1563a1bf3f78SToomas Soome 		closedir(dir);
1564a1bf3f78SToomas Soome 	if (path != NULL)
1565a1bf3f78SToomas Soome 		free(path);
1566a1bf3f78SToomas Soome 	return (result);
1567a1bf3f78SToomas Soome }
1568a1bf3f78SToomas Soome 
1569a1bf3f78SToomas Soome /*
1570a1bf3f78SToomas Soome  * Given (path) containing a vaguely reasonable path specification, return an fd
1571a1bf3f78SToomas Soome  * on the directory, and an allocated copy of the path to the directory.
1572a1bf3f78SToomas Soome  */
1573a1bf3f78SToomas Soome static int
1574a1bf3f78SToomas Soome ls_getdir(char **pathp)
1575a1bf3f78SToomas Soome {
1576a1bf3f78SToomas Soome 	struct stat sb;
1577a1bf3f78SToomas Soome 	int fd;
1578a1bf3f78SToomas Soome 	char *cp, *path;
1579a1bf3f78SToomas Soome 
1580a1bf3f78SToomas Soome 	fd = -1;
1581a1bf3f78SToomas Soome 
1582a1bf3f78SToomas Soome 	/* one extra byte for a possible trailing slash required */
1583a1bf3f78SToomas Soome 	path = malloc(strlen(*pathp) + 2);
1584a1bf3f78SToomas Soome 	strcpy(path, *pathp);
1585a1bf3f78SToomas Soome 
1586a1bf3f78SToomas Soome 	/* Make sure the path is respectable to begin with */
1587a1bf3f78SToomas Soome 	if ((cp = get_dev(path)) == NULL) {
1588a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1589a1bf3f78SToomas Soome 		    "bad path '%s'", path);
1590a1bf3f78SToomas Soome 		goto out;
1591a1bf3f78SToomas Soome 	}
1592a1bf3f78SToomas Soome 
1593a1bf3f78SToomas Soome 	/* If there's no path on the device, assume '/' */
1594a1bf3f78SToomas Soome 	if (*cp == 0)
1595a1bf3f78SToomas Soome 		strcat(path, "/");
1596a1bf3f78SToomas Soome 
1597a1bf3f78SToomas Soome 	fd = open(cp, O_RDONLY);
1598a1bf3f78SToomas Soome 	if (fd < 0) {
1599a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1600a1bf3f78SToomas Soome 		    "open '%s' failed: %s", path, strerror(errno));
1601a1bf3f78SToomas Soome 		goto out;
1602a1bf3f78SToomas Soome 	}
1603a1bf3f78SToomas Soome 	if (fstat(fd, &sb) < 0) {
1604a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1605a1bf3f78SToomas Soome 		    "stat failed: %s", strerror(errno));
1606a1bf3f78SToomas Soome 		goto out;
1607a1bf3f78SToomas Soome 	}
1608a1bf3f78SToomas Soome 	if (!S_ISDIR(sb.st_mode)) {
1609a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1610a1bf3f78SToomas Soome 		    "%s: %s", path, strerror(ENOTDIR));
1611a1bf3f78SToomas Soome 		goto out;
1612a1bf3f78SToomas Soome 	}
1613a1bf3f78SToomas Soome 
1614a1bf3f78SToomas Soome 	free(cp);
1615a1bf3f78SToomas Soome 	*pathp = path;
1616a1bf3f78SToomas Soome 	return (fd);
1617a1bf3f78SToomas Soome 
1618a1bf3f78SToomas Soome out:
1619a1bf3f78SToomas Soome 	free(cp);
1620a1bf3f78SToomas Soome 	free(path);
1621a1bf3f78SToomas Soome 	*pathp = NULL;
1622a1bf3f78SToomas Soome 	if (fd != -1)
1623a1bf3f78SToomas Soome 		close(fd);
1624a1bf3f78SToomas Soome 	return (-1);
1625a1bf3f78SToomas Soome }
1626a1bf3f78SToomas Soome 
1627a1bf3f78SToomas Soome static int
1628a1bf3f78SToomas Soome command_include(int argc, char *argv[])
1629a1bf3f78SToomas Soome {
1630a1bf3f78SToomas Soome 	int i;
1631a1bf3f78SToomas Soome 	int res;
1632a1bf3f78SToomas Soome 	char **argvbuf;
1633a1bf3f78SToomas Soome 
1634a1bf3f78SToomas Soome 	/*
1635a1bf3f78SToomas Soome 	 * Since argv is static, we need to save it here.
1636a1bf3f78SToomas Soome 	 */
1637a1bf3f78SToomas Soome 	argvbuf = (char **)calloc(argc, sizeof (char *));
1638a1bf3f78SToomas Soome 	for (i = 0; i < argc; i++)
1639a1bf3f78SToomas Soome 		argvbuf[i] = strdup(argv[i]);
1640a1bf3f78SToomas Soome 
1641a1bf3f78SToomas Soome 	res = CMD_OK;
1642a1bf3f78SToomas Soome 	for (i = 1; (i < argc) && (res == CMD_OK); i++)
1643a1bf3f78SToomas Soome 		res = include(argvbuf[i]);
1644a1bf3f78SToomas Soome 
1645a1bf3f78SToomas Soome 	for (i = 0; i < argc; i++)
1646a1bf3f78SToomas Soome 		free(argvbuf[i]);
1647a1bf3f78SToomas Soome 	free(argvbuf);
1648a1bf3f78SToomas Soome 
1649a1bf3f78SToomas Soome 	return (res);
1650a1bf3f78SToomas Soome }
1651a1bf3f78SToomas Soome 
1652a1bf3f78SToomas Soome /*
1653a1bf3f78SToomas Soome  * Header prepended to each line. The text immediately follows the header.
1654a1bf3f78SToomas Soome  * We try to make this short in order to save memory -- the loader has
1655a1bf3f78SToomas Soome  * limited memory available, and some of the forth files are very long.
1656a1bf3f78SToomas Soome  */
1657a1bf3f78SToomas Soome struct includeline
1658a1bf3f78SToomas Soome {
1659a1bf3f78SToomas Soome 	struct includeline *next;
1660a1bf3f78SToomas Soome 	int line;
1661a1bf3f78SToomas Soome 	char text[];
1662a1bf3f78SToomas Soome };
1663a1bf3f78SToomas Soome 
1664a1bf3f78SToomas Soome int
1665a1bf3f78SToomas Soome include(const char *filename)
1666a1bf3f78SToomas Soome {
1667a1bf3f78SToomas Soome 	struct includeline *script, *se, *sp;
1668a1bf3f78SToomas Soome 	int res = CMD_OK;
1669a1bf3f78SToomas Soome 	int prevsrcid, fd, line;
1670a1bf3f78SToomas Soome 	char *cp, input[256]; /* big enough? */
1671a1bf3f78SToomas Soome 	char *path;
1672a1bf3f78SToomas Soome 
1673a1bf3f78SToomas Soome 	path = get_dev(filename);
1674a1bf3f78SToomas Soome 	if (((fd = open(path, O_RDONLY)) == -1)) {
1675a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1676a1bf3f78SToomas Soome 		    "can't open '%s': %s", filename,
1677a1bf3f78SToomas Soome 		    strerror(errno));
1678a1bf3f78SToomas Soome 		free(path);
1679a1bf3f78SToomas Soome 		return (CMD_ERROR);
1680a1bf3f78SToomas Soome 	}
1681a1bf3f78SToomas Soome 
1682a1bf3f78SToomas Soome 	free(path);
1683a1bf3f78SToomas Soome 	/*
1684a1bf3f78SToomas Soome 	 * Read the script into memory.
1685a1bf3f78SToomas Soome 	 */
1686a1bf3f78SToomas Soome 	script = se = NULL;
1687a1bf3f78SToomas Soome 	line = 0;
1688a1bf3f78SToomas Soome 
1689a1bf3f78SToomas Soome 	while (fgetstr(input, sizeof (input), fd) >= 0) {
1690a1bf3f78SToomas Soome 		line++;
1691a1bf3f78SToomas Soome 		cp = input;
1692a1bf3f78SToomas Soome 		/* Allocate script line structure and copy line, flags */
1693a1bf3f78SToomas Soome 		if (*cp == '\0')
1694a1bf3f78SToomas Soome 			continue;	/* ignore empty line, save memory */
1695a1bf3f78SToomas Soome 		if (cp[0] == '\\' && cp[1] == ' ')
1696a1bf3f78SToomas Soome 			continue;	/* ignore comment */
1697a1bf3f78SToomas Soome 
1698a1bf3f78SToomas Soome 		sp = malloc(sizeof (struct includeline) + strlen(cp) + 1);
1699a1bf3f78SToomas Soome 		/*
1700a1bf3f78SToomas Soome 		 * On malloc failure (it happens!), free as much as possible
1701a1bf3f78SToomas Soome 		 * and exit
1702a1bf3f78SToomas Soome 		 */
1703a1bf3f78SToomas Soome 		if (sp == NULL) {
1704a1bf3f78SToomas Soome 			while (script != NULL) {
1705a1bf3f78SToomas Soome 				se = script;
1706a1bf3f78SToomas Soome 				script = script->next;
1707a1bf3f78SToomas Soome 				free(se);
1708a1bf3f78SToomas Soome 			}
1709a1bf3f78SToomas Soome 			snprintf(command_errbuf, sizeof (command_errbuf),
1710a1bf3f78SToomas Soome 			    "file '%s' line %d: memory allocation "
1711a1bf3f78SToomas Soome 			    "failure - aborting", filename, line);
1712a1bf3f78SToomas Soome 			return (CMD_ERROR);
1713a1bf3f78SToomas Soome 		}
1714a1bf3f78SToomas Soome 		strcpy(sp->text, cp);
1715a1bf3f78SToomas Soome 		sp->line = line;
1716a1bf3f78SToomas Soome 		sp->next = NULL;
1717a1bf3f78SToomas Soome 
1718a1bf3f78SToomas Soome 		if (script == NULL) {
1719a1bf3f78SToomas Soome 			script = sp;
1720a1bf3f78SToomas Soome 		} else {
1721a1bf3f78SToomas Soome 			se->next = sp;
1722a1bf3f78SToomas Soome 		}
1723a1bf3f78SToomas Soome 		se = sp;
1724a1bf3f78SToomas Soome 	}
1725a1bf3f78SToomas Soome 	close(fd);
1726a1bf3f78SToomas Soome 
1727a1bf3f78SToomas Soome 	/*
1728a1bf3f78SToomas Soome 	 * Execute the script
1729a1bf3f78SToomas Soome 	 */
1730a1bf3f78SToomas Soome 
1731a1bf3f78SToomas Soome 	prevsrcid = bf_vm->sourceId.i;
1732a1bf3f78SToomas Soome 	bf_vm->sourceId.i = fd+1;	/* 0 is user input device */
1733a1bf3f78SToomas Soome 
1734a1bf3f78SToomas Soome 	res = CMD_OK;
1735a1bf3f78SToomas Soome 
1736a1bf3f78SToomas Soome 	for (sp = script; sp != NULL; sp = sp->next) {
1737a1bf3f78SToomas Soome 		res = bf_run(sp->text);
1738a1bf3f78SToomas Soome 		if (res != FICL_VM_STATUS_OUT_OF_TEXT) {
1739a1bf3f78SToomas Soome 			snprintf(command_errbuf, sizeof (command_errbuf),
1740a1bf3f78SToomas Soome 			    "Error while including %s, in the line %d:\n%s",
1741a1bf3f78SToomas Soome 			    filename, sp->line, sp->text);
1742a1bf3f78SToomas Soome 			res = CMD_ERROR;
1743a1bf3f78SToomas Soome 			break;
1744a1bf3f78SToomas Soome 		} else
1745a1bf3f78SToomas Soome 			res = CMD_OK;
1746a1bf3f78SToomas Soome 	}
1747a1bf3f78SToomas Soome 
1748a1bf3f78SToomas Soome 	bf_vm->sourceId.i = -1;
1749a1bf3f78SToomas Soome 	(void) bf_run("");
1750a1bf3f78SToomas Soome 	bf_vm->sourceId.i = prevsrcid;
1751a1bf3f78SToomas Soome 
1752a1bf3f78SToomas Soome 	while (script != NULL) {
1753a1bf3f78SToomas Soome 		se = script;
1754a1bf3f78SToomas Soome 		script = script->next;
1755a1bf3f78SToomas Soome 		free(se);
1756a1bf3f78SToomas Soome 	}
1757a1bf3f78SToomas Soome 
1758a1bf3f78SToomas Soome 	return (res);
1759a1bf3f78SToomas Soome }
1760a1bf3f78SToomas Soome 
1761a1bf3f78SToomas Soome static int
1762a1bf3f78SToomas Soome command_boot(int argc, char *argv[])
1763a1bf3f78SToomas Soome {
1764a1bf3f78SToomas Soome 	return (CMD_OK);
1765a1bf3f78SToomas Soome }
1766a1bf3f78SToomas Soome 
1767a1bf3f78SToomas Soome static int
1768a1bf3f78SToomas Soome command_autoboot(int argc, char *argv[])
1769a1bf3f78SToomas Soome {
1770a1bf3f78SToomas Soome 	return (CMD_OK);
1771a1bf3f78SToomas Soome }
1772a1bf3f78SToomas Soome 
1773a1bf3f78SToomas Soome static void
1774a1bf3f78SToomas Soome moduledir_rebuild(void)
1775a1bf3f78SToomas Soome {
1776a1bf3f78SToomas Soome 	struct moduledir *mdp, *mtmp;
1777a1bf3f78SToomas Soome 	const char *path, *cp, *ep;
1778a1bf3f78SToomas Soome 	int cplen;
1779a1bf3f78SToomas Soome 
1780a1bf3f78SToomas Soome 	path = getenv("module_path");
1781a1bf3f78SToomas Soome 	if (path == NULL)
1782a1bf3f78SToomas Soome 		path = default_searchpath;
1783a1bf3f78SToomas Soome 	/*
1784a1bf3f78SToomas Soome 	 * Rebuild list of module directories if it changed
1785a1bf3f78SToomas Soome 	 */
1786a1bf3f78SToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link)
1787a1bf3f78SToomas Soome 		mdp->d_flags |= MDIR_REMOVED;
1788a1bf3f78SToomas Soome 
1789a1bf3f78SToomas Soome 	for (ep = path; *ep != 0;  ep++) {
1790a1bf3f78SToomas Soome 		cp = ep;
1791a1bf3f78SToomas Soome 		for (; *ep != 0 && *ep != ';'; ep++)
1792a1bf3f78SToomas Soome 			;
1793a1bf3f78SToomas Soome 		/*
1794a1bf3f78SToomas Soome 		 * Ignore trailing slashes
1795a1bf3f78SToomas Soome 		 */
1796a1bf3f78SToomas Soome 		for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/';
1797a1bf3f78SToomas Soome 		    cplen--)
1798a1bf3f78SToomas Soome 			;
1799a1bf3f78SToomas Soome 		STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
1800a1bf3f78SToomas Soome 			if (strlen(mdp->d_path) != cplen ||
1801a1bf3f78SToomas Soome 			    bcmp(cp, mdp->d_path, cplen) != 0)
1802a1bf3f78SToomas Soome 				continue;
1803a1bf3f78SToomas Soome 			mdp->d_flags &= ~MDIR_REMOVED;
1804a1bf3f78SToomas Soome 			break;
1805a1bf3f78SToomas Soome 		}
1806a1bf3f78SToomas Soome 		if (mdp == NULL) {
1807a1bf3f78SToomas Soome 			mdp = malloc(sizeof (*mdp) + cplen + 1);
1808a1bf3f78SToomas Soome 			if (mdp == NULL)
1809a1bf3f78SToomas Soome 				return;
1810a1bf3f78SToomas Soome 			mdp->d_path = (char *)(mdp + 1);
1811a1bf3f78SToomas Soome 			bcopy(cp, mdp->d_path, cplen);
1812a1bf3f78SToomas Soome 			mdp->d_path[cplen] = 0;
1813a1bf3f78SToomas Soome 			mdp->d_hints = NULL;
1814a1bf3f78SToomas Soome 			mdp->d_flags = 0;
1815a1bf3f78SToomas Soome 			STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
1816a1bf3f78SToomas Soome 		}
1817a1bf3f78SToomas Soome 		if (*ep == 0)
1818a1bf3f78SToomas Soome 			break;
1819a1bf3f78SToomas Soome 	}
1820a1bf3f78SToomas Soome 	/*
1821a1bf3f78SToomas Soome 	 * Delete unused directories if any
1822a1bf3f78SToomas Soome 	 */
1823a1bf3f78SToomas Soome 	mdp = STAILQ_FIRST(&moduledir_list);
1824a1bf3f78SToomas Soome 	while (mdp) {
1825a1bf3f78SToomas Soome 		if ((mdp->d_flags & MDIR_REMOVED) == 0) {
1826a1bf3f78SToomas Soome 			mdp = STAILQ_NEXT(mdp, d_link);
1827a1bf3f78SToomas Soome 		} else {
1828a1bf3f78SToomas Soome 			if (mdp->d_hints)
1829a1bf3f78SToomas Soome 				free(mdp->d_hints);
1830a1bf3f78SToomas Soome 			mtmp = mdp;
1831a1bf3f78SToomas Soome 			mdp = STAILQ_NEXT(mdp, d_link);
1832a1bf3f78SToomas Soome 			STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
1833a1bf3f78SToomas Soome 			free(mtmp);
1834a1bf3f78SToomas Soome 		}
1835a1bf3f78SToomas Soome 	}
1836a1bf3f78SToomas Soome }
1837a1bf3f78SToomas Soome 
1838a1bf3f78SToomas Soome static char *
1839a1bf3f78SToomas Soome file_lookup(const char *path, const char *name, int namelen)
1840a1bf3f78SToomas Soome {
1841a1bf3f78SToomas Soome 	struct stat st;
1842a1bf3f78SToomas Soome 	char *result, *cp, *gz;
1843a1bf3f78SToomas Soome 	int pathlen;
1844a1bf3f78SToomas Soome 
1845a1bf3f78SToomas Soome 	pathlen = strlen(path);
1846a1bf3f78SToomas Soome 	result = malloc(pathlen + namelen + 2);
1847a1bf3f78SToomas Soome 	if (result == NULL)
1848a1bf3f78SToomas Soome 		return (NULL);
1849a1bf3f78SToomas Soome 	bcopy(path, result, pathlen);
1850a1bf3f78SToomas Soome 	if (pathlen > 0 && result[pathlen - 1] != '/')
1851a1bf3f78SToomas Soome 		result[pathlen++] = '/';
1852a1bf3f78SToomas Soome 	cp = result + pathlen;
1853a1bf3f78SToomas Soome 	bcopy(name, cp, namelen);
1854a1bf3f78SToomas Soome 	cp += namelen;
1855a1bf3f78SToomas Soome 	*cp = '\0';
1856a1bf3f78SToomas Soome 	if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
1857a1bf3f78SToomas Soome 		return (result);
1858a1bf3f78SToomas Soome 	/* also check for gz file */
1859a1bf3f78SToomas Soome 	(void) asprintf(&gz, "%s.gz", result);
1860a1bf3f78SToomas Soome 	if (gz != NULL) {
1861a1bf3f78SToomas Soome 		int res = stat(gz, &st);
1862a1bf3f78SToomas Soome 		free(gz);
1863a1bf3f78SToomas Soome 		if (res == 0)
1864a1bf3f78SToomas Soome 			return (result);
1865a1bf3f78SToomas Soome 	}
1866a1bf3f78SToomas Soome 	free(result);
1867a1bf3f78SToomas Soome 	return (NULL);
1868a1bf3f78SToomas Soome }
1869a1bf3f78SToomas Soome 
1870a1bf3f78SToomas Soome static char *
1871a1bf3f78SToomas Soome file_search(const char *name)
1872a1bf3f78SToomas Soome {
1873a1bf3f78SToomas Soome 	struct moduledir *mdp;
1874a1bf3f78SToomas Soome 	struct stat sb;
1875a1bf3f78SToomas Soome 	char *result;
1876a1bf3f78SToomas Soome 	int namelen;
1877a1bf3f78SToomas Soome 
1878a1bf3f78SToomas Soome 	if (name == NULL)
1879a1bf3f78SToomas Soome 		return (NULL);
1880a1bf3f78SToomas Soome 	if (*name == 0)
1881a1bf3f78SToomas Soome 		return (strdup(name));
1882a1bf3f78SToomas Soome 
1883a1bf3f78SToomas Soome 	if (strchr(name, '/') != NULL) {
1884a1bf3f78SToomas Soome 		char *gz;
1885a1bf3f78SToomas Soome 		if (stat(name, &sb) == 0)
1886a1bf3f78SToomas Soome 			return (strdup(name));
1887a1bf3f78SToomas Soome 		/* also check for gz file */
1888a1bf3f78SToomas Soome 		(void) asprintf(&gz, "%s.gz", name);
1889a1bf3f78SToomas Soome 		if (gz != NULL) {
1890a1bf3f78SToomas Soome 			int res = stat(gz, &sb);
1891a1bf3f78SToomas Soome 			free(gz);
1892a1bf3f78SToomas Soome 			if (res == 0)
1893a1bf3f78SToomas Soome 				return (strdup(name));
1894a1bf3f78SToomas Soome 		}
1895a1bf3f78SToomas Soome 		return (NULL);
1896a1bf3f78SToomas Soome 	}
1897a1bf3f78SToomas Soome 
1898a1bf3f78SToomas Soome 	moduledir_rebuild();
1899a1bf3f78SToomas Soome 	result = NULL;
1900a1bf3f78SToomas Soome 	namelen = strlen(name);
1901a1bf3f78SToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
1902a1bf3f78SToomas Soome 		result = file_lookup(mdp->d_path, name, namelen);
1903a1bf3f78SToomas Soome 		if (result)
1904a1bf3f78SToomas Soome 			break;
1905a1bf3f78SToomas Soome 	}
1906a1bf3f78SToomas Soome 	return (result);
1907a1bf3f78SToomas Soome }
1908a1bf3f78SToomas Soome 
1909a1bf3f78SToomas Soome static int
1910a1bf3f78SToomas Soome command_load(int argc, char *argv[])
1911a1bf3f78SToomas Soome {
1912a1bf3f78SToomas Soome 	int dofile, ch;
1913a1bf3f78SToomas Soome 	char *typestr = NULL;
1914a1bf3f78SToomas Soome 	char *filename;
1915a1bf3f78SToomas Soome 	dofile = 0;
1916a1bf3f78SToomas Soome 	optind = 1;
1917a1bf3f78SToomas Soome 
1918a1bf3f78SToomas Soome 	if (argc == 1) {
1919a1bf3f78SToomas Soome 		command_errmsg = "no filename specified";
1920a1bf3f78SToomas Soome 		return (CMD_ERROR);
1921a1bf3f78SToomas Soome 	}
1922a1bf3f78SToomas Soome 
1923a1bf3f78SToomas Soome 	while ((ch = getopt(argc, argv, "kt:")) != -1) {
1924a1bf3f78SToomas Soome 		switch (ch) {
1925a1bf3f78SToomas Soome 		case 'k':
1926a1bf3f78SToomas Soome 			break;
1927a1bf3f78SToomas Soome 		case 't':
1928a1bf3f78SToomas Soome 			typestr = optarg;
1929a1bf3f78SToomas Soome 			dofile = 1;
1930a1bf3f78SToomas Soome 			break;
1931a1bf3f78SToomas Soome 		case '?':
1932a1bf3f78SToomas Soome 		default:
1933a1bf3f78SToomas Soome 			return (CMD_OK);
1934a1bf3f78SToomas Soome 		}
1935a1bf3f78SToomas Soome 	}
1936a1bf3f78SToomas Soome 	argv += (optind - 1);
1937a1bf3f78SToomas Soome 	argc -= (optind - 1);
1938a1bf3f78SToomas Soome 	if (dofile) {
1939a1bf3f78SToomas Soome 		if ((typestr == NULL) || (*typestr == 0)) {
1940a1bf3f78SToomas Soome 			command_errmsg = "invalid load type";
1941a1bf3f78SToomas Soome 			return (CMD_ERROR);
1942a1bf3f78SToomas Soome 		}
1943a1bf3f78SToomas Soome #if 0
1944a1bf3f78SToomas Soome 		return (file_loadraw(argv[1], typestr, argc - 2, argv + 2, 1)
1945a1bf3f78SToomas Soome 		    ? CMD_OK : CMD_ERROR);
1946a1bf3f78SToomas Soome #endif
1947a1bf3f78SToomas Soome 		return (CMD_OK);
1948a1bf3f78SToomas Soome 	}
1949a1bf3f78SToomas Soome 
1950a1bf3f78SToomas Soome 	filename = file_search(argv[1]);
1951a1bf3f78SToomas Soome 	if (filename == NULL) {
1952a1bf3f78SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1953a1bf3f78SToomas Soome 		    "can't find '%s'", argv[1]);
1954a1bf3f78SToomas Soome 		return (CMD_ERROR);
1955a1bf3f78SToomas Soome 	}
1956a1bf3f78SToomas Soome 	setenv("kernelname", filename, 1);
1957a1bf3f78SToomas Soome 
1958a1bf3f78SToomas Soome 	return (CMD_OK);
1959a1bf3f78SToomas Soome }
1960a1bf3f78SToomas Soome 
1961a1bf3f78SToomas Soome static int
1962a1bf3f78SToomas Soome command_unload(int argc, char *argv[])
1963a1bf3f78SToomas Soome {
1964a1bf3f78SToomas Soome 	unsetenv("kernelname");
1965a1bf3f78SToomas Soome 	return (CMD_OK);
1966a1bf3f78SToomas Soome }
1967a1bf3f78SToomas Soome 
1968a1bf3f78SToomas Soome static int
1969a1bf3f78SToomas Soome command_reboot(int argc, char *argv[])
1970a1bf3f78SToomas Soome {
1971a1bf3f78SToomas Soome 	exit(0);
1972a1bf3f78SToomas Soome 	return (CMD_OK);
1973a1bf3f78SToomas Soome }
1974