e63e55e870e158d74169451ac5bc024ee8fe0d02
[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 <io/kfile.h>
59 #include <struct/hashtable.h>
60
61 #include <stdlib.h> // atol(), NULL
62 #include <string.h> // strchr(), strcmp()
63
64 /// Hashtable hook to extract the key from a command
65 static const void* get_key_from_command(const void* cmd, uint8_t* length);
66
67 /// Hashtable that handles the commands that can be executed
68 DECLARE_HASHTABLE_STATIC(commands, MAX_COMMANDS_NUMBER, get_key_from_command);
69
70
71 /**
72  * \brief Tokenize one word at a time from a text.
73  *
74  * This function is similar to strtok, but does not use any implicit
75  * context, nor it does modify the input buffer in any form.
76  * The word is returned as a STL-like [begin,end) range.
77  *
78  * To extract the first word, make both begin and end point at the
79  * start of the text, and call the function. Then, subsequent
80  * calls will return the following words (assuming the begin/end
81  * variable are not modified between calls).
82  *
83  * \param begin Will contain the index of the first character of the word
84  * \param end Will contain the index of the character after the last
85  *     character of the word
86  *
87  * \return True if a word was extracted, false if we got to the end
88  * of the string without extracting any word.
89  */
90 static bool get_word(const char **begin, const char **end)
91 {
92         const char *cur = *end;
93
94         while ((*cur == ' ' || *cur == '\t') && *cur)
95                 ++cur;
96
97         *begin = cur;
98
99         while ((*cur != ' ' && *cur != '\t') && *cur)
100                 ++cur;
101
102         *end = cur;
103
104         return (*end != *begin);
105 }
106
107
108 /**
109  * \brief Command arguments parser.
110  *
111  * Using the format pointed by the argument fmt
112  * parses the input string filling the array argv
113  * with input parameters of the correct type.
114  *
115  * \param fmt   Parameters format string.
116  * \param input Input string.
117  * \param argv  Array filled with parameters.
118  *
119  * \return False in case of errors, otherwise true.
120  */
121 static bool parseArgs(const char *fmt, const char *input, parms argv[])
122 {
123         const char *begin = input, *end = input;
124
125         while (*fmt)
126         {
127                 // Extract the argument
128                 if (!get_word(&begin, &end))
129                         return false;
130
131                 switch (*fmt)
132                 {
133                         case 'd':
134                                 (*argv++).l = atol(begin);
135                                 break;
136
137                         case 's':
138                                 (*argv++).s = begin;
139                                 break;
140
141                         default:
142                                 ASSERT2(0, "Unknown format for argument");
143                                 return false;
144                 }
145
146                 ++fmt;
147         }
148
149         /* check if there are remaining args */
150         if (get_word(&begin, &end))
151                 return false;
152
153         return true;
154 }
155
156 /// Hook provided by the parser for matching of command names (TAB completion) for readline
157 const char* parser_rl_match(UNUSED_ARG(void *,dummy), const char *word, int word_len)
158 {
159         HashIterator cur;
160         HashIterator end = ht_iter_end(&commands);
161         const char *found = NULL;
162
163         for (cur = ht_iter_begin(&commands);
164              !ht_iter_cmp(cur, end);
165              cur = ht_iter_next(cur))
166         {
167                 const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
168                 if (strncmp(cmdp->name, word, word_len) == 0)
169                 {
170                         // If there was another matching word, it means that we have a multiple
171                         //  match: then return NULL.
172                         if (found)
173                                 return NULL;
174
175                         found = cmdp->name;
176                 }
177         }
178
179         return found;
180 }
181
182 #if CONFIG_ENABLE_COMPAT_BEHAVIOUR
183 bool parser_get_cmd_id(const char* line, unsigned long* ID)
184 {
185         const char *begin = line, *end = line;
186         char *end2;
187
188         // The first word is the ID
189         if (!get_word(&begin, &end))
190                 return false;
191
192         *ID = strtoul(begin, &end2, 10);
193         if (end2 != end)
194                 return false;
195
196         return true;
197 }
198 #endif
199
200 const struct CmdTemplate* parser_get_cmd_template(const char *input)
201 {
202         const char *begin = input, *end = input;
203
204 #if CONFIG_ENABLE_COMPAT_BEHAVIOUR
205         // Skip the ID, and get the command
206         if (!get_word(&begin, &end))
207                 return NULL;
208 #endif
209         if (!get_word(&begin, &end))
210                 return NULL;
211
212         return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
213 }
214
215 static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
216 {
217         const char *begin = input, *end = input;
218
219 #if CONFIG_ENABLE_COMPAT_BEHAVIOUR
220         // Skip the ID, and get the command
221         if (!get_word(&begin, &end))
222                 return NULL;
223 #endif
224         if (!get_word(&begin, &end))
225                 return NULL;
226
227         ASSERT2(strlen(cmdp->name) == (size_t)(end-begin), "Invalid command template specified");
228         ASSERT2(!strncmp(begin, cmdp->name, end-begin), "Invalid command template specified");
229
230         return end;
231 }
232
233 bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[PARSER_MAX_ARGS])
234 {
235         input = skip_to_params(input, cmdp);
236         if (!input)
237                 return false;
238
239         args[0].s = cmdp->name;
240         if (!parseArgs(cmdp->arg_fmt, input, args + 1))
241                 return false;
242
243         return true;
244 }
245
246 static const void* get_key_from_command(const void* cmd, uint8_t* length)
247 {
248         const struct CmdTemplate* c = cmd;
249         *length = strlen(c->name);
250         return c->name;
251 }
252
253 bool parser_process_line(const char* input)
254 {
255         const struct CmdTemplate *cmdp;
256         parms args[PARSER_MAX_ARGS];
257
258         cmdp = parser_get_cmd_template(input);
259         if (!cmdp)
260                 return false;
261
262         if (!parser_get_cmd_arguments(input, cmdp, args))
263                 return false;
264
265         if (!parser_execute_cmd(cmdp, args))
266                 return false;
267
268         return true;
269 }
270
271 void parser_register_cmd(const struct CmdTemplate* cmd)
272 {
273         ht_insert(&commands, cmd);
274 }
275
276 void parser_init(void)
277 {
278         // Initialize the hashtable used to store the command description
279         ht_init(&commands);
280 }