4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2003, 2006 Develer S.r.l. (http://www.develer.com/)
30 * All Rights Reserved.
35 * \author Bernardo Innocenti <bernie@develer.com>
36 * \author Stefano Fedrigo <aleph@develer.com>
37 * \author Giovanni Bajo <rasky@develer.com>
39 * \brief Serial protocol parser and commands.
41 * This file contains the serial protocol parser and
42 * the definition of the protocol commands. Commands are defined
43 * in a "CmdTemplate" type array, containing:
44 * - the name of the command,
45 * - the arguments it expects to receive,
46 * - the output values,
47 * - the name of the function implementing the command.
49 * The arguments and results are passed to command function
50 * using an union: the element of the union to use for each
51 * argument is determined by format strings present in the
57 *#* Revision 1.2 2006/07/19 12:56:28 bernie
58 *#* Convert to new Doxygen style.
60 *#* Revision 1.1 2006/06/01 12:27:39 marco
61 *#* Added utilities for protocols
68 #include <stdlib.h> // atol(), NULL
69 #include <string.h> // strchr(), strcmp()
71 #include <mware/hashtable.h>
76 #define MAX_COMMANDS_NUMBER 128 // 64
78 /// Hashtable hook to extract the key from a command
79 static const void* get_key_from_command(const void* cmd, uint8_t* length);
81 /// Hashtable that handles the commands that can be executed
82 DECLARE_HASHTABLE_STATIC(commands, MAX_COMMANDS_NUMBER, get_key_from_command);
86 * \brief Tokenize one word at a time from a text.
88 * This function is similar to strtok, but does not use any implicit
89 * context, nor it does modify the input buffer in any form.
90 * The word is returned as a STL-like [begin,end) range.
92 * To extract the first word, make both begin and end point at the
93 * start of the text, and call the function. Then, subsequent
94 * calls will return the following words (assuming the begin/end
95 * variable are not modified between calls).
97 * \param begin Will contain the index of the first character of the word
98 * \param end Will contain the index of the character after the last
99 * character of the word
101 * \return True if a word was extracted, false if we got to the end
102 * of the string without extracting any word.
104 static bool get_word(const char **begin, const char **end)
106 const char *cur = *end;
108 while ((*cur == ' ' || *cur == '\t') && *cur)
113 while ((*cur != ' ' && *cur != '\t') && *cur)
118 return (*end != *begin);
123 * \brief Command arguments parser.
125 * Using the format pointed by the argument fmt
126 * parses the input string filling the array argv
127 * with input parameters of the correct type.
129 * \param fmt Parameters format string.
130 * \param input Input string.
131 * \param argv Array filled with parameters.
133 * \return False in case of errors, otherwise true.
135 static bool parseArgs(const char *fmt, const char *input, parms argv[])
137 const char *begin = input, *end = input;
141 // Extract the argument
142 if (!get_word(&begin, &end))
148 (*argv++).l = atol(begin);
156 ASSERT2(0, "Unknown format for argument");
163 /* check if there are remaining args */
164 if (get_word(&begin, &end))
173 * \brief Command result formatting and printing.
175 * Prints out on device fd the values contained
176 * in the array result, using the format specified
179 * \param ser Serial handle.
180 * \param fmt Values format string.
181 * \param result Array containing result to be printed.
183 * \return -1 in case of errors, otherwise 0.
185 static int printResult(struct Serial *ser, const char *fmt, parms result[])
192 if (*fmt >= '0' && *fmt <= '9')
194 /* Collect repeat count digit (left to right order) */
195 repeat_cnt = (repeat_cnt * 10) + (*fmt - '0');
199 /* Set default repeat cnt of 1 when not specified */
203 /* Loop repeat_cnt times */
209 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
213 ser_print(ser, ARG_SEP_S);
214 ser_print(ser, (*result).s);
218 ser_printf(ser, ARG_SEP_S "%s", (*result).s);
223 ser_printf(ser, ARG_SEP_S "%ld", n);
225 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
233 while (--repeat_cnt);
236 /* Skip to next format char */
242 ser_print(ser, "\r\n");
245 #endif /* UNUSED_CODE */
247 /// Hook provided by the parser for matching of command names (TAB completion) for readline
248 const char* parser_rl_match(UNUSED_ARG(void *,dummy), const char *word, int word_len)
251 HashIterator end = ht_iter_end(&commands);
252 const char *found = NULL;
254 for (cur = ht_iter_begin(&commands);
255 !ht_iter_cmp(cur, end);
256 cur = ht_iter_next(cur))
258 const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
259 if (strncmp(cmdp->name, word, word_len) == 0)
261 // If there was another matching word, it means that we have a multiple
262 // match: then return NULL.
273 bool parser_get_cmd_id(const char* line, unsigned long* ID)
275 const char *begin = line, *end = line;
278 // The first word is the ID
279 if (!get_word(&begin, &end))
282 *ID = strtoul(begin, &end2, 10);
289 const struct CmdTemplate* parser_get_cmd_template(const char *input)
291 // const struct CmdTemplate *cmdp;
293 const char *begin = input, *end = input;
295 // Skip the ID, and get the command
296 if (!get_word(&begin, &end))
298 if (!get_word(&begin, &end))
301 return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
304 static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
306 const char *begin = input, *end = input;
308 // Skip the ID, and get the command
309 if (!get_word(&begin, &end))
311 if (!get_word(&begin, &end))
314 ASSERT2(strlen(cmdp->name) == (size_t)(end-begin), "Invalid command template specified");
315 ASSERT2(!strncmp(begin, cmdp->name, end-begin), "Invalid command template specified");
320 bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[PARSER_MAX_ARGS])
322 input = skip_to_params(input, cmdp);
326 args[0].s = cmdp->name;
327 if (!parseArgs(cmdp->arg_fmt, input, args + 1))
333 static const void* get_key_from_command(const void* cmd, uint8_t* length)
335 const struct CmdTemplate* c = cmd;
336 *length = strlen(c->name);
340 bool parser_process_line(const char* input)
342 const struct CmdTemplate *cmdp;
343 parms args[PARSER_MAX_ARGS];
345 cmdp = parser_get_cmd_template(input);
349 if (!parser_get_cmd_arguments(input, cmdp, args))
352 if (!parser_execute_cmd(cmdp, args))
358 void parser_register_cmd(const struct CmdTemplate* cmd)
360 ht_insert(&commands, cmd);
363 #if CONFIG_INTERNAL_COMMANDS
364 static ResultCode cmd_help(void)
368 // FIXME: There is no way at the moment to access the serial port. Dump
369 // this through JTAG for now
370 for (HashIterator iter = ht_iter_begin(&commands);
371 !ht_iter_cmp(iter, ht_iter_end(&commands));
372 iter = ht_iter_next(iter))
374 struct CmdTemplate* cmd = (struct CmdTemplate*)ht_iter_get(iter);
375 kprintf("%-20s", cmd->name);
376 for (unsigned j = 0; cmd->arg_fmt[j]; ++j)
377 kprintf("%c ", 'a' + j);
385 #include "cmd_hunk.h"
386 DECLARE_CMD_HUNK(help, (NIL), (NIL));
388 #endif // CONFIG_INTERNAL_COMMANDS
391 void parser_init(void)
393 // Initialize the hashtable used to store the command description
396 #if CONFIG_INTERNAL_COMMANDS
397 parser_register_cmd(&CMD_HUNK_TEMPLATE(help));