1/*	$NetBSD: commands.c,v 1.2 2006/04/22 07:58:53 cherry Exp $	*/
2
3/*-
4 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30/* __FBSDID("$FreeBSD: src/sys/boot/common/commands.c,v 1.19 2003/08/25 23:30:41 obrien Exp $"); */
31
32#include <lib/libsa/stand.h>
33#include <lib/libsa/loadfile.h>
34#include <lib/libkern/libkern.h>
35
36#include "bootstrap.h"
37
38char		*command_errmsg;
39char		command_errbuf[256];	/* XXX should have procedural interface for setting, size limit? */
40
41static int page_file(char *filename);
42
43/*
44 * Help is read from a formatted text file.
45 *
46 * Entries in the file are formatted as
47
48# Ttopic [Ssubtopic] Ddescription
49help
50text
51here
52#
53
54 *
55 * Note that for code simplicity's sake, the above format must be followed
56 * exactly.
57 *
58 * Subtopic entries must immediately follow the topic (this is used to
59 * produce the listing of subtopics).
60 *
61 * If no argument(s) are supplied by the user, the help for 'help' is displayed.
62 */
63
64static int
65help_getnext(int fd, char **topic, char **subtopic, char **desc)
66{
67    char	line[81], *cp, *ep;
68
69    for (;;) {
70	if (fgetstr(line, 80, fd) < 0)
71	    return(0);
72
73	if ((strlen(line) < 3) || (line[0] != '#') || (line[1] != ' '))
74	    continue;
75
76	*topic = *subtopic = *desc = NULL;
77	cp = line + 2;
78	while((cp != NULL) && (*cp != 0)) {
79	    ep = strchr(cp, ' ');
80	    if ((*cp == 'T') && (*topic == NULL)) {
81		if (ep != NULL)
82		    *ep++ = 0;
83		*topic = strdup(cp + 1);
84	    } else if ((*cp == 'S') && (*subtopic == NULL)) {
85		if (ep != NULL)
86		    *ep++ = 0;
87		*subtopic = strdup(cp + 1);
88	    } else if (*cp == 'D') {
89		*desc = strdup(cp + 1);
90		ep = NULL;
91	    }
92	    cp = ep;
93	}
94	if (*topic == NULL) {
95	    if (*subtopic != NULL)
96		free(*subtopic);
97	    if (*desc != NULL)
98		free(*desc);
99	    continue;
100	}
101	return(1);
102    }
103}
104
105static int
106help_emitsummary(char *topic, char *subtopic, char *desc)
107{
108    int		i;
109
110    pager_output("    ");
111    pager_output(topic);
112    i = strlen(topic);
113    if (subtopic != NULL) {
114	pager_output(" ");
115	pager_output(subtopic);
116	i += strlen(subtopic) + 1;
117    }
118    if (desc != NULL) {
119	do {
120	    pager_output(" ");
121	} while (i++ < 30);
122	pager_output(desc);
123    }
124    return (pager_output("\n"));
125}
126
127
128int
129command_help(int argc, char *argv[])
130{
131    char	buf[81];	/* XXX buffer size? */
132    int		hfd, matched, doindex;
133    char	*topic, *subtopic, *t, *s, *d;
134
135    /* page the help text from our load path */
136    sprintf(buf, "%s/boot/loader.help", getenv("loaddev"));
137    if ((hfd = open(buf, O_RDONLY)) < 0) {
138	printf("Verbose help not available, use '?' to list commands\n");
139	return(CMD_OK);
140    }
141
142    /* pick up request from arguments */
143    topic = subtopic = NULL;
144    switch(argc) {
145    case 3:
146	subtopic = strdup(argv[2]);
147    case 2:
148	topic = strdup(argv[1]);
149	break;
150    case 1:
151	topic = strdup("help");
152	break;
153    default:
154	command_errmsg = "usage is 'help <topic> [<subtopic>]";
155	return(CMD_ERROR);
156    }
157
158    /* magic "index" keyword */
159    doindex = !strcmp(topic, "index");
160    matched = doindex;
161
162    /* Scan the helpfile looking for help matching the request */
163    pager_open();
164    while(help_getnext(hfd, &t, &s, &d)) {
165
166	if (doindex) {		/* dink around formatting */
167	    if (help_emitsummary(t, s, d))
168		break;
169
170	} else if (strcmp(topic, t)) {
171	    /* topic mismatch */
172	    if(matched)		/* nothing more on this topic, stop scanning */
173		break;
174
175	} else {
176	    /* topic matched */
177	    matched = 1;
178	    if (((subtopic == NULL) && (s == NULL)) ||
179		((subtopic != NULL) && (s != NULL) && !strcmp(subtopic, s))) {
180		/* exact match, print text */
181		while((fgetstr(buf, 80, hfd) >= 0) && (buf[0] != '#')) {
182		    if (pager_output(buf))
183			break;
184		    if (pager_output("\n"))
185			break;
186		}
187	    } else if ((subtopic == NULL) && (s != NULL)) {
188		/* topic match, list subtopics */
189		if (help_emitsummary(t, s, d))
190		    break;
191	    }
192	}
193	free(t);
194	free(s);
195	free(d);
196    }
197    pager_close();
198    close(hfd);
199    if (!matched) {
200	sprintf(command_errbuf, "no help available for '%s'", topic);
201	free(topic);
202	if (subtopic)
203	    free(subtopic);
204	return(CMD_ERROR);
205    }
206    free(topic);
207    if (subtopic)
208	free(subtopic);
209    return(CMD_OK);
210}
211
212
213int
214command_commandlist(int argc, char *argv[])
215{
216    struct bootblk_command	*cmdp;
217    int		res;
218    char	name[20];
219    int i;
220
221    res = 0;
222    pager_open();
223    res = pager_output("Available commands:\n");
224
225    for (i = 0, cmdp = commands; (cmdp->c_name != NULL) && (cmdp->c_desc != NULL ); i++, cmdp = commands + i) {
226	    if (res)
227	    break;
228	if ((cmdp->c_name != NULL) && (cmdp->c_desc != NULL)) {
229	    sprintf(name, "  %s  ", cmdp->c_name);
230	    pager_output(name);
231	    pager_output(cmdp->c_desc);
232	    res = pager_output("\n");
233	}
234    }
235    pager_close();
236    return(CMD_OK);
237}
238
239/*
240 * XXX set/show should become set/echo if we have variable
241 * substitution happening.
242 */
243
244int
245command_show(int argc, char *argv[])
246{
247    struct env_var	*ev;
248    char		*cp;
249
250    if (argc < 2) {
251	/*
252	 * With no arguments, print everything.
253	 */
254	pager_open();
255	for (ev = environ; ev != NULL; ev = ev->ev_next) {
256	    pager_output(ev->ev_name);
257	    cp = getenv(ev->ev_name);
258	    if (cp != NULL) {
259		pager_output("=");
260		pager_output(cp);
261	    }
262	    if (pager_output("\n"))
263		break;
264	}
265	pager_close();
266    } else {
267	if ((cp = getenv(argv[1])) != NULL) {
268	    printf("%s\n", cp);
269	} else {
270	    sprintf(command_errbuf, "variable '%s' not found", argv[1]);
271	    return(CMD_ERROR);
272	}
273    }
274    return(CMD_OK);
275}
276
277
278int
279command_set(int argc, char *argv[])
280{
281    int		err;
282
283    if (argc != 2) {
284	command_errmsg = "wrong number of arguments";
285	return(CMD_ERROR);
286    } else {
287	if ((err = putenv(argv[1])) != 0) {
288	    command_errmsg = strerror(err);
289	    return(CMD_ERROR);
290	}
291    }
292    return(CMD_OK);
293}
294
295
296int
297command_unset(int argc, char *argv[])
298{
299    int		err;
300
301    if (argc != 2) {
302	command_errmsg = "wrong number of arguments";
303	return(CMD_ERROR);
304    } else {
305	if ((err = unsetenv(argv[1])) != 0) {
306	    command_errmsg = strerror(err);
307	    return(CMD_ERROR);
308	}
309    }
310    return(CMD_OK);
311}
312
313
314int
315command_echo(int argc, char *argv[])
316{
317    char	*s;
318    int		nl, ch;
319
320    nl = 0;
321    optind = 1;
322    optreset = 1;
323    while ((ch = getopt(argc, argv, "n")) != -1) {
324	switch(ch) {
325	case 'n':
326	    nl = 1;
327	    break;
328	case '?':
329	default:
330	    /* getopt has already reported an error */
331	    return(CMD_OK);
332	}
333    }
334    argv += (optind);
335    argc -= (optind);
336
337    s = unargv(argc, argv);
338    if (s != NULL) {
339	printf("%s", s);
340	free(s);
341    }
342    if (!nl)
343	printf("\n");
344    return(CMD_OK);
345}
346
347/*
348 * A passable emulation of the sh(1) command of the same name.
349 */
350
351
352int
353command_read(int argc, char *argv[])
354{
355    char	*prompt;
356    int		timeout;
357    time_t	when;
358    char	*cp;
359    char	*name;
360    char	buf[256];		/* XXX size? */
361    int		c;
362
363    timeout = -1;
364    prompt = NULL;
365    optind = 1;
366    optreset = 1;
367    while ((c = getopt(argc, argv, "p:t:")) != -1) {
368	switch(c) {
369
370	case 'p':
371	    prompt = optarg;
372	    break;
373	case 't':
374	    timeout = strtol(optarg, &cp, 0);
375	    if (cp == optarg) {
376		sprintf(command_errbuf, "bad timeout '%s'", optarg);
377		return(CMD_ERROR);
378	    }
379	    break;
380	default:
381	    return(CMD_OK);
382	}
383    }
384
385    argv += (optind);
386    argc -= (optind);
387    name = (argc > 0) ? argv[0]: NULL;
388
389    if (prompt != NULL)
390	printf("%s", prompt);
391    if (timeout >= 0) {
392	when = time(NULL) + timeout;
393	while (!ischar())
394	    if (time(NULL) >= when)
395		return(CMD_OK);		/* is timeout an error? */
396    }
397
398    ngets(buf, sizeof(buf));
399
400    if (name != NULL)
401	setenv(name, buf, 1);
402    return(CMD_OK);
403}
404
405/*
406 * File pager
407 */
408
409int
410command_more(int argc, char *argv[])
411{
412    int         i;
413    int         res;
414    char	line[80];
415
416    res=0;
417    pager_open();
418    for (i = 1; (i < argc) && (res == 0); i++) {
419	sprintf(line, "*** FILE %s BEGIN ***\n", argv[i]);
420	if (pager_output(line))
421		break;
422        res = page_file(argv[i]);
423	if (!res) {
424	    sprintf(line, "*** FILE %s END ***\n", argv[i]);
425	    res = pager_output(line);
426	}
427    }
428    pager_close();
429
430    if (res == 0)
431	return CMD_OK;
432    else
433	return CMD_ERROR;
434}
435
436static int
437page_file(char *filename)
438{
439    int result;
440
441    result = pager_file(filename);
442
443    if (result == -1)
444	sprintf(command_errbuf, "error showing %s", filename);
445
446    return result;
447}
448
449/*
450 * List all disk-like devices
451 */
452
453int
454command_lsdev(int argc, char *argv[])
455{
456    int		verbose, ch, i;
457    char	line[80];
458
459    verbose = 0;
460    optind = 1;
461    optreset = 1;
462    while ((ch = getopt(argc, argv, "v")) != -1) {
463	switch(ch) {
464	case 'v':
465	    verbose = 1;
466	    break;
467	case '?':
468	default:
469	    /* getopt has already reported an error */
470	    return(CMD_OK);
471	}
472    }
473    argv += (optind);
474    argc -= (optind);
475
476    pager_open();
477
478    sprintf(line, "Device Enumeration:\n");
479    pager_output(line);
480
481    for (i = 0; i < ndevs; i++) {
482	 sprintf(line, "%s\n", devsw[i].dv_name);
483	    if (pager_output(line))
484		    break;
485    }
486
487    pager_close();
488    return(CMD_OK);
489}
490
491