Updated copiright notice.
[bertos.git] / 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  * \version $Id$
34  *
35  * \author Bernardo Innocenti <bernie@develer.com>
36  * \author Stefano Fedrigo <aleph@develer.com>
37  * \author Giovanni Bajo <rasky@develer.com>
38  *
39  * \brief Serial protocol parser and commands.
40  *
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.
48  *
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
52  * CmdTemplate table.
53  */
54
55 /*#*
56  *#* $Log$
57  *#* Revision 1.2  2006/07/19 12:56:28  bernie
58  *#* Convert to new Doxygen style.
59  *#*
60  *#* Revision 1.1  2006/06/01 12:27:39  marco
61  *#* Added utilities for protocols
62  *#*
63  *#*/
64
65 #include "parser.h"
66 #include <drv/ser.h>
67
68 #include <stdlib.h> // atol(), NULL
69 #include <string.h> // strchr(), strcmp()
70
71 #include <mware/hashtable.h>
72
73 #define ARG_SEP_S " "
74 #define ARG_SEP_C ' '
75
76 #define MAX_COMMANDS_NUMBER  128  // 64
77
78 /// Hashtable hook to extract the key from a command
79 static const void* get_key_from_command(const void* cmd, uint8_t* length);
80
81 /// Hashtable that handles the commands that can be executed
82 DECLARE_HASHTABLE_STATIC(commands, MAX_COMMANDS_NUMBER, get_key_from_command);
83
84
85 /**
86  * \brief Tokenize one word at a time from a text.
87  *
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.
91  *
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).
96  *
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
100  *
101  * \return True if a word was extracted, false if we got to the end
102  * of the string without extracting any word.
103  */
104 static bool get_word(const char **begin, const char **end)
105 {
106         const char *cur = *end;
107
108         while ((*cur == ' ' || *cur == '\t') && *cur)
109                 ++cur;
110
111         *begin = cur;
112
113         while ((*cur != ' ' && *cur != '\t') && *cur)
114                 ++cur;
115
116         *end = cur;
117
118         return (*end != *begin);
119 }
120
121
122 /**
123  * \brief Command arguments parser.
124  *
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.
128  *
129  * \param fmt   Parameters format string.
130  * \param input Input string.
131  * \param argv  Array filled with parameters.
132  *
133  * \return False in case of errors, otherwise true.
134  */
135 static bool parseArgs(const char *fmt, const char *input, parms argv[])
136 {
137         const char *begin = input, *end = input;
138
139         while (*fmt)
140         {
141                 // Extract the argument
142                 if (!get_word(&begin, &end))
143                         return false;
144
145                 switch (*fmt)
146                 {
147                         case 'd':
148                                 (*argv++).l = atol(begin);
149                                 break;
150
151                         case 's':
152                                 (*argv++).s = begin;
153                                 break;
154
155                         default:
156                                 ASSERT2(0, "Unknown format for argument");
157                                 return false;
158                 }
159
160                 ++fmt;
161         }
162
163         /* check if there are remaining args */
164         if (get_word(&begin, &end))
165                 return false;
166
167         return true;
168 }
169
170
171 #ifdef UNUSED_CODE
172 /**
173  * \brief Command result formatting and printing.
174  *
175  * Prints out on device fd the values contained
176  * in the array result, using the format specified
177  * in fmt.
178  *
179  * \param ser     Serial handle.
180  * \param fmt     Values format string.
181  * \param result  Array containing result to be printed.
182  *
183  * \return -1 in case of errors, otherwise 0.
184  */
185 static int printResult(struct Serial *ser, const char *fmt, parms result[])
186 {
187         long n;
188         char repeat_cnt = 0;
189
190         while (*fmt)
191         {
192                 if (*fmt >= '0' && *fmt <= '9')
193                 {
194                         /* Collect repeat count digit (left to right order) */
195                         repeat_cnt = (repeat_cnt * 10) + (*fmt - '0');
196                 }
197                 else
198                 {
199                         /* Set default repeat cnt of 1 when not specified */
200                         if (repeat_cnt == 0)
201                                 repeat_cnt = 1;
202
203                         /* Loop repeat_cnt times */
204                         do
205                         {
206                                 switch (*fmt)
207                                 {
208                                         case 'd':
209                                                 ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
210                                                 result++;
211                                                 break;
212                                         case 'c':
213                                                 ser_print(ser, ARG_SEP_S);
214                                                 ser_print(ser, (*result).s);
215                                                 result++;
216                                                 break;
217                                         case 's':
218                                                 ser_printf(ser, ARG_SEP_S "%s", (*result).s);
219                                                 result++;
220                                                 break;
221                                         case 'n':
222                                                 n = (*result++).l;
223                                                 ser_printf(ser, ARG_SEP_S "%ld", n);
224                                                 while (n--) {
225                                                         ser_printf(ser, ARG_SEP_S "%ld", (*result).l);
226                                                         result++;
227                                                 }
228                                                 break;
229                                         default:
230                                                 break;
231                                 }
232                         }
233                         while (--repeat_cnt);
234                 }
235
236                 /* Skip to next format char */
237                 ++fmt;
238
239         } /* while (*fmt) */
240
241
242         ser_print(ser, "\r\n");
243         return 0;
244 }
245 #endif /* UNUSED_CODE */
246
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)
249 {
250         HashIterator cur;
251         HashIterator end = ht_iter_end(&commands);
252         const char *found = NULL;
253
254         for (cur = ht_iter_begin(&commands);
255              !ht_iter_cmp(cur, end);
256              cur = ht_iter_next(cur))
257         {
258                 const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
259                 if (strncmp(cmdp->name, word, word_len) == 0)
260                 {
261                         // If there was another matching word, it means that we have a multiple
262                         //  match: then return NULL.
263                         if (found)
264                                 return NULL;
265
266                         found = cmdp->name;
267                 }
268         }
269
270         return found;
271 }
272
273 bool parser_get_cmd_id(const char* line, unsigned long* ID)
274 {
275         const char *begin = line, *end = line;
276         char *end2;
277
278         // The first word is the ID
279         if (!get_word(&begin, &end))
280                 return false;
281
282         *ID = strtoul(begin, &end2, 10);
283         if (end2 != end)
284                 return false;
285
286         return true;
287 }
288
289 const struct CmdTemplate* parser_get_cmd_template(const char *input)
290 {
291 //      const struct CmdTemplate *cmdp;
292 //      int cmdlen;
293         const char *begin = input, *end = input;
294
295         // Skip the ID, and get the command
296         if (!get_word(&begin, &end))
297                 return NULL;
298         if (!get_word(&begin, &end))
299                 return NULL;
300
301         return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
302 }
303
304 static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
305 {
306         const char *begin = input, *end = input;
307
308         // Skip the ID, and get the command
309         if (!get_word(&begin, &end))
310                 return NULL;
311         if (!get_word(&begin, &end))
312                 return NULL;
313
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");
316
317         return end;
318 }
319
320 bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[PARSER_MAX_ARGS])
321 {
322         input = skip_to_params(input, cmdp);
323         if (!input)
324                 return false;
325
326         args[0].s = cmdp->name;
327         if (!parseArgs(cmdp->arg_fmt, input, args + 1))
328                 return false;
329
330         return true;
331 }
332
333 static const void* get_key_from_command(const void* cmd, uint8_t* length)
334 {
335         const struct CmdTemplate* c = cmd;
336         *length = strlen(c->name);
337         return c->name;
338 }
339
340 bool parser_process_line(const char* input)
341 {
342         const struct CmdTemplate *cmdp;
343         parms args[PARSER_MAX_ARGS];
344
345         cmdp = parser_get_cmd_template(input);
346         if (!cmdp)
347                 return false;
348
349         if (!parser_get_cmd_arguments(input, cmdp, args))
350                 return false;
351
352         if (!parser_execute_cmd(cmdp, args))
353                 return false;
354
355         return true;
356 }
357
358 void parser_register_cmd(const struct CmdTemplate* cmd)
359 {
360         ht_insert(&commands, cmd);
361 }
362
363 #if CONFIG_INTERNAL_COMMANDS
364 static ResultCode cmd_help(void)
365 {
366 #ifdef _DEBUG
367
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))
373         {
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);
378                 kprintf("\r\n");
379         }
380 #endif
381
382         return RC_OK;
383 }
384
385 #include "cmd_hunk.h"
386 DECLARE_CMD_HUNK(help, (NIL), (NIL));
387
388 #endif // CONFIG_INTERNAL_COMMANDS
389
390
391 void parser_init(void)
392 {
393         // Initialize the hashtable used to store the command description
394         ht_init(&commands);
395
396 #if CONFIG_INTERNAL_COMMANDS
397         parser_register_cmd(&CMD_HUNK_TEMPLATE(help));
398 #endif
399 }