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