c1eacc00a821f5fc65eff31e0a6da414656a9c17
[fw/openocd] / src / helper / command.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   part of this file is taken from libcli (libcli.sourceforge.net)       *
6  *   Copyright (C) David Parrish (david@dparrish.com)                      *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
22  ***************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "replacements.h"
28
29 #include "command.h"
30
31 #include "log.h"
32 #include "time_support.h"
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <unistd.h>
40
41 #include <openocd_tcl.h>
42
43 int fast_and_dangerous = 0;
44
45 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
46 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
47 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
48
49 /* forward declaration of jim_command */
50 extern int jim_command(command_context_t *context, char *line);
51
52
53
54 command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
55 {
56         command_t *c, *p;
57         
58         if (!context || !name)
59                 return NULL;
60                                 
61         c = malloc(sizeof(command_t));
62         
63         c->name = strdup(name);
64         c->parent = parent;
65         c->children = NULL;
66         c->handler = handler;
67         c->mode = mode;
68         if (!help)
69                 help="";
70         c->next = NULL;
71         
72         /* place command in tree */
73         if (parent)
74         {
75                 if (parent->children)
76                 {
77                         /* find last child */
78                         for (p = parent->children; p && p->next; p = p->next);
79                         if (p)
80                                 p->next = c;
81                 }
82                 else
83                 {
84                         parent->children = c;
85                 }
86         }
87         else
88         {
89                 if (context->commands)
90                 {
91                         /* find last command */
92                         for (p = context->commands; p && p->next; p = p->next);
93                         if (p)
94                                 p->next = c;
95                 }
96                 else
97                 {
98                         context->commands = c;
99                 }
100         }
101         /* accumulate help text in Tcl helptext list.  */
102     Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
103     if (Jim_IsShared(helptext))
104         helptext = Jim_DuplicateObj(interp, helptext);
105         Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
106         
107         Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
108
109         /* maximum of two levels :-) */
110         if (c->parent!=NULL)
111         {
112                 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
113         } 
114         Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
115         
116         Jim_ListAppendElement(interp, cmd_entry, cmd_list);
117         Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
118         Jim_ListAppendElement(interp, helptext, cmd_entry);
119         return c;
120 }
121
122 int unregister_all_commands(command_context_t *context)
123 {
124         command_t *c, *c2;
125         
126         if (context == NULL)
127                 return ERROR_OK;
128         
129         
130         while(NULL != context->commands)
131         {
132                 c = context->commands;
133                 
134                 while(NULL != c->children)
135                 {
136                         c2 = c->children;
137                         c->children = c->children->next;
138                         free(c2->name);
139                         c2->name = NULL;
140                         free(c2);
141                         c2 = NULL;
142                 }
143                 
144                 context->commands = context->commands->next;
145                 
146                 free(c->name);
147                 c->name = NULL;
148                 free(c);
149                 c = NULL;               
150         }
151         
152         return ERROR_OK;
153 }
154
155 int unregister_command(command_context_t *context, char *name)
156 {
157         command_t *c, *p = NULL, *c2;
158         
159         if ((!context) || (!name))
160                 return ERROR_INVALID_ARGUMENTS;
161         
162         /* find command */
163         for (c = context->commands; c; c = c->next)
164         {
165                 if (strcmp(name, c->name) == 0)
166                 {
167                         /* unlink command */
168                         if (p)
169                         {
170                                 p->next = c->next;
171                         }
172                         else
173                         {
174                                 context->commands = c->next;
175                         }
176                         
177                         /* unregister children */
178                         if (c->children)
179                         {
180                                 for (c2 = c->children; c2; c2 = c2->next)
181                                 {
182                                         free(c2->name);
183                                         free(c2);
184                                 }
185                         }
186                         
187                         /* delete command */
188                         free(c->name);
189                         free(c);
190                 }
191                 
192                 /* remember the last command for unlinking */
193                 p = c;
194         }
195         
196         return ERROR_OK;
197 }
198
199 int parse_line(char *line, char *words[], int max_words)
200 {
201         int nwords = 0;
202         char *p = line;
203         char *word_start = line;
204         int inquote = 0;
205
206         while (nwords < max_words - 1)
207         {
208                 /* check if we reached
209                  * a terminating NUL
210                  * a matching closing quote character " or '
211                  * we're inside a word but not a quote, and the current character is whitespace
212                  */
213                 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
214                 {
215                         /* we're inside a word or quote, and reached its end*/
216                         if (word_start)
217                         {
218                                 int len;
219                                 char *word_end=p;
220                                 
221                                 /* This will handle extra whitespace within quotes */
222                                 while (isspace(*word_start)&&(word_start<word_end))
223                                         word_start++;
224                                 while (isspace(*(word_end-1))&&(word_start<word_end))
225                                         word_end--;
226                                 len = word_end - word_start;
227                                 
228                                 if (len>0)
229                                 {
230                                         /* copy the word */
231                                         memcpy(words[nwords] = malloc(len + 1), word_start, len);
232                                         /* add terminating NUL */
233                                         words[nwords++][len] = 0;
234                                 }
235                         }
236                         /* we're done parsing the line */
237                         if (!*p)
238                                 break;
239
240                         /* skip over trailing quote or whitespace*/
241                         if (inquote || isspace(*p))
242                                 p++;
243                         while (isspace(*p))
244                                 p++;
245                         
246                         inquote = 0;
247                         word_start = 0;
248                 }
249                 else if (*p == '"' || *p == '\'')
250                 {
251                         /* we've reached the beginning of a quote */
252                         inquote = *p++;
253                         word_start = p;
254                 }
255                 else
256                 {
257                         /* we've reached the beginning of a new word */
258                         if (!word_start)
259                                 word_start = p;
260                         
261                         /* normal character, skip */
262                         p++;
263                 }
264         }
265         
266         return nwords;
267 }
268
269 void command_output_text(command_context_t *context, const char *data)
270 {
271         if( context && context->output_handler && data  ){
272                 context->output_handler( context, data );
273         }
274 }
275
276 void command_print_n(command_context_t *context, char *format, ...)
277 {
278         char *string;
279         
280         va_list ap;
281         va_start(ap, format);
282
283         string = alloc_vprintf(format, ap);
284         if (string != NULL)
285         {
286                 context->output_handler(context, string);
287                 free(string);
288         }
289
290         va_end(ap);
291 }
292
293 void command_print(command_context_t *context, char *format, ...)
294 {
295         char *string;
296
297         va_list ap;
298         va_start(ap, format);
299
300         string = alloc_vprintf(format, ap);
301         if (string != NULL)
302         {
303                 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
304                 context->output_handler(context, string);
305                 free(string);
306         }
307
308         va_end(ap);
309 }
310
311 command_t *find_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word, int *new_start_word)
312 {
313         command_t *c;
314         
315         for (c = commands; c; c = c->next)
316         {
317                 if (strcasecmp(c->name, words[start_word]))
318                         continue;
319
320                 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
321                 {
322                         if (!c->children)
323                         {
324                                 if (!c->handler)
325                                 {
326                                         return NULL;
327                                 }
328                                 else
329                                 {
330                                         *new_start_word=start_word;
331                                         return c;
332                                 }
333                         }
334                         else
335                         {
336                                 if (start_word == num_words - 1)
337                                 {
338                                         return NULL;
339                                 }
340                                 return find_command(context, c->children, words, num_words, start_word + 1, new_start_word);
341                         }
342                 }
343         }
344         return NULL;
345 }
346
347 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words)
348 {
349         int start_word=0;
350         command_t *c;
351         c = find_command(context, commands, words, num_words, start_word, &start_word);
352         if (c == NULL)
353         {
354                 /* just return command not found */
355                 return ERROR_COMMAND_NOTFOUND;
356         }
357         
358         int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
359         if (retval == ERROR_COMMAND_SYNTAX_ERROR)
360         {
361                 
362         }
363         else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
364         {
365                 /* just fall through for a shutdown request */
366         }
367         else if (retval != ERROR_OK)
368         {
369                 /* we do not print out an error message because the command *should*
370                  * have printed out an error
371                  */
372                 LOG_DEBUG("Command failed with error code %d", retval); 
373         }
374         
375         return retval; 
376 }
377
378 int command_run_line_internal(command_context_t *context, char *line)
379 {
380         LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ 
381         
382         int nwords;
383         char *words[128] = {0};
384         int retval;
385         int i;
386
387         /* skip preceding whitespace */
388         while (isspace(*line))
389                 line++;
390         
391         /* empty line, ignore */
392         if (!*line)
393                 return ERROR_OK;
394         
395         /* ignore comments */
396         if (*line && (line[0] == '#'))
397                 return ERROR_OK;
398         
399         LOG_DEBUG("%s", line);
400
401         nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
402         
403         if (nwords > 0)
404         {
405                 retval = find_and_run_command(context, context->commands, words, nwords);
406         }
407         else
408                 return ERROR_INVALID_ARGUMENTS;
409         
410         for (i = 0; i < nwords; i++)
411                 free(words[i]);
412         
413         return retval;
414 }
415
416 int command_run_line(command_context_t *context, char *line)
417 {
418         /* if a command is unknown to the "unknown" proc in tcl/commands.tcl will
419          * redirect it to OpenOCD.
420          * 
421          * This avoids having to type the "openocd" prefix and makes OpenOCD
422          * commands "native" to Tcl.
423          */
424         return jim_command(context, line);
425 }
426
427
428 int command_run_linef(command_context_t *context, char *format, ...)
429 {
430         int retval=ERROR_FAIL;
431         char *string;
432         va_list ap;
433         va_start(ap, format);
434         string = alloc_vprintf(format, ap);
435         if (string!=NULL)
436         {
437                 retval=command_run_line(context, string);
438         }
439         va_end(ap);
440         return retval;
441 }
442
443
444
445 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
446 {
447         context->output_handler = output_handler;
448         context->output_handler_priv = priv;
449 }
450
451 command_context_t* copy_command_context(command_context_t* context)
452 {
453         command_context_t* copy_context = malloc(sizeof(command_context_t));
454
455         *copy_context = *context;
456         
457         return copy_context;
458 }
459
460 int command_done(command_context_t *context)
461 {
462         free(context);
463         context = NULL;
464         
465         return ERROR_OK;
466 }
467
468 command_context_t* command_init()
469 {
470         command_context_t* context = malloc(sizeof(command_context_t));
471         
472         context->mode = COMMAND_EXEC;
473         context->commands = NULL;
474         context->current_target = 0;
475         context->output_handler = NULL;
476         context->output_handler_priv = NULL;
477         
478         register_command(context, NULL, "sleep", handle_sleep_command,
479                                          COMMAND_ANY, "sleep for <n> milliseconds");
480         
481         register_command(context, NULL, "time", handle_time_command,
482                                          COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
483         
484         register_command(context, NULL, "fast", handle_fast_command,
485                                          COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
486         
487         return context;
488 }
489
490 /* sleep command sleeps for <n> miliseconds
491  * this is useful in target startup scripts
492  */
493 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
494 {
495         unsigned long duration = 0;
496         
497         if (argc == 1)
498         {
499                 duration = strtoul(args[0], NULL, 0);
500                 usleep(duration * 1000);
501         }
502
503         return ERROR_OK;
504 }
505
506 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
507 {
508         if (argc!=1)
509                 return ERROR_COMMAND_SYNTAX_ERROR;
510         
511         fast_and_dangerous = strcmp("enable", args[0])==0;
512         
513         return ERROR_OK;
514 }
515
516
517 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
518 {
519         duration_t duration;
520         char *duration_text;
521         int retval;
522         float t;
523         
524         if (argc<1)
525                 return ERROR_COMMAND_SYNTAX_ERROR;
526         
527         duration_start_measure(&duration);
528         
529         retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc);
530         if (retval == ERROR_COMMAND_NOTFOUND)
531         {
532                 command_print(cmd_ctx, "Command %s not found", args[0]);
533         }
534         
535         duration_stop_measure(&duration, &duration_text);
536         
537         t=duration.duration.tv_sec;
538         t+=((float)duration.duration.tv_usec / 1000000.0);
539         command_print(cmd_ctx, "%s took %fs", args[0], t);
540         
541         free(duration_text);
542
543         return retval;
544 }