138465Smsmith/*-
238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
338465Smsmith * All rights reserved.
438465Smsmith *
538465Smsmith * Redistribution and use in source and binary forms, with or without
638465Smsmith * modification, are permitted provided that the following conditions
738465Smsmith * are met:
838465Smsmith * 1. Redistributions of source code must retain the above copyright
938465Smsmith *    notice, this list of conditions and the following disclaimer.
1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138465Smsmith *    notice, this list of conditions and the following disclaimer in the
1238465Smsmith *    documentation and/or other materials provided with the distribution.
1338465Smsmith *
1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738465Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438465Smsmith * SUCH DAMAGE.
2538465Smsmith */
2638465Smsmith
27119483Sobrien#include <sys/cdefs.h>
28119483Sobrien__FBSDID("$FreeBSD: stable/11/stand/common/commands.c 344286 2019-02-19 18:34:00Z kevans $");
29119483Sobrien
3038465Smsmith#include <stand.h>
3138465Smsmith#include <string.h>
3238465Smsmith
3338465Smsmith#include "bootstrap.h"
3438465Smsmith
35344286Skevansconst char	*command_errmsg;
36329010Skevans/* XXX should have procedural interface for setting, size limit? */
37329010Skevanschar		command_errbuf[COMMAND_ERRBUFSZ];
3840775Smsmith
3943491Sjkhstatic int page_file(char *filename);
4040775Smsmith
4140775Smsmith/*
4240775Smsmith * Help is read from a formatted text file.
4340775Smsmith *
4440775Smsmith * Entries in the file are formatted as
4540775Smsmith
4640775Smsmith# Ttopic [Ssubtopic] Ddescription
4740775Smsmithhelp
4840775Smsmithtext
4940775Smsmithhere
5040775Smsmith#
5140775Smsmith
5240775Smsmith *
5340775Smsmith * Note that for code simplicity's sake, the above format must be followed
5440775Smsmith * exactly.
5540775Smsmith *
5640775Smsmith * Subtopic entries must immediately follow the topic (this is used to
5740775Smsmith * produce the listing of subtopics).
5840775Smsmith *
5940775Smsmith * If no argument(s) are supplied by the user, the help for 'help' is displayed.
6040775Smsmith */
6138465SmsmithCOMMAND_SET(help, "help", "detailed help", command_help);
6238465Smsmith
6338465Smsmithstatic int
64344286Skevanshelp_getnext(int fd, char **topic, char **subtopic, char **desc)
6538465Smsmith{
66344286Skevans	char	line[81], *cp, *ep;
67344285Skevans
68344286Skevans	/* Make sure we provide sane values. */
69344286Skevans	*topic = *subtopic = *desc = NULL;
70344286Skevans	for (;;) {
71344286Skevans		if (fgetstr(line, 80, fd) < 0)
72344286Skevans			return (0);
7338465Smsmith
74344286Skevans		if (strlen(line) < 3 || line[0] != '#' || line[1] != ' ')
75344286Skevans			continue;
76344286Skevans
77344286Skevans		cp = line + 2;
78344286Skevans		while (cp != NULL && *cp != 0) {
79344286Skevans			ep = strchr(cp, ' ');
80344286Skevans			if (*cp == 'T' && *topic == NULL) {
81344286Skevans				if (ep != NULL)
82344286Skevans					*ep++ = 0;
83344286Skevans				*topic = strdup(cp + 1);
84344286Skevans			} else if (*cp == 'S' && *subtopic == NULL) {
85344286Skevans				if (ep != NULL)
86344286Skevans					*ep++ = 0;
87344286Skevans				*subtopic = strdup(cp + 1);
88344286Skevans			} else if (*cp == 'D') {
89344286Skevans				*desc = strdup(cp + 1);
90344286Skevans				ep = NULL;
91344286Skevans			}
92344286Skevans			cp = ep;
93344286Skevans		}
94344286Skevans		if (*topic == NULL) {
95344286Skevans			free(*subtopic);
96344286Skevans			free(*desc);
97344286Skevans			*subtopic = *desc = NULL;
98344286Skevans			continue;
99344286Skevans		}
100344286Skevans		return (1);
10140775Smsmith	}
10240775Smsmith}
10340775Smsmith
104135929Srustatic int
10540775Smsmithhelp_emitsummary(char *topic, char *subtopic, char *desc)
10640775Smsmith{
107344286Skevans	int	i;
108344286Skevans
109344286Skevans	pager_output("    ");
110344286Skevans	pager_output(topic);
111344286Skevans	i = strlen(topic);
112344286Skevans	if (subtopic != NULL) {
113344286Skevans		pager_output(" ");
114344286Skevans		pager_output(subtopic);
115344286Skevans		i += strlen(subtopic) + 1;
116344286Skevans	}
117344286Skevans	if (desc != NULL) {
118344286Skevans		do {
119344286Skevans			pager_output(" ");
120344286Skevans		} while (i++ < 30);
121344286Skevans		pager_output(desc);
122344286Skevans	}
123344286Skevans	return (pager_output("\n"));
12440775Smsmith}
12540775Smsmith
126344286Skevans
12740775Smsmithstatic int
128344286Skevanscommand_help(int argc, char *argv[])
12940775Smsmith{
130344286Skevans	char	buf[81];	/* XXX buffer size? */
131344286Skevans	int	hfd, matched, doindex;
132344286Skevans	char	*topic, *subtopic, *t, *s, *d;
13340775Smsmith
134344286Skevans	/* page the help text from our load path */
135344286Skevans	snprintf(buf, sizeof(buf), "%s/boot/loader.help", getenv("loaddev"));
136344286Skevans	if ((hfd = open(buf, O_RDONLY)) < 0) {
137344286Skevans		printf("Verbose help not available, "
138344286Skevans		    "use '?' to list commands\n");
139344286Skevans		return (CMD_OK);
140344286Skevans	}
14140775Smsmith
142344286Skevans	/* pick up request from arguments */
143344286Skevans	topic = subtopic = NULL;
144344286Skevans	switch (argc) {
145344286Skevans	case 3:
146344286Skevans		subtopic = strdup(argv[2]);
147344286Skevans		/* FALLTHROUGH */
148344286Skevans	case 2:
149344286Skevans		topic = strdup(argv[1]);
150344286Skevans		break;
151344286Skevans	case 1:
152344286Skevans		topic = strdup("help");
153344286Skevans		break;
154344286Skevans	default:
155344286Skevans		command_errmsg = "usage is 'help <topic> [<subtopic>]";
156344286Skevans		close(hfd);
157344286Skevans		return(CMD_ERROR);
158344286Skevans	}
15940775Smsmith
160344286Skevans	/* magic "index" keyword */
161344286Skevans	doindex = strcmp(topic, "index") == 0? 1 : 0;
162344286Skevans	matched = doindex;
16340775Smsmith
164344286Skevans	/* Scan the helpfile looking for help matching the request */
165344286Skevans	pager_open();
166344286Skevans	while (help_getnext(hfd, &t, &s, &d)) {
16740775Smsmith
168344286Skevans		if (doindex) {		/* dink around formatting */
169344286Skevans			if (help_emitsummary(t, s, d))
170344286Skevans				break;
17140775Smsmith
172344286Skevans		} else if (strcmp(topic, t)) {
173344286Skevans			/* topic mismatch */
174344286Skevans			if (matched) {
175344286Skevans				/* nothing more on this topic, stop scanning */
176344286Skevans				break;
177344286Skevans			}
178344286Skevans		} else {
179344286Skevans			/* topic matched */
180344286Skevans			matched = 1;
181344286Skevans			if ((subtopic == NULL && s == NULL) ||
182344286Skevans			    (subtopic != NULL && s != NULL &&
183344286Skevans			    strcmp(subtopic, s) == 0)) {
184344286Skevans				/* exact match, print text */
185344286Skevans				while (fgetstr(buf, 80, hfd) >= 0 &&
186344286Skevans				    buf[0] != '#') {
187344286Skevans					if (pager_output(buf))
188344286Skevans						break;
189344286Skevans					if (pager_output("\n"))
190344286Skevans						break;
191344286Skevans				}
192344286Skevans			} else if (subtopic == NULL && s != NULL) {
193344286Skevans				/* topic match, list subtopics */
194344286Skevans				if (help_emitsummary(t, s, d))
195344286Skevans					break;
196344286Skevans			}
19740775Smsmith		}
198344286Skevans		free(t);
199344286Skevans		free(s);
200344286Skevans		free(d);
201344286Skevans		t = s = d = NULL;
20240775Smsmith	}
20340775Smsmith	free(t);
20440775Smsmith	free(s);
20540775Smsmith	free(d);
206344286Skevans	pager_close();
207344286Skevans	close(hfd);
208344286Skevans	if (!matched) {
209344286Skevans		snprintf(command_errbuf, sizeof(command_errbuf),
210344286Skevans		    "no help available for '%s'", topic);
211344286Skevans		free(topic);
212344286Skevans		free(subtopic);
213344286Skevans		return (CMD_ERROR);
214344286Skevans	}
21544570Sdcs	free(topic);
216329183Skevans	free(subtopic);
217344286Skevans	return (CMD_OK);
21838465Smsmith}
21938465Smsmith
22038465SmsmithCOMMAND_SET(commandlist, "?", "list commands", command_commandlist);
22138465Smsmith
222300146Simp/*
223300146Simp * Please note: although we use the pager for the list of commands,
224300146Simp * this routine is called from the ? FORTH function which then
225300146Simp * unconditionally prints some commands. This will lead to anomalous
226300146Simp * behavior. There's no 'pager_output' binding to FORTH to allow
227300146Simp * things to work right, so I'm documenting the bug rather than
228300156Simp * fixing it.
229300146Simp */
23038465Smsmithstatic int
231344286Skevanscommand_commandlist(int argc __unused, char *argv[] __unused)
23238465Smsmith{
233344286Skevans	struct bootblk_command	**cmdp;
234344286Skevans	int	res;
235344286Skevans	char	name[20];
236137615Sru
237344286Skevans	res = 0;
238344286Skevans	pager_open();
239344286Skevans	res = pager_output("Available commands:\n");
240344286Skevans	SET_FOREACH(cmdp, Xcommand_set) {
241344286Skevans		if (res)
242344286Skevans			break;
243344286Skevans		if ((*cmdp)->c_name != NULL && (*cmdp)->c_desc != NULL) {
244344286Skevans			snprintf(name, sizeof(name), "  %-15s  ",
245344286Skevans			    (*cmdp)->c_name);
246344286Skevans			pager_output(name);
247344286Skevans			pager_output((*cmdp)->c_desc);
248344286Skevans			res = pager_output("\n");
249344286Skevans		}
250137615Sru	}
251344286Skevans	pager_close();
252344286Skevans	return (CMD_OK);
25338465Smsmith}
25438465Smsmith
25538465Smsmith/*
25638465Smsmith * XXX set/show should become set/echo if we have variable
25738465Smsmith * substitution happening.
25838465Smsmith */
25938465Smsmith
26038465SmsmithCOMMAND_SET(show, "show", "show variable(s)", command_show);
26138465Smsmith
26238465Smsmithstatic int
26338465Smsmithcommand_show(int argc, char *argv[])
26438465Smsmith{
265344286Skevans	struct env_var	*ev;
266344286Skevans	char		*cp;
26738465Smsmith
268344286Skevans	if (argc < 2) {
269344286Skevans		/*
270344286Skevans		 * With no arguments, print everything.
271344286Skevans		 */
272344286Skevans		pager_open();
273344286Skevans		for (ev = environ; ev != NULL; ev = ev->ev_next) {
274344286Skevans			pager_output(ev->ev_name);
275344286Skevans			cp = getenv(ev->ev_name);
276344286Skevans			if (cp != NULL) {
277344286Skevans				pager_output("=");
278344286Skevans				pager_output(cp);
279344286Skevans			}
280344286Skevans			if (pager_output("\n"))
281344286Skevans				break;
282344286Skevans		}
283344286Skevans		pager_close();
28438465Smsmith	} else {
285344286Skevans		if ((cp = getenv(argv[1])) != NULL) {
286344286Skevans			printf("%s\n", cp);
287344286Skevans		} else {
288344286Skevans			snprintf(command_errbuf, sizeof(command_errbuf),
289344286Skevans			    "variable '%s' not found", argv[1]);
290344286Skevans			return (CMD_ERROR);
291344286Skevans		}
29238465Smsmith	}
293344286Skevans	return (CMD_OK);
29438465Smsmith}
29538465Smsmith
29638465SmsmithCOMMAND_SET(set, "set", "set a variable", command_set);
29738465Smsmith
29838465Smsmithstatic int
29938465Smsmithcommand_set(int argc, char *argv[])
30038465Smsmith{
301344286Skevans	int	err;
302344286Skevans
303344286Skevans	if (argc != 2) {
304344286Skevans		command_errmsg = "wrong number of arguments";
305344286Skevans		return (CMD_ERROR);
306344286Skevans	} else {
307344286Skevans		if ((err = putenv(argv[1])) != 0) {
308344286Skevans			command_errmsg = strerror(err);
309344286Skevans			return (CMD_ERROR);
310344286Skevans		}
31138465Smsmith	}
312344286Skevans	return (CMD_OK);
31338465Smsmith}
31438465Smsmith
31538465SmsmithCOMMAND_SET(unset, "unset", "unset a variable", command_unset);
31638465Smsmith
31738465Smsmithstatic int
318344286Skevanscommand_unset(int argc, char *argv[])
31938465Smsmith{
320344286Skevans	int	err;
321344286Skevans
322344286Skevans	if (argc != 2) {
323344286Skevans		command_errmsg = "wrong number of arguments";
324344286Skevans		return (CMD_ERROR);
325344286Skevans	} else {
326344286Skevans		if ((err = unsetenv(argv[1])) != 0) {
327344286Skevans			command_errmsg = strerror(err);
328344286Skevans			return (CMD_ERROR);
329344286Skevans		}
33038465Smsmith	}
331344286Skevans	return (CMD_OK);
33238465Smsmith}
33338465Smsmith
334137667SruCOMMAND_SET(echo, "echo", "echo arguments", command_echo);
33538764Smsmith
33638764Smsmithstatic int
33738764Smsmithcommand_echo(int argc, char *argv[])
33838764Smsmith{
339344286Skevans	char	*s;
340344286Skevans	int	nl, ch;
341344286Skevans
342344286Skevans	nl = 0;
343344286Skevans	optind = 1;
344344286Skevans	optreset = 1;
345344286Skevans	while ((ch = getopt(argc, argv, "n")) != -1) {
346344286Skevans		switch (ch) {
347344286Skevans		case 'n':
348344286Skevans			nl = 1;
349344286Skevans			break;
350344286Skevans		case '?':
351344286Skevans		default:
352344286Skevans			/* getopt has already reported an error */
353344286Skevans			return (CMD_OK);
354344286Skevans		}
35538764Smsmith	}
356344286Skevans	argv += (optind);
357344286Skevans	argc -= (optind);
35838764Smsmith
359344286Skevans	s = unargv(argc, argv);
360344286Skevans	if (s != NULL) {
361344286Skevans		printf("%s", s);
362344286Skevans		free(s);
363344286Skevans	}
364344286Skevans	if (!nl)
365344286Skevans		printf("\n");
366344286Skevans	return (CMD_OK);
36738764Smsmith}
36838764Smsmith
36940015Smsmith/*
37040015Smsmith * A passable emulation of the sh(1) command of the same name.
37140015Smsmith */
37240015Smsmith
373137667SruCOMMAND_SET(read, "read", "read input from the terminal", command_read);
37440015Smsmith
37540015Smsmithstatic int
37640015Smsmithcommand_read(int argc, char *argv[])
37740015Smsmith{
378344286Skevans	char	*prompt;
379344286Skevans	int	timeout;
380344286Skevans	time_t	when;
381344286Skevans	char	*cp;
382344286Skevans	char	*name;
383344286Skevans	char	buf[256];		/* XXX size? */
384344286Skevans	int	c;
38540015Smsmith
386344286Skevans	timeout = -1;
387344286Skevans	prompt = NULL;
388344286Skevans	optind = 1;
389344286Skevans	optreset = 1;
390344286Skevans	while ((c = getopt(argc, argv, "p:t:")) != -1) {
391344286Skevans		switch (c) {
392344286Skevans		case 'p':
393344286Skevans			prompt = optarg;
394344286Skevans			break;
395344286Skevans		case 't':
396344286Skevans			timeout = strtol(optarg, &cp, 0);
397344286Skevans			if (cp == optarg) {
398344286Skevans				snprintf(command_errbuf,
399344286Skevans				    sizeof(command_errbuf),
400344286Skevans				    "bad timeout '%s'", optarg);
401344286Skevans				return (CMD_ERROR);
402344286Skevans			}
403344286Skevans			break;
404344286Skevans		default:
405344286Skevans			return (CMD_OK);
406344286Skevans		}
40740015Smsmith	}
40840015Smsmith
409344286Skevans	argv += (optind);
410344286Skevans	argc -= (optind);
411344286Skevans	name = (argc > 0) ? argv[0]: NULL;
41240015Smsmith
413344286Skevans	if (prompt != NULL)
414344286Skevans		printf("%s", prompt);
415344286Skevans	if (timeout >= 0) {
416344286Skevans		when = time(NULL) + timeout;
417344286Skevans		while (!ischar())
418344286Skevans			if (time(NULL) >= when)
419344286Skevans				return (CMD_OK); /* is timeout an error? */
420344286Skevans	}
42140015Smsmith
422344286Skevans	ngets(buf, sizeof(buf));
423344286Skevans
424344286Skevans	if (name != NULL)
425344286Skevans		setenv(name, buf, 1);
426344286Skevans	return (CMD_OK);
42740015Smsmith}
42840775Smsmith
42940775Smsmith/*
43043491Sjkh * File pager
43143491Sjkh */
43243491SjkhCOMMAND_SET(more, "more", "show contents of a file", command_more);
43343491Sjkh
43443491Sjkhstatic int
43543491Sjkhcommand_more(int argc, char *argv[])
43643491Sjkh{
437344286Skevans	int	i;
438344286Skevans	int	res;
439344286Skevans	char	line[80];
44043491Sjkh
441344286Skevans	res = 0;
442344286Skevans	pager_open();
443344286Skevans	for (i = 1; (i < argc) && (res == 0); i++) {
444344286Skevans		snprintf(line, sizeof(line), "*** FILE %s BEGIN ***\n",
445344286Skevans		    argv[i]);
446344286Skevans		if (pager_output(line))
447344286Skevans			break;
448344286Skevans		res = page_file(argv[i]);
449344286Skevans		if (!res) {
450344286Skevans			snprintf(line, sizeof(line), "*** FILE %s END ***\n",
451344286Skevans			    argv[i]);
452344286Skevans			res = pager_output(line);
453344286Skevans		}
45443491Sjkh	}
455344286Skevans	pager_close();
45643491Sjkh
457344286Skevans	if (res == 0)
458344286Skevans		return (CMD_OK);
459344286Skevans	else
460344286Skevans		return (CMD_ERROR);
46143491Sjkh}
46243491Sjkh
46343491Sjkhstatic int
46443491Sjkhpage_file(char *filename)
46543491Sjkh{
466344286Skevans	int result;
46743491Sjkh
468344286Skevans	result = pager_file(filename);
46943491Sjkh
470344286Skevans	if (result == -1) {
471344286Skevans		snprintf(command_errbuf, sizeof(command_errbuf),
472344286Skevans		    "error showing %s", filename);
473344286Skevans	}
47443491Sjkh
475344286Skevans	return (result);
476344286Skevans}
47743491Sjkh
47843491Sjkh/*
47940775Smsmith * List all disk-like devices
48040775Smsmith */
48142418SmsmithCOMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev);
48240775Smsmith
48340775Smsmithstatic int
48440775Smsmithcommand_lsdev(int argc, char *argv[])
48540775Smsmith{
486344286Skevans	int	verbose, ch, i;
487344286Skevans	char	line[80];
488344286Skevans
489344286Skevans	verbose = 0;
490344286Skevans	optind = 1;
491344286Skevans	optreset = 1;
492344286Skevans	while ((ch = getopt(argc, argv, "v")) != -1) {
493344286Skevans		switch (ch) {
494344286Skevans		case 'v':
495344286Skevans			verbose = 1;
496344286Skevans			break;
497344286Skevans		case '?':
498344286Skevans		default:
499344286Skevans			/* getopt has already reported an error */
500344286Skevans			return (CMD_OK);
501344286Skevans		}
50240775Smsmith	}
503344286Skevans	argv += (optind);
504344286Skevans	argc -= (optind);
50540775Smsmith
506344286Skevans	pager_open();
507344286Skevans	for (i = 0; devsw[i] != NULL; i++) {
508344286Skevans		if (devsw[i]->dv_print != NULL) {
509344286Skevans			if (devsw[i]->dv_print(verbose))
510344286Skevans				break;
511344286Skevans		} else {
512344286Skevans			snprintf(line, sizeof(line), "%s: (unknown)\n",
513344286Skevans			    devsw[i]->dv_name);
514344286Skevans			if (pager_output(line))
515344286Skevans				break;
516344286Skevans		}
51740775Smsmith	}
518344286Skevans	pager_close();
519344286Skevans	return (CMD_OK);
52040775Smsmith}
521