]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cli.c
rewrite of cmd_cpu/do_cpu_freq
[z180-stamp.git] / avr / cli.c
CommitLineData
35edb766 1/*
04b3ea0e 2 * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
35edb766
L
3 *
4 * (C) Copyright 2000
5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6 *
7 * Add to readline cmdline-editing by
8 * (C) Copyright 2005
9 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
10 *
8ed66016 11 * SPDX-License-Identifier: GPL-2.0
35edb766
L
12 */
13
8ed66016 14#include "cli.h"
2dec14cd 15#include "command.h"
d684c216 16#include <ctype.h>
d684c216
L
17
18#include "config.h"
d684c216
L
19#include "debug.h"
20#include "env.h"
21#include "cli_readline.h"
22#include "con-utils.h"
d684c216 23
bbd45c46
L
24
25/* FIXME: Quoting problems */
26
d684c216
L
27#define DEBUG_PARSER 0 /* set to 1 to debug */
28
29#define debug_parser(fmt, args...) \
30 debug_cond(DEBUG_PARSER, fmt, ##args)
31
13e88ed5 32
78035a8d
L
33static int_least8_t exec_flags;
34#define OPT_XTRACE 1
35#define OPT_VERBOSE 2
36
13e88ed5
L
37static int_least8_t command_level;
38
39static void cli_trace_cmd(int_fast8_t level, int argc, char *argv[])
40{
41 while (level-- > 0)
42 putchar('+');
43 for (int_fast8_t i = 0; i < argc; i++)
44 printf_P(PSTR(" %s"), argv[i]);
45 putchar('\n');
46}
47
48
49
50
d684c216
L
51static int cli_parse_line(char *line, char *argv[])
52{
b0e4f7e5 53 uint_fast8_t state = 0;
d684c216 54 uint_fast8_t nargs = 0;
b0e4f7e5
L
55 char *inp, *outp;
56 char c, quote;
57
58 debug_parser("%s: \"%s\"\n", __func__, line);
59
aca998c3 60 for (outp = inp = line, quote = '\0'; (c = *inp) != '\0'; inp++) {
b0e4f7e5
L
61
62 switch (state) {
bbd45c46 63 case 0: /* before arg string, waiting for arg start */
b0e4f7e5
L
64 if (isblank(c))
65 continue;
66
67 argv[nargs++] = inp; /* begin of argument string */
68 outp = inp;
69 state = 1;
70 /* fall thru */
71
bbd45c46 72 case 1: /* in arg string, waiting for end of arg string */
b0e4f7e5
L
73 if (c == '\\') {
74 ++state;
75 continue;
76 }
77 if (c == '\"' || c == '\'') {
78 quote = c;
79 state = 3;
80 continue;
81 }
82 if (isblank(c)) {
83 c = '\0';
84 state = 0;
85 }
86 break;
87
bbd45c46 88 case 3: /* in quote */
b0e4f7e5
L
89 if (c == '\\' && quote == '\"') {
90 ++state;
91 continue;
92 }
93 if (c == quote) {
94 state = 1;
95 continue;
96 }
97 break;
d684c216 98
bbd45c46 99 case 2: /* waiting for next char */
b0e4f7e5
L
100 case 4:
101 --state;
102 break;
d684c216 103
b0e4f7e5 104 }
aca998c3
L
105
106 if (nargs > CONFIG_SYS_MAXARGS) {
107 --nargs;
108 break;
109 }
b0e4f7e5 110 *outp++ = c;
d684c216
L
111 }
112
b0e4f7e5 113 if (*inp != '\0')
d684c216
L
114 printf_P(PSTR("** Too many args (max. %d) **\n"), CONFIG_SYS_MAXARGS);
115
b0e4f7e5 116 *outp = '\0';
d684c216 117 argv[nargs] = NULL;
b0e4f7e5
L
118 debug_parser("%s: nargs=%d\n", __func__, nargs);
119#if 0
120 for (int i = 0; i < nargs; i++)
121 debug_parser("%s: arg %d: >%s<\n", __func__, i, argv[i]);
122#endif
d684c216 123 return nargs;
b0e4f7e5 124
d684c216
L
125}
126
507d25e2
L
127static
128void append_char(uint_fast8_t pass, char **p, char c)
d684c216 129{
507d25e2
L
130
131 if (pass) {
132 **p = c;
133 }
134 ++(*p);
135}
136
137static
78035a8d 138char *process_macros(char *input)
507d25e2 139{
78035a8d
L
140 char c, prev, *inp;
141 char *output = NULL;
142 char *outp = NULL;
507d25e2
L
143 const char *varname = NULL;
144
78035a8d
L
145 debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n",
146 strlen(input), input);
147
507d25e2
L
148 for(uint_fast8_t pass = 0; pass < 2; pass++)
149 {
b0e4f7e5
L
150 uint_fast8_t state = 0;
151 /* 0 = waiting for '$' */
507d25e2
L
152 /* 1 = waiting for '{' */
153 /* 2 = waiting for '}' */
154 /* 3 = waiting for ''' */
155
78035a8d
L
156 if (pass > 0) {
157 size_t outputlen = outp - (char *) NULL;
158 output = (char *) malloc(outputlen);
159 if (output == NULL) {
160 printf_P(PSTR("** Can't process command: Out of memory! **\n"));
161 return NULL;
162 }
507d25e2 163 outp = output;
d684c216
L
164 }
165
507d25e2 166 inp = input;
507d25e2 167
b0e4f7e5 168 for (prev = '\0'; (c = *inp++) != '\0'; prev = c) {
507d25e2 169
507d25e2 170
507d25e2
L
171
172 switch (state) {
173 case 0: /* Waiting for (unescaped) $ */
174 if ((c == '\'') && (prev != '\\')) {
175 state = 3;
176 break;
177 }
b0e4f7e5 178 if ((c == '$') && (prev != '\\')) {
507d25e2 179 state++;
b0e4f7e5
L
180 continue;
181 }
507d25e2
L
182 break;
183 case 1: /* Waiting for { */
184 if (c == '{') {
185 state++;
186 varname = inp;
b0e4f7e5 187 continue;
507d25e2
L
188 } else {
189 state = 0;
190 append_char(pass, &outp, '$');
507d25e2
L
191 }
192 break;
193 case 2: /* Waiting for } */
194 if (c == '}') {
195 /* Terminate variable name */
196 *(inp-1) = '\0';
bddc7e77 197 const char *envval = getenv_str(varname);
507d25e2
L
198 *(inp-1) = '}';
199 /* Copy into the line if it exists */
200 if (envval != NULL)
201 while (*envval)
202 append_char(pass, &outp, *(envval++));
203 /* Look for another '$' */
204 state = 0;
205 }
b0e4f7e5 206 continue;
507d25e2 207 case 3: /* Waiting for ' */
b0e4f7e5 208 if (c == '\'')
507d25e2 209 state = 0;
507d25e2 210 break;
d684c216 211 }
b0e4f7e5 212 append_char(pass, &outp, c);
d684c216 213 }
d684c216 214
507d25e2
L
215 append_char(pass, &outp, 0);
216 }
d684c216
L
217
218 debug_parser("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
507d25e2
L
219 strlen(output), output);
220
221 return output;
d684c216
L
222}
223
f76ca346
L
224/**
225 *
226 *
d684c216
L
227 * WARNING:
228 *
229 * We must create a temporary copy of the command since the command we get
bddc7e77 230 * may be the result from getenv_str(), which returns a pointer directly to
d684c216
L
231 * the environment data, which may change magicly when the command we run
232 * creates or modifies environment variables (like "bootp" does).
f76ca346
L
233 *
234 *
235 * @param cmd
236 * @param flag
237 * @returns
238 *
d684c216 239 */
fcf1d5b3 240static int cli_run_command(const char *cmd, uint_fast8_t flag)
d684c216 241{
78035a8d 242 char cmdbuf[strlen(cmd) + 1]; /* working copy of cmd */
d684c216
L
243 char *token; /* start of token in cmdbuf */
244 char *sep; /* end of token (separator) in cmdbuf */
507d25e2 245 char *finaltoken = NULL; /* token after macro expansion */
d684c216
L
246 char *str;
247 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
248 int argc;
249 uint_fast8_t inquotes, repeatable = 1;
250 int rc = 0;
251
78035a8d 252 exec_flags = 0;
13e88ed5
L
253 char *optenv = getenv_str(PSTR("cli"));
254 if (optenv) {
78035a8d
L
255 if (strstr_P(optenv, PSTR("verbose")) != NULL)
256 exec_flags |= OPT_VERBOSE;
257 if (strstr_P(optenv, PSTR("xtrace")) != NULL)
258 exec_flags |= OPT_XTRACE;
13e88ed5
L
259 }
260
f76ca346 261 debug_parser("[RUN_COMMAND] cmd[%p]=\"%s\"\n",
d684c216
L
262 cmd, cmd ? cmd : "NULL");
263
78035a8d 264 if (exec_flags & OPT_VERBOSE)
13e88ed5
L
265 printf_P(PSTR("%s\n"), cmd, cmd ? cmd : "");
266
d684c216
L
267 if (!cmd || !*cmd)
268 return -1; /* empty command */
269
78035a8d 270 clear_ctrlc(); /* forget any previous Control C */
d684c216 271
78035a8d
L
272 str = strcpy(cmdbuf, cmd);
273 //str = cmdbuf;
13e88ed5 274 ++command_level;
d684c216
L
275
276 /* Process separators and check for invalid
277 * repeatable commands
278 */
279
78035a8d 280 debug_parser("[PROCESS_SEPARATORS] \"%s\"\n", cmd);
d684c216
L
281 while (*str) {
282 /*
283 * Find separator, or string end
284 * Allow simple escape of ';' by writing "\;"
285 */
286 for (inquotes = 0, sep = str; *sep; sep++) {
287 if ((*sep == '\'') &&
01c1907d
L
288 (sep != str) && /* past string start */
289 (*(sep - 1) != '\\')) /* and NOT escaped */
d684c216
L
290 inquotes = !inquotes;
291
292 if (!inquotes &&
01c1907d
L
293 (*sep == ';' || *sep == '\n' /* separator */
294 || *sep == '#') && /* or start of comment */
295 ((sep == str) || /* string start */
296 (*(sep - 1) != '\\'))) /* or NOT escaped */
d684c216
L
297 break;
298 }
299
01c1907d
L
300 /* no more commands after unescaped '#' token */
301 if (*sep == '#')
302 *sep = '\0';
303
304 /* Limit the token to data between separators */
d684c216
L
305 token = str;
306 if (*sep) {
01c1907d 307 str = sep + 1; /* start of command for next pass */
d684c216
L
308 *sep = '\0';
309 } else {
01c1907d 310 str = sep; /* no more commands for next pass */
d684c216 311 }
7a66f43c
L
312 while (isblank(*token))
313 ++token;
314 if (*token == '\0') /* empty (comment only) */
315 continue;
d684c216
L
316 debug_parser("token: \"%s\"\n", token);
317
318 /* find macros in this token and replace them */
78035a8d
L
319 finaltoken = process_macros(token);
320 if (finaltoken == NULL) {
321 rc = -1; /* no command at all */
322 continue;
323 }
d684c216
L
324
325 /* Extract arguments */
326 argc = cli_parse_line(finaltoken, argv);
327 if (argc == 0) {
78035a8d
L
328 rc = -1; /* no command at all */
329 free(finaltoken);
330 continue;
d684c216
L
331 }
332
78035a8d 333 if (exec_flags & OPT_XTRACE)
13e88ed5
L
334 cli_trace_cmd(command_level, argc, argv);
335
336 rc = cmd_process(flag, argc, argv, &repeatable);
78035a8d 337 free(finaltoken);
13e88ed5 338 if (rc != CMD_RET_SUCCESS) {
78035a8d 339 if (exec_flags & OPT_VERBOSE)
13e88ed5 340 printf_P(PSTR("Command failed, result=%d\n"), rc);
d684c216 341 rc = -1;
13e88ed5 342 }
d684c216
L
343
344 /* Did the user stop this? */
345 if (had_ctrlc()) {
346 rc = -1; /* if stopped then not repeatable */
347 break;
348 }
349 }
350
13e88ed5 351 --command_level;
d684c216
L
352
353 return rc ? rc : repeatable;
354}
355
356static int cli_run_command_list(const char *cmd)
357{
358 return (cli_run_command(cmd, 0) < 0);
359}
360
361/******************************************************************************/
362
363
364/*
b08e079d 365 * Run a command.
d684c216
L
366 *
367 * @param cmd Command to run
368 * @param flag Execution flags (CMD_FLAG_...)
369 * @return 0 on success, or != 0 on error.
370 */
fcf1d5b3 371int run_command(const char *cmd, uint_fast8_t flag)
d684c216
L
372{
373 /*
374 * cli_run_command can return 0 or 1 for success, so clean up
375 * its result.
376 */
377 if (cli_run_command(cmd, flag) == -1)
378 return 1;
379
380 return 0;
381}
382
383/*
384 * Run a command using the selected parser, and check if it is repeatable.
385 *
386 * @param cmd Command to run
387 * @param flag Execution flags (CMD_FLAG_...)
388 * @return 0 (not repeatable) or 1 (repeatable) on success, -1 on error.
389 */
fcf1d5b3 390static int run_command_repeatable(const char *cmd, uint_fast8_t flag)
d684c216
L
391{
392 return cli_run_command(cmd, flag);
393}
394
395int run_command_list(const char *cmd, int len)
396{
397 (void) len;
f76ca346 398
d684c216
L
399 return cli_run_command_list(cmd);
400}
401
402/****************************************************************************/
403
d684c216
L
404
405void cli_loop(void)
406{
d684c216 407 char *lastcommand = NULL;
d684c216 408 int len;
fcf1d5b3 409 uint_fast8_t flag;
d684c216
L
410 int rc = 1;
411
412 for (;;) {
66e23a9d 413 len = cli_readline(lastcommand ? PSTR(CONFIG_SYS_PROMPT_REPEAT) : PSTR(CONFIG_SYS_PROMPT), 1);
d684c216
L
414
415 flag = 0; /* assume no special flags for now */
416 if (len > 0) {
69988dc1
L
417 free (lastcommand);
418 lastcommand = strdup(console_buffer);
d684c216
L
419 } else if (len == 0)
420 flag |= CMD_FLAG_REPEAT;
421
13e88ed5 422 if (len == -1) {
78035a8d 423 if (exec_flags & OPT_VERBOSE)
13e88ed5
L
424 my_puts_P(PSTR("<INTERRUPT>\n"));
425 } else
d684c216
L
426 rc = run_command_repeatable(lastcommand, flag);
427
428 if (rc <= 0) {
429 /* invalid command or not repeatable, forget it */
d684c216
L
430 free(lastcommand);
431 lastcommand = NULL;
d684c216
L
432 }
433 }
434}