1 /* 2 * (C) Copyright 2000 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * Add to readline cmdline-editing by 6 * (C) Copyright 2005 7 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> 8 * 9 * SPDX-License-Identifier: GPL-2.0+ 10 */ 11 12 #include <common.h> 13 #include <bootretry.h> 14 #include <cli.h> 15 #include <linux/ctype.h> 16 17 #define DEBUG_PARSER 0 /* set to 1 to debug */ 18 19 #define debug_parser(fmt, args...) \ 20 debug_cond(DEBUG_PARSER, fmt, ##args) 21 22 23 int cli_simple_parse_line(char *line, char *argv[]) 24 { 25 int nargs = 0; 26 27 debug_parser("%s: \"%s\"\n", __func__, line); 28 while (nargs < CONFIG_SYS_MAXARGS) { 29 /* skip any white space */ 30 while (isblank(*line)) 31 ++line; 32 33 if (*line == '\0') { /* end of line, no more args */ 34 argv[nargs] = NULL; 35 debug_parser("%s: nargs=%d\n", __func__, nargs); 36 return nargs; 37 } 38 39 argv[nargs++] = line; /* begin of argument string */ 40 41 /* find end of string */ 42 while (*line && !isblank(*line)) 43 ++line; 44 45 if (*line == '\0') { /* end of line, no more args */ 46 argv[nargs] = NULL; 47 debug_parser("parse_line: nargs=%d\n", nargs); 48 return nargs; 49 } 50 51 *line++ = '\0'; /* terminate current arg */ 52 } 53 54 printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); 55 56 debug_parser("%s: nargs=%d\n", __func__, nargs); 57 return nargs; 58 } 59 60 void cli_simple_process_macros(const char *input, char *output) 61 { 62 char c, prev; 63 const char *varname_start = NULL; 64 int inputcnt = strlen(input); 65 int outputcnt = CONFIG_SYS_CBSIZE; 66 int state = 0; /* 0 = waiting for '$' */ 67 68 /* 1 = waiting for '(' or '{' */ 69 /* 2 = waiting for ')' or '}' */ 70 /* 3 = waiting for ''' */ 71 char __maybe_unused *output_start = output; 72 73 debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), 74 input); 75 76 prev = '\0'; /* previous character */ 77 78 while (inputcnt && outputcnt) { 79 c = *input++; 80 inputcnt--; 81 82 if (state != 3) { 83 /* remove one level of escape characters */ 84 if ((c == '\\') && (prev != '\\')) { 85 if (inputcnt-- == 0) 86 break; 87 prev = c; 88 c = *input++; 89 } 90 } 91 92 switch (state) { 93 case 0: /* Waiting for (unescaped) $ */ 94 if ((c == '\'') && (prev != '\\')) { 95 state = 3; 96 break; 97 } 98 if ((c == '$') && (prev != '\\')) { 99 state++; 100 } else { 101 *(output++) = c; 102 outputcnt--; 103 } 104 break; 105 case 1: /* Waiting for ( */ 106 if (c == '(' || c == '{') { 107 state++; 108 varname_start = input; 109 } else { 110 state = 0; 111 *(output++) = '$'; 112 outputcnt--; 113 114 if (outputcnt) { 115 *(output++) = c; 116 outputcnt--; 117 } 118 } 119 break; 120 case 2: /* Waiting for ) */ 121 if (c == ')' || c == '}') { 122 int i; 123 char envname[CONFIG_SYS_CBSIZE], *envval; 124 /* Varname # of chars */ 125 int envcnt = input - varname_start - 1; 126 127 /* Get the varname */ 128 for (i = 0; i < envcnt; i++) 129 envname[i] = varname_start[i]; 130 envname[i] = 0; 131 132 /* Get its value */ 133 envval = getenv(envname); 134 135 /* Copy into the line if it exists */ 136 if (envval != NULL) 137 while ((*envval) && outputcnt) { 138 *(output++) = *(envval++); 139 outputcnt--; 140 } 141 /* Look for another '$' */ 142 state = 0; 143 } 144 break; 145 case 3: /* Waiting for ' */ 146 if ((c == '\'') && (prev != '\\')) { 147 state = 0; 148 } else { 149 *(output++) = c; 150 outputcnt--; 151 } 152 break; 153 } 154 prev = c; 155 } 156 157 if (outputcnt) 158 *output = 0; 159 else 160 *(output - 1) = 0; 161 162 debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", 163 strlen(output_start), output_start); 164 } 165 166 /* 167 * WARNING: 168 * 169 * We must create a temporary copy of the command since the command we get 170 * may be the result from getenv(), which returns a pointer directly to 171 * the environment data, which may change magicly when the command we run 172 * creates or modifies environment variables (like "bootp" does). 173 */ 174 int cli_simple_run_command(const char *cmd, int flag) 175 { 176 char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ 177 char *token; /* start of token in cmdbuf */ 178 char *sep; /* end of token (separator) in cmdbuf */ 179 char finaltoken[CONFIG_SYS_CBSIZE]; 180 char *str = cmdbuf; 181 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ 182 int argc, inquotes; 183 int repeatable = 1; 184 int rc = 0; 185 186 debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); 187 if (DEBUG_PARSER) { 188 /* use puts - string may be loooong */ 189 puts(cmd ? cmd : "NULL"); 190 puts("\"\n"); 191 } 192 clear_ctrlc(); /* forget any previous Control C */ 193 194 if (!cmd || !*cmd) 195 return -1; /* empty command */ 196 197 if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { 198 puts("## Command too long!\n"); 199 return -1; 200 } 201 202 strcpy(cmdbuf, cmd); 203 204 /* Process separators and check for invalid 205 * repeatable commands 206 */ 207 208 debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); 209 while (*str) { 210 /* 211 * Find separator, or string end 212 * Allow simple escape of ';' by writing "\;" 213 */ 214 for (inquotes = 0, sep = str; *sep; sep++) { 215 if ((*sep == '\'') && 216 (*(sep - 1) != '\\')) 217 inquotes = !inquotes; 218 219 if (!inquotes && 220 (*sep == ';') && /* separator */ 221 (sep != str) && /* past string start */ 222 (*(sep - 1) != '\\')) /* and NOT escaped */ 223 break; 224 } 225 226 /* 227 * Limit the token to data between separators 228 */ 229 token = str; 230 if (*sep) { 231 str = sep + 1; /* start of command for next pass */ 232 *sep = '\0'; 233 } else { 234 str = sep; /* no more commands for next pass */ 235 } 236 debug_parser("token: \"%s\"\n", token); 237 238 /* find macros in this token and replace them */ 239 cli_simple_process_macros(token, finaltoken); 240 241 /* Extract arguments */ 242 argc = cli_simple_parse_line(finaltoken, argv); 243 if (argc == 0) { 244 rc = -1; /* no command at all */ 245 continue; 246 } 247 248 if (cmd_process(flag, argc, argv, &repeatable, NULL)) 249 rc = -1; 250 251 /* Did the user stop this? */ 252 if (had_ctrlc()) 253 return -1; /* if stopped then not repeatable */ 254 } 255 256 return rc ? rc : repeatable; 257 } 258 259 void cli_simple_loop(void) 260 { 261 static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; 262 263 int len; 264 int flag; 265 int rc = 1; 266 267 for (;;) { 268 if (rc >= 0) { 269 /* Saw enough of a valid command to 270 * restart the timeout. 271 */ 272 bootretry_reset_cmd_timeout(); 273 } 274 len = cli_readline(CONFIG_SYS_PROMPT); 275 276 flag = 0; /* assume no special flags for now */ 277 if (len > 0) 278 strcpy(lastcommand, console_buffer); 279 else if (len == 0) 280 flag |= CMD_FLAG_REPEAT; 281 #ifdef CONFIG_BOOT_RETRY_TIME 282 else if (len == -2) { 283 /* -2 means timed out, retry autoboot 284 */ 285 puts("\nTimed out waiting for command\n"); 286 # ifdef CONFIG_RESET_TO_RETRY 287 /* Reinit board to run initialization code again */ 288 do_reset(NULL, 0, 0, NULL); 289 # else 290 return; /* retry autoboot */ 291 # endif 292 } 293 #endif 294 295 if (len == -1) 296 puts("<INTERRUPT>\n"); 297 else 298 rc = run_command_repeatable(lastcommand, flag); 299 300 if (rc <= 0) { 301 /* invalid command or not repeatable, forget it */ 302 lastcommand[0] = 0; 303 } 304 } 305 } 306 307 int cli_simple_run_command_list(char *cmd, int flag) 308 { 309 char *line, *next; 310 int rcode = 0; 311 312 /* 313 * Break into individual lines, and execute each line; terminate on 314 * error. 315 */ 316 next = cmd; 317 line = cmd; 318 while (*next) { 319 if (*next == '\n') { 320 *next = '\0'; 321 /* run only non-empty commands */ 322 if (*line) { 323 debug("** exec: \"%s\"\n", line); 324 if (cli_simple_run_command(line, 0) < 0) { 325 rcode = 1; 326 break; 327 } 328 } 329 line = next + 1; 330 } 331 ++next; 332 } 333 if (rcode == 0 && *line) 334 rcode = (cli_simple_run_command(line, 0) < 0); 335 336 return rcode; 337 } 338