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