1 /*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 /* 31 * Simple commandline interpreter, toplevel and misc. 32 * 33 * XXX may be obsoleted by BootFORTH or some other, better, interpreter. 34 */ 35 36 #include <stand.h> 37 #include <string.h> 38 #include "bootstrap.h" 39 40 #define MAXARGS 20 /* maximum number of arguments allowed */ 41 42 const char * volatile interp_identifier; 43 44 /* 45 * Interactive mode 46 */ 47 void 48 interact(void) 49 { 50 static char input[256]; /* big enough? */ 51 52 TSENTER(); 53 54 /* 55 * Because interp_identifier is volatile, it cannot be optimized out by 56 * the compiler as it's considered an externally observable event. This 57 * prevents the compiler from optimizing out our carefully placed 58 * $Interpreter:4th string that userboot may use to determine that 59 * we need to switch interpreters. 60 */ 61 interp_identifier = bootprog_interp; 62 interp_init(); 63 64 printf("\n"); 65 66 /* 67 * Before interacting, we might want to autoboot. 68 */ 69 autoboot_maybe(); 70 71 /* 72 * Not autobooting, go manual 73 */ 74 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 75 if (getenv("prompt") == NULL) 76 setenv("prompt", "${interpret}", 1); 77 if (getenv("interpret") == NULL) 78 setenv("interpret", "OK", 1); 79 80 for (;;) { 81 input[0] = '\0'; 82 interp_emit_prompt(); 83 ngets(input, sizeof(input)); 84 interp_run(input); 85 } 86 } 87 88 /* 89 * Read commands from a file, then execute them. 90 * 91 * We store the commands in memory and close the source file so that the media 92 * holding it can safely go away while we are executing. 93 * 94 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 95 * that the script won't stop if they fail). 96 */ 97 COMMAND_SET(include, "include", "read commands from a file", command_include); 98 99 static int 100 command_include(int argc, char *argv[]) 101 { 102 int i; 103 int res; 104 char **argvbuf; 105 106 /* 107 * Since argv is static, we need to save it here. 108 */ 109 argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); 110 for (i = 0; i < argc; i++) 111 argvbuf[i] = strdup(argv[i]); 112 113 res=CMD_OK; 114 for (i = 1; (i < argc) && (res == CMD_OK); i++) 115 res = interp_include(argvbuf[i]); 116 117 for (i = 0; i < argc; i++) 118 free(argvbuf[i]); 119 free(argvbuf); 120 121 return(res); 122 } 123 124 /* 125 * Emit the current prompt; use the same syntax as the parser 126 * for embedding environment variables. Does not accept input. 127 */ 128 void 129 interp_emit_prompt(void) 130 { 131 char *pr, *p, *cp, *ev; 132 133 if ((cp = getenv("prompt")) == NULL) 134 cp = ">"; 135 pr = p = strdup(cp); 136 137 while (*p != 0) { 138 if ((*p == '$') && (*(p+1) == '{')) { 139 for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 140 ; 141 *cp = 0; 142 ev = getenv(p + 2); 143 144 if (ev != NULL) 145 printf("%s", ev); 146 p = cp + 1; 147 continue; 148 } 149 putchar(*p++); 150 } 151 putchar(' '); 152 free(pr); 153 } 154 155 static struct bootblk_command * 156 interp_lookup_cmd(const char *cmd) 157 { 158 struct bootblk_command **cmdp; 159 160 /* search the command set for the command */ 161 SET_FOREACH(cmdp, Xcommand_set) { 162 if (((*cmdp)->c_name != NULL) && !strcmp(cmd, (*cmdp)->c_name)) 163 return (*cmdp); 164 } 165 return (NULL); 166 } 167 168 /* 169 * Perform a builtin command 170 */ 171 int 172 interp_builtin_cmd(int argc, char *argv[]) 173 { 174 int result; 175 struct bootblk_command *cmd; 176 177 if (argc < 1) 178 return (CMD_OK); 179 180 /* set return defaults; a successful command will override these */ 181 command_errmsg = command_errbuf; 182 strcpy(command_errbuf, "no error message"); 183 result = CMD_ERROR; 184 185 cmd = interp_lookup_cmd(argv[0]); 186 if (cmd != NULL && cmd->c_fn) { 187 result = cmd->c_fn(argc, argv); 188 } else { 189 command_errmsg = "unknown command"; 190 } 191 return (result); 192 } 193 194 /* 195 * Return true if the builtin command exists 196 */ 197 bool 198 interp_has_builtin_cmd(const char *cmd) 199 { 200 return (interp_lookup_cmd(cmd) != NULL); 201 } 202