593bb5947a395a43e9b34d2e5520646bfc99181d
[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 Serial protocol parser and commands.
34  *
35  * This file contains the serial 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  * \version $Id$
49  *
50  * \author Bernardo Innocenti <bernie@develer.com>
51  * \author Stefano Fedrigo <aleph@develer.com>
52  * \author Giovanni Bajo <rasky@develer.com>
53  *
54  *
55  */
56
57
58 #include "parser.h"
59
60 #include "cfg/cfg_parser.h"
61
62 #include <drv/ser.h>
63 #include <mware/hashtable.h>
64
65 #include <stdlib.h> // atol(), NULL
66 #include <string.h> // strchr(), strcmp()
67
68
69
70 #define ARG_SEP_S " "
71 #define ARG_SEP_C ' '
72
73 #define MAX_COMMANDS_NUMBER  128  // 64
74
75 /// Hashtable hook to extract the key from a command
76 static const void* get_key_from_command(const void* cmd, uint8_t* length);
77
78 /// Hashtable that handles the commands that can be executed
79 DECLARE_HASHTABLE_STATIC(commands, MAX_COMMANDS_NUMBER, get_key_from_command);
80
81
82 /**
83  * \brief Tokenize one word at a time from a text.
84  *
85  * This function is similar to strtok, but does not use any implicit
86  * context, nor it does modify the input buffer in any form.
87  * The word is returned as a STL-like [begin,end) range.
88  *
89  * To extract the first word, make both begin and end point at the
90  * start of the text, and call the function. Then, subsequent
91  * calls will return the following words (assuming the begin/end
92  * variable are not modified between calls).
93  *
94  * \param begin Will contain the index of the first character of the word
95  * \param end Will contain the index of the character after the last
96  *     character of the word
97  *
98  * \return True if a word was extracted, false if we got to the end
99  * of the string without extracting any word.
100  */
101 static bool get_word(const char **begin, const char **end)
102 {
103         const char *cur = *end;
104
105         while ((*cur == ' ' || *cur == '\t') && *cur)
106                 ++cur;
107
108         *begin = cur;
109
110         while ((*cur != ' ' && *cur != '\t') && *cur)
111                 ++cur;
112
113         *end = cur;
114
115         return (*end != *begin);
116 }
117
118
119 /**
120  * \brief Command arguments parser.
121  *
122  * Using the format pointed by the argument fmt
123  * parses the input string filling the array argv
124  * with input parameters of the correct type.
125  *
126  * \param fmt   Parameters format string.
127  * \param input Input string.
128  * \param argv  Array filled with parameters.
129  *
130  * \return False in case of errors, otherwise true.
131  */
132 static bool parseArgs(const char *fmt, const char *input, parms argv[])
133 {
134         const char *begin = input, *end = input;
135
136         while (*fmt)
137         {
138                 // Extract the argument
139                 if (!get_word(&begin, &end))
140                         return false;
141
142                 switch (*fmt)
143                 {
144                         case 'd':
145                                 (*argv++).l = atol(begin);
146                                 break;
147
148                         case 's':
149                                 (*argv++).s = begin;
150                                 break;
151
152                         default:
153                                 ASSERT2(0, "Unknown format for argument");
154                                 return false;
155                 }
156
157                 ++fmt;
158         }
159
160         /* check if there are remaining args */
161         if (get_word(&begin, &end))
162                 return false;
163
164         return true;
165 }
166
167
168 #ifdef UNUSED_CODE
169 /**
170  * \brief Command result formatting and printing.
171  *
172  * Prints out on device fd the values contained
173  * in the array result, using the format specified
174  * in fmt.
175  *
176  * \param ser     Serial handle.
177  * \param fmt     Values format string.
178  * \param result  Array containing result to be printed.
179  *
180  * \return -1 in case of errors, otherwise 0.
181  */
182 static int printResult(struct Serial *ser, const char *fmt, parms result[])
183 {
184         long n;
185         char repeat_cnt = 0;
186
187         while (*fmt)
188         {
189                 if (*fmt >= '0' && *fmt <= '9')
190                 {
191                         /* Collect repeat count digit (left to right order) */
192                         repeat_cnt = (repeat_cnt * 10) + (*fmt - '0');
193                 }
194                 else
195                 {
196                         /* Set default repeat cnt of 1 when not specified */
197                         if (repeat_cnt == 0)
198                                 repeat_cnt = 1;
199
200                         /* Loop repeat_cnt times */
201                         do
202                         {
203                                 switch (*fmt)
204                                 {
205                                         case 'd':
206                                                 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
207                                                 result++;
208                                                 break;
209                                         case 'c':
210                                                 ser_print(ser, ARG_SEP_S);
211                                                 ser_print(ser, (*result).s);
212                                                 result++;
213                                                 break;
214                                         case 's':
215                                                 ser_printf(ser, ARG_SEP_S "%s", (*result).s);
216                                                 result++;
217                                                 break;
218                                         case 'n':
219                                                 n = (*result++).l;
220                                                 ser_printf(ser, ARG_SEP_S "%ld", n);
221                                                 while (n--) {
222                                                         ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
223                                                         result++;
224                                                 }
225                                                 break;
226                                         default:
227                                                 break;
228                                 }
229                         }
230                         while (--repeat_cnt);
231                 }
232
233                 /* Skip to next format char */
234                 ++fmt;
235
236         } /* while (*fmt) */
237
238
239         ser_print(ser, "\r\n");
240         return 0;
241 }
242 #endif /* UNUSED_CODE */
243
244 /// Hook provided by the parser for matching of command names (TAB completion) for readline
245 const char* parser_rl_match(UNUSED_ARG(void *,dummy), const char *word, int word_len)
246 {
247         HashIterator cur;
248         HashIterator end = ht_iter_end(&commands);
249         const char *found = NULL;
250
251         for (cur = ht_iter_begin(&commands);
252              !ht_iter_cmp(cur, end);
253              cur = ht_iter_next(cur))
254         {
255                 const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
256                 if (strncmp(cmdp->name, word, word_len) == 0)
257                 {
258                         // If there was another matching word, it means that we have a multiple
259                         //  match: then return NULL.
260                         if (found)
261                                 return NULL;
262
263                         found = cmdp->name;
264                 }
265         }
266
267         return found;
268 }
269
270 bool parser_get_cmd_id(const char* line, unsigned long* ID)
271 {
272         const char *begin = line, *end = line;
273         char *end2;
274
275         // The first word is the ID
276         if (!get_word(&begin, &end))
277                 return false;
278
279         *ID = strtoul(begin, &end2, 10);
280         if (end2 != end)
281                 return false;
282
283         return true;
284 }
285
286 const struct CmdTemplate* parser_get_cmd_template(const char *input)
287 {
288 //      const struct CmdTemplate *cmdp;
289 //      int cmdlen;
290         const char *begin = input, *end = input;
291
292         // Skip the ID, and get the command
293         if (!get_word(&begin, &end))
294                 return NULL;
295         if (!get_word(&begin, &end))
296                 return NULL;
297
298         return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
299 }
300
301 static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
302 {
303         const char *begin = input, *end = input;
304
305         // Skip the ID, and get the command
306         if (!get_word(&begin, &end))
307                 return NULL;
308         if (!get_word(&begin, &end))
309                 return NULL;
310
311         ASSERT2(strlen(cmdp->name) == (size_t)(end-begin), "Invalid command template specified");
312         ASSERT2(!strncmp(begin, cmdp->name, end-begin), "Invalid command template specified");
313
314         return end;
315 }
316
317 bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[PARSER_MAX_ARGS])
318 {
319         input = skip_to_params(input, cmdp);
320         if (!input)
321                 return false;
322
323         args[0].s = cmdp->name;
324         if (!parseArgs(cmdp->arg_fmt, input, args + 1))
325                 return false;
326
327         return true;
328 }
329
330 static const void* get_key_from_command(const void* cmd, uint8_t* length)
331 {
332         const struct CmdTemplate* c = cmd;
333         *length = strlen(c->name);
334         return c->name;
335 }
336
337 bool parser_process_line(const char* input)
338 {
339         const struct CmdTemplate *cmdp;
340         parms args[PARSER_MAX_ARGS];
341
342         cmdp = parser_get_cmd_template(input);
343         if (!cmdp)
344                 return false;
345
346         if (!parser_get_cmd_arguments(input, cmdp, args))
347                 return false;
348
349         if (!parser_execute_cmd(cmdp, args))
350                 return false;
351
352         return true;
353 }
354
355 void parser_register_cmd(const struct CmdTemplate* cmd)
356 {
357         ht_insert(&commands, cmd);
358 }
359
360 #if CONFIG_INTERNAL_COMMANDS
361 #warning FIXME:This code use boost lib, if you compile with internal command you must fix it.
362 static ResultCode cmd_help(void)
363 {
364 #ifdef _DEBUG
365
366         // FIXME: There is no way at the moment to access the serial port. Dump
367         //  this through JTAG for now
368         for (HashIterator iter = ht_iter_begin(&commands);
369                 !ht_iter_cmp(iter, ht_iter_end(&commands));
370                 iter = ht_iter_next(iter))
371         {
372                 struct CmdTemplate* cmd = (struct CmdTemplate*)ht_iter_get(iter);
373                 kprintf("%-20s", cmd->name);
374                 for (unsigned j = 0; cmd->arg_fmt[j]; ++j)
375                         kprintf("%c ", 'a' + j);
376                 kprintf("\r\n");
377         }
378 #endif
379
380         return RC_OK;
381 }
382
383 #include "cmd_hunk.h"
384 DECLARE_CMD_HUNK(help, (NIL), (NIL));
385
386 #endif // CONFIG_INTERNAL_COMMANDS
387
388
389 void parser_init(void)
390 {
391         // Initialize the hashtable used to store the command description
392         ht_init(&commands);
393
394 #if CONFIG_INTERNAL_COMMANDS
395         parser_register_cmd(&CMD_HUNK_TEMPLATE(help));
396 #endif
397 }