]> git.gag.com Git - fw/openocd/blob - src/helper/command.c
Charles Hardin <ckhardin@gmail.com> and Øyvind Harboe
[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 extern command_context_t *active_cmd_ctx;
45
46 int handle_sleep_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 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
49 {               
50         Jim_Obj *tclOutput=(Jim_Obj *)privData;
51
52         Jim_AppendString(interp, tclOutput, string, strlen(string));
53 }
54
55 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
56 {
57         /* the private data is stashed in the interp structure */
58         command_t *c;
59         command_context_t *context;
60         int *retval;
61         int i;
62         int nwords;
63         char **words;
64
65         target_call_timer_callbacks_now();
66         LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ 
67         
68         c = interp->cmdPrivData;
69         LOG_DEBUG("script_command - %s", c->name);
70
71         nwords = argc;
72         words = malloc(sizeof(char *) * nwords);
73         for (i = 0; i < nwords; i++)
74         {
75                 int len;
76
77                 words[i] = strdup(Jim_GetString(argv[i], &len));
78                 if (words[i] == NULL) 
79                 {
80                         return JIM_ERR;
81                 }
82                 LOG_DEBUG("script_command - %s, argv[%u]=%s", c->name, i, words[i]);
83         }
84
85         /* grab the command context from the associated data */
86         context = Jim_GetAssocData(interp, "context");
87         retval = Jim_GetAssocData(interp, "retval"); 
88         if (context != NULL && retval != NULL)
89         {
90                 /* capture log output and return it */
91                 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
92                 log_add_callback(tcl_output, tclOutput);
93                 
94                 *retval = run_command(context, c, words, nwords);
95                 
96                 log_remove_callback(tcl_output, tclOutput);
97                 Jim_SetResult(interp, tclOutput);
98                 
99         }
100
101         for (i = 0; i < nwords; i++)
102                 free(words[i]);
103         free(words);
104
105         return (*retval==ERROR_OK)?JIM_OK:JIM_ERR;
106 }
107
108 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)
109 {
110         command_t *c, *p;
111         
112         if (!context || !name)
113                 return NULL;
114                                 
115         c = malloc(sizeof(command_t));
116         
117         c->name = strdup(name);
118         c->parent = parent;
119         c->children = NULL;
120         c->handler = handler;
121         c->mode = mode;
122         if (!help)
123                 help="";
124         c->next = NULL;
125         
126         /* place command in tree */
127         if (parent)
128         {
129                 if (parent->children)
130                 {
131                         /* find last child */
132                         for (p = parent->children; p && p->next; p = p->next);
133                         if (p)
134                                 p->next = c;
135                 }
136                 else
137                 {
138                         parent->children = c;
139                 }
140         }
141         else
142         {
143                 if (context->commands)
144                 {
145                         /* find last command */
146                         for (p = context->commands; p && p->next; p = p->next);
147                         if (p)
148                                 p->next = c;
149                 }
150                 else
151                 {
152                         context->commands = c;
153                 }
154         }
155         
156         /* just a placeholder, no handler */
157         if (c->handler==NULL)
158                 return c;
159
160         /* If this is a two level command, e.g. "flash banks", then the
161          * "unknown" proc in startup.tcl must redirect to  this command.
162          * 
163          * "flash banks" is translated by "unknown" to "flash_banks"
164          * if such a proc exists
165          */
166         /* Print help for command */
167         const char *t1="";
168         const char *t2="";
169         const char *t3="";
170         /* maximum of two levels :-) */
171         if (c->parent!=NULL)
172         {
173                 t1=c->parent->name;
174                 t2="_";
175         }
176         t3=c->name;
177         const char *full_name=alloc_printf("%s%s%s", t1, t2, t3);
178         Jim_CreateCommand(interp, full_name, script_command, c, NULL);
179         free((void *)full_name);
180         
181         
182         /* accumulate help text in Tcl helptext list.  */
183     Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
184     if (Jim_IsShared(helptext))
185         helptext = Jim_DuplicateObj(interp, helptext);
186         Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
187         
188         Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
189
190         /* maximum of two levels :-) */
191         if (c->parent!=NULL)
192         {
193                 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
194         } 
195         Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
196         
197         Jim_ListAppendElement(interp, cmd_entry, cmd_list);
198         Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
199         Jim_ListAppendElement(interp, helptext, cmd_entry);
200         return c;
201 }
202
203 int unregister_all_commands(command_context_t *context)
204 {
205         command_t *c, *c2;
206         
207         if (context == NULL)
208                 return ERROR_OK;
209         
210         
211         while(NULL != context->commands)
212         {
213                 c = context->commands;
214                 
215                 while(NULL != c->children)
216                 {
217                         c2 = c->children;
218                         c->children = c->children->next;
219                         free(c2->name);
220                         c2->name = NULL;
221                         free(c2);
222                         c2 = NULL;
223                 }
224                 
225                 context->commands = context->commands->next;
226                 
227                 free(c->name);
228                 c->name = NULL;
229                 free(c);
230                 c = NULL;               
231         }
232         
233         return ERROR_OK;
234 }
235
236 int unregister_command(command_context_t *context, char *name)
237 {
238         command_t *c, *p = NULL, *c2;
239         
240         if ((!context) || (!name))
241                 return ERROR_INVALID_ARGUMENTS;
242         
243         /* find command */
244         for (c = context->commands; c; c = c->next)
245         {
246                 if (strcmp(name, c->name) == 0)
247                 {
248                         /* unlink command */
249                         if (p)
250                         {
251                                 p->next = c->next;
252                         }
253                         else
254                         {
255                                 context->commands = c->next;
256                         }
257                         
258                         /* unregister children */
259                         if (c->children)
260                         {
261                                 for (c2 = c->children; c2; c2 = c2->next)
262                                 {
263                                         free(c2->name);
264                                         free(c2);
265                                 }
266                         }
267                         
268                         /* delete command */
269                         free(c->name);
270                         free(c);
271                 }
272                 
273                 /* remember the last command for unlinking */
274                 p = c;
275         }
276         
277         return ERROR_OK;
278 }
279
280
281 void command_output_text(command_context_t *context, const char *data)
282 {
283         if( context && context->output_handler && data  ){
284                 context->output_handler( context, data );
285         }
286 }
287
288 void command_print_n(command_context_t *context, char *format, ...)
289 {
290         char *string;
291         
292         va_list ap;
293         va_start(ap, format);
294
295         string = alloc_vprintf(format, ap);
296         if (string != NULL)
297         {
298                 context->output_handler(context, string);
299                 free(string);
300         }
301
302         va_end(ap);
303 }
304
305 void command_print(command_context_t *context, char *format, ...)
306 {
307         char *string;
308
309         va_list ap;
310         va_start(ap, format);
311
312         string = alloc_vprintf(format, ap);
313         if (string != NULL)
314         {
315                 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
316                 context->output_handler(context, string);
317                 free(string);
318         }
319
320         va_end(ap);
321 }
322
323 int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
324 {
325         int start_word=0;
326         if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) ))
327         {
328                 /* Config commands can not run after the config stage */
329                 return ERROR_FAIL;
330         }
331         
332         int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
333         if (retval == ERROR_COMMAND_SYNTAX_ERROR)
334         {
335                 /* Print help for command */
336                 const char *t1="";
337                 const char *t2="";
338                 const char *t3="";
339                 /* maximum of two levels :-) */
340                 if (c->parent!=NULL)
341                 {
342                         t1=c->parent->name;
343                         t2=" ";
344                 }
345                 t3=c->name;
346                 command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
347         }
348         else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
349         {
350                 /* just fall through for a shutdown request */
351         }
352         else if (retval != ERROR_OK)
353         {
354                 /* we do not print out an error message because the command *should*
355                  * have printed out an error
356                  */
357                 LOG_DEBUG("Command failed with error code %d", retval); 
358         }
359         
360         return retval; 
361 }
362
363 int command_run_line(command_context_t *context, char *line)
364 {
365         /* all the parent commands have been registered with the interpreter
366          * so, can just evaluate the line as a script and check for
367          * results
368          */
369         /* run the line thru a script engine */
370         int retval;
371         int retcode;
372
373         Jim_DeleteAssocData(interp, "context"); /* remove existing */
374         retcode = Jim_SetAssocData(interp, "context", NULL, context);
375         if (retcode != JIM_OK)
376                 return ERROR_FAIL;
377
378         /* associated the return value */
379         retval = ERROR_OK;
380         Jim_DeleteAssocData(interp, "retval"); /* remove existing */
381         retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
382         if (retcode != JIM_OK)
383                 return ERROR_FAIL;
384
385         retcode = Jim_Eval(interp, line);       
386         if (retcode == JIM_ERR) {
387                 Jim_PrintErrorMessage(interp);
388         } else if (retcode == JIM_EXIT) {
389                 /* ignore. */
390                 /* exit(Jim_GetExitCode(interp)); */
391         } else {
392                 const char *result;
393                 int reslen;
394
395                 result = Jim_GetString(Jim_GetResult(interp), &reslen);
396                 if (reslen) {
397                         int i;
398                         char buff[256+1];
399                         for (i = 0; i < reslen; i += 256)
400                         {
401                                 int chunk;
402                                 chunk = reslen - i;
403                                 if (chunk > 256)
404                                         chunk = 256;
405                                 strncpy(buff, result+i, chunk);
406                                 buff[chunk] = 0; 
407                                 LOG_USER_N("%s", buff);
408                         }
409                         LOG_USER_N("%s", "\n");
410                 }
411         }
412         return retval;
413 }
414
415
416 int command_run_linef(command_context_t *context, char *format, ...)
417 {
418         int retval=ERROR_FAIL;
419         char *string;
420         va_list ap;
421         va_start(ap, format);
422         string = alloc_vprintf(format, ap);
423         if (string!=NULL)
424         {
425                 retval=command_run_line(context, string);
426         }
427         va_end(ap);
428         return retval;
429 }
430
431
432
433 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
434 {
435         context->output_handler = output_handler;
436         context->output_handler_priv = priv;
437 }
438
439 command_context_t* copy_command_context(command_context_t* context)
440 {
441         command_context_t* copy_context = malloc(sizeof(command_context_t));
442
443         *copy_context = *context;
444
445         return copy_context;
446 }
447
448 int command_done(command_context_t *context)
449 {
450         free(context);
451         context = NULL;
452         
453         return ERROR_OK;
454 }
455
456 command_context_t* command_init()
457 {
458         command_context_t* context = malloc(sizeof(command_context_t));
459         
460         context->mode = COMMAND_EXEC;
461         context->commands = NULL;
462         context->current_target = 0;
463         context->output_handler = NULL;
464         context->output_handler_priv = NULL;
465         
466         register_command(context, NULL, "sleep", handle_sleep_command,
467                                          COMMAND_ANY, "sleep for <n> milliseconds");
468         
469         register_command(context, NULL, "fast", handle_fast_command,
470                                          COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
471         
472         return context;
473 }
474
475 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
476 {
477         if (!cmd_ctx)
478                 return ERROR_INVALID_ARGUMENTS;
479
480         cmd_ctx->mode = mode;
481         return ERROR_OK;
482 }
483
484 /* sleep command sleeps for <n> miliseconds
485  * this is useful in target startup scripts
486  */
487 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
488 {
489         unsigned long duration = 0;
490         
491         if (argc == 1)
492         {
493                 duration = strtoul(args[0], NULL, 0);
494                 usleep(duration * 1000);
495         }
496
497         return ERROR_OK;
498 }
499
500 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
501 {
502         if (argc!=1)
503                 return ERROR_COMMAND_SYNTAX_ERROR;
504         
505         fast_and_dangerous = strcmp("enable", args[0])==0;
506         
507         return ERROR_OK;
508 }