Fix warning with gcc 4.6.1 (and also nightly test).
[bertos.git] / bertos / mware / parser.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
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.
10  *
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.
15  *
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
19  *
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.
28  *
29  * Copyright 2003, 2006 Develer S.r.l. (http://www.develer.com/)
30  * All Rights Reserved.
31  * -->
32  *
33  * \brief Channel protocol parser and commands.
34  *
35  * This file contains the channel protocol parser and
36  * the definition of the protocol commands. Commands are defined
37  * in a "CmdTemplate" type array, containing:
38  * - the name of the command,
39  * - the arguments it expects to receive,
40  * - the output values,
41  * - the name of the function implementing the command.
42  *
43  * The arguments and results are passed to command function
44  * using an union: the element of the union to use for each
45  * argument is determined by format strings present in the
46  * CmdTemplate table.
47  *
48  * \author Bernie Innocenti <bernie@codewiz.org>
49  * \author Stefano Fedrigo <aleph@develer.com>
50  * \author Giovanni Bajo <rasky@develer.com>
51  *
52  *
53  */
54
55
56 #include "parser.h"
57
58 #include "cfg/cfg_parser.h"
59
60 #include <io/kfile.h>
61 #include <struct/hashtable.h>
62
63 #include <stdlib.h> // atol(), NULL
64 #include <string.h> // strchr(), strcmp()
65
66 /// Hashtable hook to extract the key from a command
67 static const void* get_key_from_command(const void* cmd, uint8_t* length);
68
69 /// Hashtable that handles the commands that can be executed
70 DECLARE_HASHTABLE_STATIC(commands, CONFIG_MAX_COMMANDS_NUMBER, get_key_from_command);
71
72
73 /**
74  * \brief Tokenize one word at a time from a text.
75  *
76  * This function is similar to strtok, but does not use any implicit
77  * context, nor it does modify the input buffer in any form.
78  * The word is returned as a STL-like [begin,end) range.
79  *
80  * To extract the first word, make both begin and end point at the
81  * start of the text, and call the function. Then, subsequent
82  * calls will return the following words (assuming the begin/end
83  * variable are not modified between calls).
84  *
85  * \param begin Will contain the index of the first character of the word
86  * \param end Will contain the index of the character after the last
87  *     character of the word
88  *
89  * \return True if a word was extracted, false if we got to the end
90  * of the string without extracting any word.
91  */
92 static bool get_word(const char **begin, const char **end)
93 {
94         const char *cur = *end;
95
96         while ((*cur == ' ' || *cur == '\t') && *cur)
97                 ++cur;
98
99         *begin = cur;
100
101         while ((*cur != ' ' && *cur != '\t') && *cur)
102                 ++cur;
103
104         *end = cur;
105
106         return (*end != *begin);
107 }
108
109
110 /**
111  * \brief Command arguments parser.
112  *
113  * Using the format pointed by the argument fmt
114  * parses the input string filling the array argv
115  * with input parameters of the correct type.
116  *
117  * \param fmt   Parameters format string.
118  * \param input Input string.
119  * \param argv  Array filled with parameters.
120  *
121  * \return False in case of errors, otherwise true.
122  */
123 static bool parseArgs(const char *fmt, const char *input, parms argv[])
124 {
125         const char *begin = input, *end = input;
126
127         while (*fmt)
128         {
129                 // Extract the argument
130                 if (!get_word(&begin, &end))
131                         return false;
132
133                 switch (*fmt)
134                 {
135                         case 'd':
136                                 (*argv++).l = atol(begin);
137                                 break;
138
139                         case 's':
140                                 (*argv++).s = begin;
141                                 break;
142
143                         default:
144                                 ASSERT2(0, "Unknown format for argument");
145                                 return false;
146                 }
147
148                 ++fmt;
149         }
150
151         /* check if there are remaining args */
152         if (get_word(&begin, &end))
153                 return false;
154
155         return true;
156 }
157
158 /// Hook provided by the parser for matching of command names (TAB completion) for readline
159 const char* parser_rl_match(UNUSED_ARG(void *,dummy), const char *word, int word_len)
160 {
161         HashIterator cur;
162         HashIterator end = ht_iter_end(&commands);
163         const char *found = NULL;
164
165         for (cur = ht_iter_begin(&commands);
166              !ht_iter_cmp(cur, end);
167              cur = ht_iter_next(cur))
168         {
169                 const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
170                 if (strncmp(cmdp->name, word, word_len) == 0)
171                 {
172                         // If there was another matching word, it means that we have a multiple
173                         //  match: then return NULL.
174                         if (found)
175                                 return NULL;
176
177                         found = cmdp->name;
178                 }
179         }
180
181         return found;
182 }
183
184 #if CONFIG_ENABLE_COMPAT_BEHAVIOUR
185 bool parser_get_cmd_id(const char* line, unsigned long* ID)
186 {
187         const char *begin = line, *end = line;
188         char *end2;
189
190         // The first word is the ID
191         if (!get_word(&begin, &end))
192                 return false;
193
194         *ID = strtoul(begin, &end2, 10);
195         if (end2 != end)
196                 return false;
197
198         return true;
199 }
200 #endif
201
202 /**
203  * Find the template for the command contained in the text line.
204  * The template can be used to tokenize the command and interpret
205  * it.
206  *
207  * This function can be used to find out which command is contained
208  * in a given text line without parsing all the parameters and
209  * executing it.
210  *
211  * \param input Text line to be processed (ASCIIZ)
212  *
213  * \return The command template associated with the command contained
214  * in the line, or NULL if the command is invalid.
215  */
216 const struct CmdTemplate* parser_get_cmd_template(const char *input)
217 {
218         const char *begin = input, *end = input;
219
220 #if CONFIG_ENABLE_COMPAT_BEHAVIOUR
221         // Skip the ID, and get the command
222         if (!get_word(&begin, &end))
223                 return NULL;
224 #endif
225         if (!get_word(&begin, &end))
226                 return NULL;
227
228         return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
229 }
230
231 static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
232 {
233         const char *begin = input, *end = input;
234
235 #if CONFIG_ENABLE_COMPAT_BEHAVIOUR
236         // Skip the ID, and get the command
237         if (!get_word(&begin, &end))
238                 return NULL;
239 #endif
240         if (!get_word(&begin, &end))
241                 return NULL;
242
243         ASSERT2(strlen(cmdp->name) == (size_t)(end-begin), "Invalid command template specified");
244         ASSERT2(!strncmp(begin, cmdp->name, end-begin), "Invalid command template specified");
245
246         return end;
247 }
248
249 /**
250  * Extract the arguments for the command contained in the text line.
251  *
252  * The first argument will always be the command name, so the actual arguments
253  * will start at index 1.
254  *
255  * \param input Text line to be processed (ASCIIZ)
256  * \param cmdp Command template for this line
257  * \param args Will contain the extracted parameters
258  *
259  * \return True if everything OK, false in case of parsing error.
260  */
261 bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[CONFIG_PARSER_MAX_ARGS])
262 {
263         input = skip_to_params(input, cmdp);
264         if (!input)
265                 return false;
266
267         args[0].s = cmdp->name;
268         if (!parseArgs(cmdp->arg_fmt, input, args + 1))
269                 return false;
270
271         return true;
272 }
273
274 static const void* get_key_from_command(const void* cmd, uint8_t* length)
275 {
276         const struct CmdTemplate* c = cmd;
277         *length = strlen(c->name);
278         return c->name;
279 }
280
281 /**
282  * \brief Command input handler.
283  *
284  * Process the input, calling the requested command (if found).
285  *
286  * \param input Text line to be processed (ASCIIZ)
287  *
288  * \return true if everything is OK, false in case of errors
289  */
290 bool parser_process_line(const char* input)
291 {
292         const struct CmdTemplate *cmdp;
293         parms args[CONFIG_PARSER_MAX_ARGS];
294
295         cmdp = parser_get_cmd_template(input);
296         if (!cmdp)
297                 return false;
298
299         if (!parser_get_cmd_arguments(input, cmdp, args))
300                 return false;
301
302         if (!parser_execute_cmd(cmdp, args))
303                 return false;
304
305         return true;
306 }
307
308 /**
309  * Register a new command into the parser
310  *
311  * \param cmd Command template describing the command
312  * \return true if registration was successful, false otherwise
313  */
314 bool parser_register_cmd(const struct CmdTemplate* cmd)
315 {
316         return ht_insert(&commands, cmd);
317 }
318
319 void parser_init(void)
320 {
321         // Initialize the hashtable used to store the command description
322         ht_init(&commands);
323 }