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