]>
cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cli.c
13 #include "cli_readline.h"
14 #include "con-utils.h"
17 #define DEBUG_PARSER 0 /* set to 1 to debug */
19 #define debug_parser(fmt, args...) \
20 debug_cond(DEBUG_PARSER, fmt, ##args)
22 static int cli_parse_line(char *line
, char *argv
[])
24 static const FLASH
char delim
[] = {" \t"};
27 uint_fast8_t nargs
= 0;
29 debug_parser("parse_line: \"%s\"\n", line
);
31 ptr
= strtok_P(line
, delim
);
32 while(nargs
< CONFIG_SYS_MAXARGS
&& ptr
!= NULL
) {
34 ptr
= strtok_P(NULL
, delim
);
38 printf_P(PSTR("** Too many args (max. %d) **\n"), CONFIG_SYS_MAXARGS
);
41 debug_parser("parse_line: nargs=%d\n", nargs
);
47 void append_char(uint_fast8_t pass
, char **p
, char c
)
57 char *process_macros(char *input
, char *output
)
59 char c
, prev
, *inp
, *outp
;
60 const char *varname
= NULL
;
62 for(uint_fast8_t pass
= 0; pass
< 2; pass
++)
64 uint_fast8_t state
= 0; /* 0 = waiting for '$' */
65 /* 1 = waiting for '{' */
66 /* 2 = waiting for '}' */
67 /* 3 = waiting for ''' */
72 int outputlen
= outp
- output
;
73 outp
= xrealloc(output
, outputlen
);
78 prev
= '\0'; /* previous character */
80 debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(inp
),
83 while ((c
= *inp
++) != '\0') {
86 /* remove one level of escape characters */
87 if ((c
== '\\') && (prev
!= '\\')) {
96 case 0: /* Waiting for (unescaped) $ */
97 if ((c
== '\'') && (prev
!= '\\')) {
101 if ((c
== '$') && (prev
!= '\\'))
104 append_char(pass
, &outp
, c
);
106 case 1: /* Waiting for { */
112 append_char(pass
, &outp
, '$');
113 append_char(pass
, &outp
, c
);
116 case 2: /* Waiting for } */
118 /* Terminate variable name */
120 const char *envval
= getenv(varname
);
122 /* Copy into the line if it exists */
125 append_char(pass
, &outp
, *(envval
++));
126 /* Look for another '$' */
130 case 3: /* Waiting for ' */
131 if ((c
== '\'') && (prev
!= '\\'))
134 append_char(pass
, &outp
, c
);
140 append_char(pass
, &outp
, 0);
143 debug_parser("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
144 strlen(output
), output
);
154 * We must create a temporary copy of the command since the command we get
155 * may be the result from getenv(), which returns a pointer directly to
156 * the environment data, which may change magicly when the command we run
157 * creates or modifies environment variables (like "bootp" does).
165 static int cli_run_command(const char *cmd
, int flag
)
167 char *cmdbuf
; /* working copy of cmd */
168 char *token
; /* start of token in cmdbuf */
169 char *sep
; /* end of token (separator) in cmdbuf */
170 char *finaltoken
= NULL
; /* token after macro expansion */
172 char *argv
[CONFIG_SYS_MAXARGS
+ 1]; /* NULL terminated */
174 uint_fast8_t inquotes
, repeatable
= 1;
177 debug_parser("[RUN_COMMAND] cmd[%p]=\"%s\"\n",
178 cmd
, cmd
? cmd
: "NULL");
180 clear_ctrlc(); /* forget any previous Control C */
183 return -1; /* empty command */
185 cmdbuf
= strdup(cmd
);
187 return -1; /* not enough memory */
191 /* Process separators and check for invalid
192 * repeatable commands
195 debug_parser("[PROCESS_SEPARATORS] %s\n", cmd
);
198 * Find separator, or string end
199 * Allow simple escape of ';' by writing "\;"
201 for (inquotes
= 0, sep
= str
; *sep
; sep
++) {
202 if ((*sep
== '\'') &&
203 (*(sep
- 1) != '\\'))
204 inquotes
= !inquotes
;
207 (*sep
== ';' || *sep
== '\n') && /* separator */
208 (sep
!= str
) && /* past string start */
209 (*(sep
- 1) != '\\')) /* and NOT escaped */
214 * Limit the token to data between separators
218 str
= sep
+ 1; /* start of command for next pass */
221 str
= sep
; /* no more commands for next pass */
223 debug_parser("token: \"%s\"\n", token
);
225 /* find macros in this token and replace them */
226 finaltoken
= process_macros(token
, finaltoken
);
228 /* Extract arguments */
229 argc
= cli_parse_line(finaltoken
, argv
);
231 rc
= -1; /* no command at all */
235 if (cmd_process(flag
, argc
, argv
, &repeatable
) != CMD_RET_SUCCESS
)
238 /* Did the user stop this? */
240 rc
= -1; /* if stopped then not repeatable */
248 return rc
? rc
: repeatable
;
251 static int cli_run_command_list(const char *cmd
)
253 return (cli_run_command(cmd
, 0) < 0);
256 /******************************************************************************/
260 * Run a command using the selected parser.
262 * @param cmd Command to run
263 * @param flag Execution flags (CMD_FLAG_...)
264 * @return 0 on success, or != 0 on error.
266 int run_command(const char *cmd
, int flag
)
269 * cli_run_command can return 0 or 1 for success, so clean up
272 if (cli_run_command(cmd
, flag
) == -1)
279 * Run a command using the selected parser, and check if it is repeatable.
281 * @param cmd Command to run
282 * @param flag Execution flags (CMD_FLAG_...)
283 * @return 0 (not repeatable) or 1 (repeatable) on success, -1 on error.
285 static int run_command_repeatable(const char *cmd
, int flag
)
287 return cli_run_command(cmd
, flag
);
290 int run_command_list(const char *cmd
, int len
)
294 return cli_run_command_list(cmd
);
297 /****************************************************************************/
302 char *lastcommand
= NULL
;
308 len
= cli_readline(PSTR(CONFIG_SYS_PROMPT
));
310 flag
= 0; /* assume no special flags for now */
313 lastcommand
= strdup(console_buffer
);
315 flag
|= CMD_FLAG_REPEAT
;
318 my_puts_P(PSTR("<INTERRUPT>\n"));
320 rc
= run_command_repeatable(lastcommand
, flag
);
323 /* invalid command or not repeatable, forget it */
331 command_ret_t
do_run(cmd_tbl_t
*cmdtp
, int flag
, int argc
, char * const argv
[])
337 return CMD_RET_USAGE
;
339 for (i
= 1; i
< argc
; ++i
) {
342 arg
= getenv(argv
[i
]);
344 printf_P(PSTR("## Error: \"%s\" not defined\n"), argv
[i
]);
345 return CMD_RET_FAILURE
;
348 if (run_command(arg
, flag
) != 0)
349 return CMD_RET_FAILURE
;
351 return CMD_RET_SUCCESS
;