1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2000 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 * 6 * Add to readline cmdline-editing by 7 * (C) Copyright 2005 8 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> 9 */ 10 11 #include <common.h> 12 #include <bootretry.h> 13 #include <cli.h> 14 #include <console.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 = env_get(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 env_get(), 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 + 1] = { 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 strlcpy(lastcommand, console_buffer, 279 CONFIG_SYS_CBSIZE + 1); 280 else if (len == 0) 281 flag |= CMD_FLAG_REPEAT; 282 #ifdef CONFIG_BOOT_RETRY_TIME 283 else if (len == -2) { 284 /* -2 means timed out, retry autoboot 285 */ 286 puts("\nTimed out waiting for command\n"); 287 # ifdef CONFIG_RESET_TO_RETRY 288 /* Reinit board to run initialization code again */ 289 do_reset(NULL, 0, 0, NULL); 290 # else 291 return; /* retry autoboot */ 292 # endif 293 } 294 #endif 295 296 if (len == -1) 297 puts("<INTERRUPT>\n"); 298 else 299 rc = run_command_repeatable(lastcommand, flag); 300 301 if (rc <= 0) { 302 /* invalid command or not repeatable, forget it */ 303 lastcommand[0] = 0; 304 } 305 } 306 } 307 308 int cli_simple_run_command_list(char *cmd, int flag) 309 { 310 char *line, *next; 311 int rcode = 0; 312 313 /* 314 * Break into individual lines, and execute each line; terminate on 315 * error. 316 */ 317 next = cmd; 318 line = cmd; 319 while (*next) { 320 if (*next == '\n') { 321 *next = '\0'; 322 /* run only non-empty commands */ 323 if (*line) { 324 debug("** exec: \"%s\"\n", line); 325 if (cli_simple_run_command(line, 0) < 0) { 326 rcode = 1; 327 break; 328 } 329 } 330 line = next + 1; 331 } 332 ++next; 333 } 334 if (rcode == 0 && *line) 335 rcode = (cli_simple_run_command(line, 0) < 0); 336 337 return rcode; 338 } 339