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