4 * Copyright 2003, 2006 Develer S.r.l. (http://www.develer.com/)
10 * \author Bernardo Innocenti <bernie@develer.com>
11 * \author Stefano Fedrigo <aleph@develer.com>
12 * \author Giovanni Bajo <rasky@develer.com>
14 * \brief Serial protocol parser and commands.
16 * This file contains the serial protocol parser and
17 * the definition of the protocol commands. Commands are defined
18 * in a "CmdTemplate" type array, containing:
19 * - the name of the command,
20 * - the arguments it expects to receive,
21 * - the output values,
22 * - the name of the function implementing the command.
24 * The arguments and results are passed to command function
25 * using an union: the element of the union to use for each
26 * argument is determined by format strings present in the
32 *#* Revision 1.2 2006/07/19 12:56:28 bernie
33 *#* Convert to new Doxygen style.
35 *#* Revision 1.1 2006/06/01 12:27:39 marco
36 *#* Added utilities for protocols
43 #include <stdlib.h> // atol(), NULL
44 #include <string.h> // strchr(), strcmp()
46 #include <mware/hashtable.h>
51 #define MAX_COMMANDS_NUMBER 128 // 64
53 /// Hashtable hook to extract the key from a command
54 static const void* get_key_from_command(const void* cmd, uint8_t* length);
56 /// Hashtable that handles the commands that can be executed
57 DECLARE_HASHTABLE_STATIC(commands, MAX_COMMANDS_NUMBER, get_key_from_command);
61 * \brief Tokenize one word at a time from a text.
63 * This function is similar to strtok, but does not use any implicit
64 * context, nor it does modify the input buffer in any form.
65 * The word is returned as a STL-like [begin,end) range.
67 * To extract the first word, make both begin and end point at the
68 * start of the text, and call the function. Then, subsequent
69 * calls will return the following words (assuming the begin/end
70 * variable are not modified between calls).
72 * \param begin Will contain the index of the first character of the word
73 * \param end Will contain the index of the character after the last
74 * character of the word
76 * \return True if a word was extracted, false if we got to the end
77 * of the string without extracting any word.
79 static bool get_word(const char **begin, const char **end)
81 const char *cur = *end;
83 while ((*cur == ' ' || *cur == '\t') && *cur)
88 while ((*cur != ' ' && *cur != '\t') && *cur)
93 return (*end != *begin);
98 * \brief Command arguments parser.
100 * Using the format pointed by the argument fmt
101 * parses the input string filling the array argv
102 * with input parameters of the correct type.
104 * \param fmt Parameters format string.
105 * \param input Input string.
106 * \param argv Array filled with parameters.
108 * \return False in case of errors, otherwise true.
110 static bool parseArgs(const char *fmt, const char *input, parms argv[])
112 const char *begin = input, *end = input;
116 // Extract the argument
117 if (!get_word(&begin, &end))
123 (*argv++).l = atol(begin);
131 ASSERT2(0, "Unknown format for argument");
138 /* check if there are remaining args */
139 if (get_word(&begin, &end))
148 * \brief Command result formatting and printing.
150 * Prints out on device fd the values contained
151 * in the array result, using the format specified
154 * \param ser Serial handle.
155 * \param fmt Values format string.
156 * \param result Array containing result to be printed.
158 * \return -1 in case of errors, otherwise 0.
160 static int printResult(struct Serial *ser, const char *fmt, parms result[])
167 if (*fmt >= '0' && *fmt <= '9')
169 /* Collect repeat count digit (left to right order) */
170 repeat_cnt = (repeat_cnt * 10) + (*fmt - '0');
174 /* Set default repeat cnt of 1 when not specified */
178 /* Loop repeat_cnt times */
184 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
188 ser_print(ser, ARG_SEP_S);
189 ser_print(ser, (*result).s);
193 ser_printf(ser, ARG_SEP_S "%s", (*result).s);
198 ser_printf(ser, ARG_SEP_S "%ld", n);
200 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
208 while (--repeat_cnt);
211 /* Skip to next format char */
217 ser_print(ser, "\r\n");
220 #endif /* UNUSED_CODE */
222 /// Hook provided by the parser for matching of command names (TAB completion) for readline
223 const char* parser_rl_match(UNUSED_ARG(void *,dummy), const char *word, int word_len)
226 HashIterator end = ht_iter_end(&commands);
227 const char *found = NULL;
229 for (cur = ht_iter_begin(&commands);
230 !ht_iter_cmp(cur, end);
231 cur = ht_iter_next(cur))
233 const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
234 if (strncmp(cmdp->name, word, word_len) == 0)
236 // If there was another matching word, it means that we have a multiple
237 // match: then return NULL.
248 bool parser_get_cmd_id(const char* line, unsigned long* ID)
250 const char *begin = line, *end = line;
253 // The first word is the ID
254 if (!get_word(&begin, &end))
257 *ID = strtoul(begin, &end2, 10);
264 const struct CmdTemplate* parser_get_cmd_template(const char *input)
266 // const struct CmdTemplate *cmdp;
268 const char *begin = input, *end = input;
270 // Skip the ID, and get the command
271 if (!get_word(&begin, &end))
273 if (!get_word(&begin, &end))
276 return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
279 static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
281 const char *begin = input, *end = input;
283 // Skip the ID, and get the command
284 if (!get_word(&begin, &end))
286 if (!get_word(&begin, &end))
289 ASSERT2(strlen(cmdp->name) == (size_t)(end-begin), "Invalid command template specified");
290 ASSERT2(!strncmp(begin, cmdp->name, end-begin), "Invalid command template specified");
295 bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[PARSER_MAX_ARGS])
297 input = skip_to_params(input, cmdp);
301 args[0].s = cmdp->name;
302 if (!parseArgs(cmdp->arg_fmt, input, args + 1))
308 static const void* get_key_from_command(const void* cmd, uint8_t* length)
310 const struct CmdTemplate* c = cmd;
311 *length = strlen(c->name);
315 bool parser_process_line(const char* input)
317 const struct CmdTemplate *cmdp;
318 parms args[PARSER_MAX_ARGS];
320 cmdp = parser_get_cmd_template(input);
324 if (!parser_get_cmd_arguments(input, cmdp, args))
327 if (!parser_execute_cmd(cmdp, args))
333 void parser_register_cmd(const struct CmdTemplate* cmd)
335 ht_insert(&commands, cmd);
338 #if CONFIG_INTERNAL_COMMANDS
339 static ResultCode cmd_help(void)
343 // FIXME: There is no way at the moment to access the serial port. Dump
344 // this through JTAG for now
345 for (HashIterator iter = ht_iter_begin(&commands);
346 !ht_iter_cmp(iter, ht_iter_end(&commands));
347 iter = ht_iter_next(iter))
349 struct CmdTemplate* cmd = (struct CmdTemplate*)ht_iter_get(iter);
350 kprintf("%-20s", cmd->name);
351 for (unsigned j = 0; cmd->arg_fmt[j]; ++j)
352 kprintf("%c ", 'a' + j);
360 #include "cmd_hunk.h"
361 DECLARE_CMD_HUNK(help, (NIL), (NIL));
363 #endif // CONFIG_INTERNAL_COMMANDS
366 void parser_init(void)
368 // Initialize the hashtable used to store the command description
371 #if CONFIG_INTERNAL_COMMANDS
372 parser_register_cmd(&CMD_HUNK_TEMPLATE(help));