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