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