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.1 2006/06/01 12:27:39 marco
33 *#* Added utilities for protocols
40 #include <stdlib.h> // atol(), NULL
41 #include <string.h> // strchr(), strcmp()
43 #include <mware/hashtable.h>
48 #define MAX_COMMANDS_NUMBER 128 // 64
50 //! Hashtable hook to extract the key from a command
51 static const void* get_key_from_command(const void* cmd, uint8_t* length);
53 //! Hashtable that handles the commands that can be executed
54 DECLARE_HASHTABLE_STATIC(commands, MAX_COMMANDS_NUMBER, get_key_from_command);
58 * \brief Tokenize one word at a time from a text.
60 * This function is similar to strtok, but does not use any implicit
61 * context, nor it does modify the input buffer in any form.
62 * The word is returned as a STL-like [begin,end) range.
64 * To extract the first word, make both begin and end point at the
65 * start of the text, and call the function. Then, subsequent
66 * calls will return the following words (assuming the begin/end
67 * variable are not modified between calls).
69 * \param begin Will contain the index of the first character of the word
70 * \param end Will contain the index of the character after the last
71 * character of the word
73 * \return True if a word was extracted, false if we got to the end
74 * of the string without extracting any word.
76 static bool get_word(const char **begin, const char **end)
78 const char *cur = *end;
80 while ((*cur == ' ' || *cur == '\t') && *cur)
85 while ((*cur != ' ' && *cur != '\t') && *cur)
90 return (*end != *begin);
95 * \brief Command arguments parser.
97 * Using the format pointed by the argument fmt
98 * parses the input string filling the array argv
99 * with input parameters of the correct type.
101 * \param fmt Parameters format string.
102 * \param input Input string.
103 * \param argv Array filled with parameters.
105 * \return False in case of errors, otherwise true.
107 static bool parseArgs(const char *fmt, const char *input, parms argv[])
109 const char *begin = input, *end = input;
113 // Extract the argument
114 if (!get_word(&begin, &end))
120 (*argv++).l = atol(begin);
128 ASSERT2(0, "Unknown format for argument");
135 /* check if there are remaining args */
136 if (get_word(&begin, &end))
145 * \brief Command result formatting and printing.
147 * Prints out on device fd the values contained
148 * in the array result, using the format specified
151 * \param ser Serial handle.
152 * \param fmt Values format string.
153 * \param result Array containing result to be printed.
155 * \return -1 in case of errors, otherwise 0.
157 static int printResult(struct Serial *ser, const char *fmt, parms result[])
164 if (*fmt >= '0' && *fmt <= '9')
166 /* Collect repeat count digit (left to right order) */
167 repeat_cnt = (repeat_cnt * 10) + (*fmt - '0');
171 /* Set default repeat cnt of 1 when not specified */
175 /* Loop repeat_cnt times */
181 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
185 ser_print(ser, ARG_SEP_S);
186 ser_print(ser, (*result).s);
190 ser_printf(ser, ARG_SEP_S "%s", (*result).s);
195 ser_printf(ser, ARG_SEP_S "%ld", n);
197 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
205 while (--repeat_cnt);
208 /* Skip to next format char */
214 ser_print(ser, "\r\n");
217 #endif /* UNUSED_CODE */
219 //! Hook provided by the parser for matching of command names (TAB completion) for readline
220 const char* parser_rl_match(UNUSED_ARG(void *,dummy), const char *word, int word_len)
223 HashIterator end = ht_iter_end(&commands);
224 const char *found = NULL;
226 for (cur = ht_iter_begin(&commands);
227 !ht_iter_cmp(cur, end);
228 cur = ht_iter_next(cur))
230 const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
231 if (strncmp(cmdp->name, word, word_len) == 0)
233 // If there was another matching word, it means that we have a multiple
234 // match: then return NULL.
245 bool parser_get_cmd_id(const char* line, unsigned long* ID)
247 const char *begin = line, *end = line;
250 // The first word is the ID
251 if (!get_word(&begin, &end))
254 *ID = strtoul(begin, &end2, 10);
261 const struct CmdTemplate* parser_get_cmd_template(const char *input)
263 // const struct CmdTemplate *cmdp;
265 const char *begin = input, *end = input;
267 // Skip the ID, and get the command
268 if (!get_word(&begin, &end))
270 if (!get_word(&begin, &end))
273 return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
276 static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
278 const char *begin = input, *end = input;
280 // Skip the ID, and get the command
281 if (!get_word(&begin, &end))
283 if (!get_word(&begin, &end))
286 ASSERT2(strlen(cmdp->name) == (size_t)(end-begin), "Invalid command template specified");
287 ASSERT2(!strncmp(begin, cmdp->name, end-begin), "Invalid command template specified");
292 bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[PARSER_MAX_ARGS])
294 input = skip_to_params(input, cmdp);
298 args[0].s = cmdp->name;
299 if (!parseArgs(cmdp->arg_fmt, input, args + 1))
305 static const void* get_key_from_command(const void* cmd, uint8_t* length)
307 const struct CmdTemplate* c = cmd;
308 *length = strlen(c->name);
312 bool parser_process_line(const char* input)
314 const struct CmdTemplate *cmdp;
315 parms args[PARSER_MAX_ARGS];
317 cmdp = parser_get_cmd_template(input);
321 if (!parser_get_cmd_arguments(input, cmdp, args))
324 if (!parser_execute_cmd(cmdp, args))
330 void parser_register_cmd(const struct CmdTemplate* cmd)
332 ht_insert(&commands, cmd);
335 #if CONFIG_INTERNAL_COMMANDS
336 static ResultCode cmd_help(void)
340 // FIXME: There is no way at the moment to access the serial port. Dump
341 // this through JTAG for now
342 for (HashIterator iter = ht_iter_begin(&commands);
343 !ht_iter_cmp(iter, ht_iter_end(&commands));
344 iter = ht_iter_next(iter))
346 struct CmdTemplate* cmd = (struct CmdTemplate*)ht_iter_get(iter);
347 kprintf("%-20s", cmd->name);
348 for (unsigned j = 0; cmd->arg_fmt[j]; ++j)
349 kprintf("%c ", 'a' + j);
357 #include "cmd_hunk.h"
358 DECLARE_CMD_HUNK(help, (NIL), (NIL));
360 #endif // CONFIG_INTERNAL_COMMANDS
363 void parser_init(void)
365 // Initialize the hashtable used to store the command description
368 #if CONFIG_INTERNAL_COMMANDS
369 parser_register_cmd(&CMD_HUNK_TEMPLATE(help));