help is now implemented in startup.tcl/help
[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         Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
104         
105         Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
106
107         /* maximum of two levels :-) */
108         if (c->parent!=NULL)
109         {
110                 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
111         } 
112         Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
113         
114         Jim_ListAppendElement(interp, cmd_entry, cmd_list);
115         Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
116         Jim_ListAppendElement(interp, helptext, cmd_entry);
117         return c;
118 }
119
120 int unregister_all_commands(command_context_t *context)
121 {
122         command_t *c, *c2;
123         
124         if (context == NULL)
125                 return ERROR_OK;
126         
127         
128         while(NULL != context->commands)
129         {
130                 c = context->commands;
131                 
132                 while(NULL != c->children)
133                 {
134                         c2 = c->children;
135                         c->children = c->children->next;
136                         free(c2->name);
137                         c2->name = NULL;
138                         free(c2);
139                         c2 = NULL;
140                 }
141                 
142                 context->commands = context->commands->next;
143                 
144                 free(c->name);
145                 c->name = NULL;
146                 free(c);
147                 c = NULL;               
148         }
149         
150         return ERROR_OK;
151 }
152
153 int unregister_command(command_context_t *context, char *name)
154 {
155         command_t *c, *p = NULL, *c2;
156         
157         if ((!context) || (!name))
158                 return ERROR_INVALID_ARGUMENTS;
159         
160         /* find command */
161         for (c = context->commands; c; c = c->next)
162         {
163                 if (strcmp(name, c->name) == 0)
164                 {
165                         /* unlink command */
166                         if (p)
167                         {
168                                 p->next = c->next;
169                         }
170                         else
171                         {
172                                 context->commands = c->next;
173                         }
174                         
175                         /* unregister children */
176                         if (c->children)
177                         {
178                                 for (c2 = c->children; c2; c2 = c2->next)
179                                 {
180                                         free(c2->name);
181                                         free(c2);
182                                 }
183                         }
184                         
185                         /* delete command */
186                         free(c->name);
187                         free(c);
188                 }
189                 
190                 /* remember the last command for unlinking */
191                 p = c;
192         }
193         
194         return ERROR_OK;
195 }
196
197 int parse_line(char *line, char *words[], int max_words)
198 {
199         int nwords = 0;
200         char *p = line;
201         char *word_start = line;
202         int inquote = 0;
203
204         while (nwords < max_words - 1)
205         {
206                 /* check if we reached
207                  * a terminating NUL
208                  * a matching closing quote character " or '
209                  * we're inside a word but not a quote, and the current character is whitespace
210                  */
211                 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
212                 {
213                         /* we're inside a word or quote, and reached its end*/
214                         if (word_start)
215                         {
216                                 int len;
217                                 char *word_end=p;
218                                 
219                                 /* This will handle extra whitespace within quotes */
220                                 while (isspace(*word_start)&&(word_start<word_end))
221                                         word_start++;
222                                 while (isspace(*(word_end-1))&&(word_start<word_end))
223                                         word_end--;
224                                 len = word_end - word_start;
225                                 
226                                 if (len>0)
227                                 {
228                                         /* copy the word */
229                                         memcpy(words[nwords] = malloc(len + 1), word_start, len);
230                                         /* add terminating NUL */
231                                         words[nwords++][len] = 0;
232                                 }
233                         }
234                         /* we're done parsing the line */
235                         if (!*p)
236                                 break;
237
238                         /* skip over trailing quote or whitespace*/
239                         if (inquote || isspace(*p))
240                                 p++;
241                         while (isspace(*p))
242                                 p++;
243                         
244                         inquote = 0;
245                         word_start = 0;
246                 }
247                 else if (*p == '"' || *p == '\'')
248                 {
249                         /* we've reached the beginning of a quote */
250                         inquote = *p++;
251                         word_start = p;
252                 }
253                 else
254                 {
255                         /* we've reached the beginning of a new word */
256                         if (!word_start)
257                                 word_start = p;
258                         
259                         /* normal character, skip */
260                         p++;
261                 }
262         }
263         
264         return nwords;
265 }
266
267 void command_output_text(command_context_t *context, const char *data)
268 {
269         if( context && context->output_handler && data  ){
270                 context->output_handler( context, data );
271         }
272 }
273
274 void command_print_n(command_context_t *context, char *format, ...)
275 {
276         char *string;
277         
278         va_list ap;
279         va_start(ap, format);
280
281         string = alloc_vprintf(format, ap);
282         if (string != NULL)
283         {
284                 context->output_handler(context, string);
285                 free(string);
286         }
287
288         va_end(ap);
289 }
290
291 void command_print(command_context_t *context, char *format, ...)
292 {
293         char *string;
294
295         va_list ap;
296         va_start(ap, format);
297
298         string = alloc_vprintf(format, ap);
299         if (string != NULL)
300         {
301                 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
302                 context->output_handler(context, string);
303                 free(string);
304         }
305
306         va_end(ap);
307 }
308
309 command_t *find_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word, int *new_start_word)
310 {
311         command_t *c;
312         
313         for (c = commands; c; c = c->next)
314         {
315                 if (strcasecmp(c->name, words[start_word]))
316                         continue;
317
318                 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
319                 {
320                         if (!c->children)
321                         {
322                                 if (!c->handler)
323                                 {
324                                         return NULL;
325                                 }
326                                 else
327                                 {
328                                         *new_start_word=start_word;
329                                         return c;
330                                 }
331                         }
332                         else
333                         {
334                                 if (start_word == num_words - 1)
335                                 {
336                                         return NULL;
337                                 }
338                                 return find_command(context, c->children, words, num_words, start_word + 1, new_start_word);
339                         }
340                 }
341         }
342         return NULL;
343 }
344
345 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words)
346 {
347         int start_word=0;
348         command_t *c;
349         c = find_command(context, commands, words, num_words, start_word, &start_word);
350         if (c == NULL)
351         {
352                 /* just return command not found */
353                 return ERROR_COMMAND_NOTFOUND;
354         }
355         
356         int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
357         if (retval == ERROR_COMMAND_SYNTAX_ERROR)
358         {
359                 
360         }
361         else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
362         {
363                 /* just fall through for a shutdown request */
364         }
365         else if (retval != ERROR_OK)
366         {
367                 /* we do not print out an error message because the command *should*
368                  * have printed out an error
369                  */
370                 LOG_DEBUG("Command failed with error code %d", retval); 
371         }
372         
373         return retval; 
374 }
375
376 int command_run_line_internal(command_context_t *context, char *line)
377 {
378         LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ 
379         
380         int nwords;
381         char *words[128] = {0};
382         int retval;
383         int i;
384
385         /* skip preceding whitespace */
386         while (isspace(*line))
387                 line++;
388         
389         /* empty line, ignore */
390         if (!*line)
391                 return ERROR_OK;
392         
393         /* ignore comments */
394         if (*line && (line[0] == '#'))
395                 return ERROR_OK;
396         
397         LOG_DEBUG("%s", line);
398
399         nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
400         
401         if (nwords > 0)
402         {
403                 retval = find_and_run_command(context, context->commands, words, nwords);
404         }
405         else
406                 return ERROR_INVALID_ARGUMENTS;
407         
408         for (i = 0; i < nwords; i++)
409                 free(words[i]);
410         
411         return retval;
412 }
413
414 int command_run_line(command_context_t *context, char *line)
415 {
416         /* if a command is unknown to the "unknown" proc in tcl/commands.tcl will
417          * redirect it to OpenOCD.
418          * 
419          * This avoids having to type the "openocd" prefix and makes OpenOCD
420          * commands "native" to Tcl.
421          */
422         return jim_command(context, line);
423 }
424
425
426 int command_run_linef(command_context_t *context, char *format, ...)
427 {
428         int retval=ERROR_FAIL;
429         char *string;
430         va_list ap;
431         va_start(ap, format);
432         string = alloc_vprintf(format, ap);
433         if (string!=NULL)
434         {
435                 retval=command_run_line(context, string);
436         }
437         va_end(ap);
438         return retval;
439 }
440
441
442
443 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
444 {
445         context->output_handler = output_handler;
446         context->output_handler_priv = priv;
447 }
448
449 command_context_t* copy_command_context(command_context_t* context)
450 {
451         command_context_t* copy_context = malloc(sizeof(command_context_t));
452
453         *copy_context = *context;
454         
455         return copy_context;
456 }
457
458 int command_done(command_context_t *context)
459 {
460         free(context);
461         context = NULL;
462         
463         return ERROR_OK;
464 }
465
466 command_context_t* command_init()
467 {
468         command_context_t* context = malloc(sizeof(command_context_t));
469         
470         context->mode = COMMAND_EXEC;
471         context->commands = NULL;
472         context->current_target = 0;
473         context->output_handler = NULL;
474         context->output_handler_priv = NULL;
475         
476         register_command(context, NULL, "sleep", handle_sleep_command,
477                                          COMMAND_ANY, "sleep for <n> milliseconds");
478         
479         register_command(context, NULL, "time", handle_time_command,
480                                          COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
481         
482         register_command(context, NULL, "fast", handle_fast_command,
483                                          COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
484         
485         return context;
486 }
487
488 /* sleep command sleeps for <n> miliseconds
489  * this is useful in target startup scripts
490  */
491 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
492 {
493         unsigned long duration = 0;
494         
495         if (argc == 1)
496         {
497                 duration = strtoul(args[0], NULL, 0);
498                 usleep(duration * 1000);
499         }
500
501         return ERROR_OK;
502 }
503
504 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
505 {
506         if (argc!=1)
507                 return ERROR_COMMAND_SYNTAX_ERROR;
508         
509         fast_and_dangerous = strcmp("enable", args[0])==0;
510         
511         return ERROR_OK;
512 }
513
514
515 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
516 {
517         duration_t duration;
518         char *duration_text;
519         int retval;
520         float t;
521         
522         if (argc<1)
523                 return ERROR_COMMAND_SYNTAX_ERROR;
524         
525         duration_start_measure(&duration);
526         
527         retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc);
528         if (retval == ERROR_COMMAND_NOTFOUND)
529         {
530                 command_print(cmd_ctx, "Command %s not found", args[0]);
531         }
532         
533         duration_stop_measure(&duration, &duration_text);
534         
535         t=duration.duration.tv_sec;
536         t+=((float)duration.duration.tv_usec / 1000000.0);
537         command_print(cmd_ctx, "%s took %fs", args[0], t);
538         
539         free(duration_text);
540
541         return retval;
542 }