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$"); 29119483Sobrien 3038465Smsmith#include <stand.h> 3138465Smsmith#include <string.h> 3238465Smsmith 3338465Smsmith#include "bootstrap.h" 3438465Smsmith 3538465Smsmithchar *command_errmsg; 3638465Smsmithchar command_errbuf[256]; /* XXX should have procedural interface for setting, size limit? */ 3740775Smsmith 3843491Sjkhstatic int page_file(char *filename); 3940775Smsmith 4040775Smsmith/* 4140775Smsmith * Help is read from a formatted text file. 4240775Smsmith * 4340775Smsmith * Entries in the file are formatted as 4440775Smsmith 4540775Smsmith# Ttopic [Ssubtopic] Ddescription 4640775Smsmithhelp 4740775Smsmithtext 4840775Smsmithhere 4940775Smsmith# 5040775Smsmith 5140775Smsmith * 5240775Smsmith * Note that for code simplicity's sake, the above format must be followed 5340775Smsmith * exactly. 5440775Smsmith * 5540775Smsmith * Subtopic entries must immediately follow the topic (this is used to 5640775Smsmith * produce the listing of subtopics). 5740775Smsmith * 5840775Smsmith * If no argument(s) are supplied by the user, the help for 'help' is displayed. 5940775Smsmith */ 6038465SmsmithCOMMAND_SET(help, "help", "detailed help", command_help); 6138465Smsmith 6238465Smsmithstatic int 6340775Smsmithhelp_getnext(int fd, char **topic, char **subtopic, char **desc) 6438465Smsmith{ 6540775Smsmith char line[81], *cp, *ep; 6640775Smsmith 6740775Smsmith for (;;) { 6840775Smsmith if (fgetstr(line, 80, fd) < 0) 6940775Smsmith return(0); 7040775Smsmith 7140775Smsmith if ((strlen(line) < 3) || (line[0] != '#') || (line[1] != ' ')) 7240775Smsmith continue; 7338465Smsmith 7440775Smsmith *topic = *subtopic = *desc = NULL; 7540775Smsmith cp = line + 2; 7640775Smsmith while((cp != NULL) && (*cp != 0)) { 7740775Smsmith ep = strchr(cp, ' '); 7840775Smsmith if ((*cp == 'T') && (*topic == NULL)) { 7940775Smsmith if (ep != NULL) 8040775Smsmith *ep++ = 0; 8140775Smsmith *topic = strdup(cp + 1); 8240775Smsmith } else if ((*cp == 'S') && (*subtopic == NULL)) { 8340775Smsmith if (ep != NULL) 8440775Smsmith *ep++ = 0; 8540775Smsmith *subtopic = strdup(cp + 1); 8640775Smsmith } else if (*cp == 'D') { 8740775Smsmith *desc = strdup(cp + 1); 8840775Smsmith ep = NULL; 8940775Smsmith } 9040775Smsmith cp = ep; 9140775Smsmith } 9240775Smsmith if (*topic == NULL) { 9340775Smsmith if (*subtopic != NULL) 9440775Smsmith free(*subtopic); 9540775Smsmith if (*desc != NULL) 9640775Smsmith free(*desc); 9740775Smsmith continue; 9840775Smsmith } 9940775Smsmith return(1); 10040775Smsmith } 10140775Smsmith} 10240775Smsmith 103135929Srustatic int 10440775Smsmithhelp_emitsummary(char *topic, char *subtopic, char *desc) 10540775Smsmith{ 10640775Smsmith int i; 10740775Smsmith 10840775Smsmith pager_output(" "); 10940775Smsmith pager_output(topic); 11040775Smsmith i = strlen(topic); 11140775Smsmith if (subtopic != NULL) { 11240775Smsmith pager_output(" "); 11340775Smsmith pager_output(subtopic); 11440775Smsmith i += strlen(subtopic) + 1; 11540775Smsmith } 11640775Smsmith if (desc != NULL) { 11740775Smsmith do { 11840775Smsmith pager_output(" "); 11940775Smsmith } while (i++ < 30); 12040775Smsmith pager_output(desc); 12140775Smsmith } 122135929Sru return (pager_output("\n")); 12340775Smsmith} 12440775Smsmith 12540775Smsmith 12640775Smsmithstatic int 12740775Smsmithcommand_help(int argc, char *argv[]) 12840775Smsmith{ 12940775Smsmith char buf[81]; /* XXX buffer size? */ 13040775Smsmith int hfd, matched, doindex; 13140775Smsmith char *topic, *subtopic, *t, *s, *d; 13240775Smsmith 13338465Smsmith /* page the help text from our load path */ 13440775Smsmith sprintf(buf, "%s/boot/loader.help", getenv("loaddev")); 13540775Smsmith if ((hfd = open(buf, O_RDONLY)) < 0) { 13638465Smsmith printf("Verbose help not available, use '?' to list commands\n"); 13740775Smsmith return(CMD_OK); 13840775Smsmith } 13940775Smsmith 14040775Smsmith /* pick up request from arguments */ 14140775Smsmith topic = subtopic = NULL; 14240775Smsmith switch(argc) { 14340775Smsmith case 3: 14440775Smsmith subtopic = strdup(argv[2]); 14540775Smsmith case 2: 14640775Smsmith topic = strdup(argv[1]); 14740775Smsmith break; 14840775Smsmith case 1: 14940775Smsmith topic = strdup("help"); 15040775Smsmith break; 15140775Smsmith default: 15240775Smsmith command_errmsg = "usage is 'help <topic> [<subtopic>]"; 153198537Sbrueffer close(hfd); 15440775Smsmith return(CMD_ERROR); 15540775Smsmith } 15640775Smsmith 15740775Smsmith /* magic "index" keyword */ 15840775Smsmith doindex = !strcmp(topic, "index"); 15940775Smsmith matched = doindex; 16040775Smsmith 16140775Smsmith /* Scan the helpfile looking for help matching the request */ 16240775Smsmith pager_open(); 16340775Smsmith while(help_getnext(hfd, &t, &s, &d)) { 16440775Smsmith 16540775Smsmith if (doindex) { /* dink around formatting */ 166135929Sru if (help_emitsummary(t, s, d)) 167135929Sru break; 16840775Smsmith 16940775Smsmith } else if (strcmp(topic, t)) { 17040775Smsmith /* topic mismatch */ 17140775Smsmith if(matched) /* nothing more on this topic, stop scanning */ 17240775Smsmith break; 17340775Smsmith 17440775Smsmith } else { 17540775Smsmith /* topic matched */ 17640775Smsmith matched = 1; 17740775Smsmith if (((subtopic == NULL) && (s == NULL)) || 17840775Smsmith ((subtopic != NULL) && (s != NULL) && !strcmp(subtopic, s))) { 17940775Smsmith /* exact match, print text */ 18040775Smsmith while((fgetstr(buf, 80, hfd) >= 0) && (buf[0] != '#')) { 18155173Smsmith if (pager_output(buf)) 18255173Smsmith break; 18355173Smsmith if (pager_output("\n")) 18455173Smsmith break; 18540775Smsmith } 18640775Smsmith } else if ((subtopic == NULL) && (s != NULL)) { 18740775Smsmith /* topic match, list subtopics */ 188135929Sru if (help_emitsummary(t, s, d)) 189135929Sru break; 19040775Smsmith } 19140775Smsmith } 19240775Smsmith free(t); 19340775Smsmith free(s); 19440775Smsmith free(d); 19540775Smsmith } 19640775Smsmith pager_close(); 19740775Smsmith close(hfd); 19840775Smsmith if (!matched) { 19940775Smsmith sprintf(command_errbuf, "no help available for '%s'", topic); 20044570Sdcs free(topic); 20144570Sdcs if (subtopic) 20244570Sdcs free(subtopic); 20340775Smsmith return(CMD_ERROR); 20440775Smsmith } 20544570Sdcs free(topic); 20644570Sdcs if (subtopic) 20744570Sdcs free(subtopic); 20838465Smsmith return(CMD_OK); 20938465Smsmith} 21038465Smsmith 21140775Smsmith 21238465SmsmithCOMMAND_SET(commandlist, "?", "list commands", command_commandlist); 21338465Smsmith 21438465Smsmithstatic int 21538465Smsmithcommand_commandlist(int argc, char *argv[]) 21638465Smsmith{ 21738465Smsmith struct bootblk_command **cmdp; 218137615Sru int res; 219137615Sru char name[20]; 220137615Sru 221137615Sru res = 0; 222137615Sru pager_open(); 223137615Sru res = pager_output("Available commands:\n"); 22440553Smsmith SET_FOREACH(cmdp, Xcommand_set) { 225137615Sru if (res) 226137615Sru break; 227137615Sru if (((*cmdp)->c_name != NULL) && ((*cmdp)->c_desc != NULL)) { 228137615Sru sprintf(name, " %-15s ", (*cmdp)->c_name); 229137615Sru pager_output(name); 230137615Sru pager_output((*cmdp)->c_desc); 231137615Sru res = pager_output("\n"); 232137615Sru } 23340553Smsmith } 234137615Sru pager_close(); 23538465Smsmith return(CMD_OK); 23638465Smsmith} 23738465Smsmith 23838465Smsmith/* 23938465Smsmith * XXX set/show should become set/echo if we have variable 24038465Smsmith * substitution happening. 24138465Smsmith */ 24238465Smsmith 24338465SmsmithCOMMAND_SET(show, "show", "show variable(s)", command_show); 24438465Smsmith 24538465Smsmithstatic int 24638465Smsmithcommand_show(int argc, char *argv[]) 24738465Smsmith{ 24838465Smsmith struct env_var *ev; 24938465Smsmith char *cp; 25038465Smsmith 25138465Smsmith if (argc < 2) { 25238465Smsmith /* 25338465Smsmith * With no arguments, print everything. 25438465Smsmith */ 25538465Smsmith pager_open(); 25638465Smsmith for (ev = environ; ev != NULL; ev = ev->ev_next) { 25738465Smsmith pager_output(ev->ev_name); 25838465Smsmith cp = getenv(ev->ev_name); 25938465Smsmith if (cp != NULL) { 26038465Smsmith pager_output("="); 26138465Smsmith pager_output(cp); 26238465Smsmith } 26361653Sps if (pager_output("\n")) 26461653Sps break; 26538465Smsmith } 26638465Smsmith pager_close(); 26738465Smsmith } else { 26838465Smsmith if ((cp = getenv(argv[1])) != NULL) { 26938465Smsmith printf("%s\n", cp); 27038465Smsmith } else { 27138465Smsmith sprintf(command_errbuf, "variable '%s' not found", argv[1]); 27238465Smsmith return(CMD_ERROR); 27338465Smsmith } 27438465Smsmith } 27538465Smsmith return(CMD_OK); 27638465Smsmith} 27738465Smsmith 27838465SmsmithCOMMAND_SET(set, "set", "set a variable", command_set); 27938465Smsmith 28038465Smsmithstatic int 28138465Smsmithcommand_set(int argc, char *argv[]) 28238465Smsmith{ 28338465Smsmith int err; 28438465Smsmith 28538465Smsmith if (argc != 2) { 28638465Smsmith command_errmsg = "wrong number of arguments"; 28738465Smsmith return(CMD_ERROR); 28838465Smsmith } else { 28938465Smsmith if ((err = putenv(argv[1])) != 0) { 29038465Smsmith command_errmsg = strerror(err); 29138465Smsmith return(CMD_ERROR); 29238465Smsmith } 29338465Smsmith } 29438465Smsmith return(CMD_OK); 29538465Smsmith} 29638465Smsmith 29738465SmsmithCOMMAND_SET(unset, "unset", "unset a variable", command_unset); 29838465Smsmith 29938465Smsmithstatic int 30038465Smsmithcommand_unset(int argc, char *argv[]) 30138465Smsmith{ 30238465Smsmith int err; 30338465Smsmith 30438465Smsmith if (argc != 2) { 30538465Smsmith command_errmsg = "wrong number of arguments"; 30638465Smsmith return(CMD_ERROR); 30738465Smsmith } else { 30838465Smsmith if ((err = unsetenv(argv[1])) != 0) { 30938465Smsmith command_errmsg = strerror(err); 31038465Smsmith return(CMD_ERROR); 31138465Smsmith } 31238465Smsmith } 31338465Smsmith return(CMD_OK); 31438465Smsmith} 31538465Smsmith 316137667SruCOMMAND_SET(echo, "echo", "echo arguments", command_echo); 31738764Smsmith 31838764Smsmithstatic int 31938764Smsmithcommand_echo(int argc, char *argv[]) 32038764Smsmith{ 32138764Smsmith char *s; 32238764Smsmith int nl, ch; 32338764Smsmith 32438764Smsmith nl = 0; 32538764Smsmith optind = 1; 32642512Smsmith optreset = 1; 32738764Smsmith while ((ch = getopt(argc, argv, "n")) != -1) { 32838764Smsmith switch(ch) { 32938764Smsmith case 'n': 33038764Smsmith nl = 1; 33138764Smsmith break; 33238764Smsmith case '?': 33338764Smsmith default: 33438764Smsmith /* getopt has already reported an error */ 33538764Smsmith return(CMD_OK); 33638764Smsmith } 33738764Smsmith } 33838764Smsmith argv += (optind); 33938764Smsmith argc -= (optind); 34038764Smsmith 34138764Smsmith s = unargv(argc, argv); 34238764Smsmith if (s != NULL) { 34362873Skris printf("%s", s); 34438764Smsmith free(s); 34538764Smsmith } 34638764Smsmith if (!nl) 34738764Smsmith printf("\n"); 34838764Smsmith return(CMD_OK); 34938764Smsmith} 35038764Smsmith 35140015Smsmith/* 35240015Smsmith * A passable emulation of the sh(1) command of the same name. 35340015Smsmith */ 35440015Smsmith 355137667SruCOMMAND_SET(read, "read", "read input from the terminal", command_read); 35640015Smsmith 35740015Smsmithstatic int 35840015Smsmithcommand_read(int argc, char *argv[]) 35940015Smsmith{ 36040015Smsmith char *prompt; 36140015Smsmith int timeout; 36240015Smsmith time_t when; 36340015Smsmith char *cp; 36440015Smsmith char *name; 36540015Smsmith char buf[256]; /* XXX size? */ 36640015Smsmith int c; 36740015Smsmith 36840015Smsmith timeout = -1; 36940015Smsmith prompt = NULL; 37040015Smsmith optind = 1; 37142512Smsmith optreset = 1; 37240015Smsmith while ((c = getopt(argc, argv, "p:t:")) != -1) { 37340015Smsmith switch(c) { 37440015Smsmith 37540015Smsmith case 'p': 37640015Smsmith prompt = optarg; 37740015Smsmith break; 37840015Smsmith case 't': 37940015Smsmith timeout = strtol(optarg, &cp, 0); 38040015Smsmith if (cp == optarg) { 38140015Smsmith sprintf(command_errbuf, "bad timeout '%s'", optarg); 38240015Smsmith return(CMD_ERROR); 38340015Smsmith } 38440015Smsmith break; 38540015Smsmith default: 38640015Smsmith return(CMD_OK); 38740015Smsmith } 38840015Smsmith } 38940015Smsmith 39040015Smsmith argv += (optind); 39140015Smsmith argc -= (optind); 39240015Smsmith name = (argc > 0) ? argv[0]: NULL; 39340015Smsmith 39440015Smsmith if (prompt != NULL) 39562873Skris printf("%s", prompt); 39640015Smsmith if (timeout >= 0) { 39740015Smsmith when = time(NULL) + timeout; 39840015Smsmith while (!ischar()) 39940015Smsmith if (time(NULL) >= when) 40040015Smsmith return(CMD_OK); /* is timeout an error? */ 40140015Smsmith } 40240015Smsmith 40340015Smsmith ngets(buf, sizeof(buf)); 40440015Smsmith 40540015Smsmith if (name != NULL) 40640015Smsmith setenv(name, buf, 1); 40740015Smsmith return(CMD_OK); 40840015Smsmith} 40940775Smsmith 41040775Smsmith/* 41143491Sjkh * File pager 41243491Sjkh */ 41343491SjkhCOMMAND_SET(more, "more", "show contents of a file", command_more); 41443491Sjkh 41543491Sjkhstatic int 41643491Sjkhcommand_more(int argc, char *argv[]) 41743491Sjkh{ 41843491Sjkh int i; 41943491Sjkh int res; 42043491Sjkh char line[80]; 42143491Sjkh 42243491Sjkh res=0; 42343491Sjkh pager_open(); 42443491Sjkh for (i = 1; (i < argc) && (res == 0); i++) { 42543491Sjkh sprintf(line, "*** FILE %s BEGIN ***\n", argv[i]); 42672879Sdcs if (pager_output(line)) 42772879Sdcs break; 42843491Sjkh res = page_file(argv[i]); 42943491Sjkh if (!res) { 43043491Sjkh sprintf(line, "*** FILE %s END ***\n", argv[i]); 43172879Sdcs res = pager_output(line); 43243491Sjkh } 43343491Sjkh } 43443491Sjkh pager_close(); 43543491Sjkh 43643491Sjkh if (res == 0) 43743491Sjkh return CMD_OK; 43843491Sjkh else 43943491Sjkh return CMD_ERROR; 44043491Sjkh} 44143491Sjkh 44243491Sjkhstatic int 44343491Sjkhpage_file(char *filename) 44443491Sjkh{ 44543491Sjkh int result; 44643491Sjkh 44743491Sjkh result = pager_file(filename); 44843491Sjkh 44943491Sjkh if (result == -1) 45043491Sjkh sprintf(command_errbuf, "error showing %s", filename); 45143491Sjkh 45243491Sjkh return result; 45343491Sjkh} 45443491Sjkh 45543491Sjkh/* 45640775Smsmith * List all disk-like devices 45740775Smsmith */ 45842418SmsmithCOMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev); 45940775Smsmith 46040775Smsmithstatic int 46140775Smsmithcommand_lsdev(int argc, char *argv[]) 46240775Smsmith{ 46340775Smsmith int verbose, ch, i; 46440775Smsmith char line[80]; 46540775Smsmith 46640775Smsmith verbose = 0; 46740775Smsmith optind = 1; 46842512Smsmith optreset = 1; 46940775Smsmith while ((ch = getopt(argc, argv, "v")) != -1) { 47040775Smsmith switch(ch) { 47140775Smsmith case 'v': 47240775Smsmith verbose = 1; 47340775Smsmith break; 47440775Smsmith case '?': 47540775Smsmith default: 47640775Smsmith /* getopt has already reported an error */ 47740775Smsmith return(CMD_OK); 47840775Smsmith } 47940775Smsmith } 48040775Smsmith argv += (optind); 48140775Smsmith argc -= (optind); 48240775Smsmith 48340775Smsmith pager_open(); 48440775Smsmith for (i = 0; devsw[i] != NULL; i++) { 48540775Smsmith if (devsw[i]->dv_print != NULL){ 48685997Sjhb sprintf(line, "%s devices:\n", devsw[i]->dv_name); 48772879Sdcs if (pager_output(line)) 48872879Sdcs break; 48940775Smsmith devsw[i]->dv_print(verbose); 49040775Smsmith } else { 49140775Smsmith sprintf(line, "%s: (unknown)\n", devsw[i]->dv_name); 49272879Sdcs if (pager_output(line)) 49372879Sdcs break; 49440775Smsmith } 49540775Smsmith } 49640775Smsmith pager_close(); 49740775Smsmith return(CMD_OK); 49840775Smsmith} 49942418Smsmith 500