]> git.gag.com Git - fw/openocd/blob - src/helper/command.c
Collect output from openocd commands into openocd_output local variable
[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                 
98                 /* We dump output into this local variable */
99                 Jim_SetVariableStr(interp, "openocd_output", tclOutput);
100         }
101
102         for (i = 0; i < nwords; i++)
103                 free(words[i]);
104         free(words);
105
106         return (*retval==ERROR_OK)?JIM_OK:JIM_ERR;
107 }
108
109 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)
110 {
111         command_t *c, *p;
112         
113         if (!context || !name)
114                 return NULL;
115                                 
116         c = malloc(sizeof(command_t));
117         
118         c->name = strdup(name);
119         c->parent = parent;
120         c->children = NULL;
121         c->handler = handler;
122         c->mode = mode;
123         if (!help)
124                 help="";
125         c->next = NULL;
126         
127         /* place command in tree */
128         if (parent)
129         {
130                 if (parent->children)
131                 {
132                         /* find last child */
133                         for (p = parent->children; p && p->next; p = p->next);
134                         if (p)
135                                 p->next = c;
136                 }
137                 else
138                 {
139                         parent->children = c;
140                 }
141         }
142         else
143         {
144                 if (context->commands)
145                 {
146                         /* find last command */
147                         for (p = context->commands; p && p->next; p = p->next);
148                         if (p)
149                                 p->next = c;
150                 }
151                 else
152                 {
153                         context->commands = c;
154                 }
155         }
156         
157         /* just a placeholder, no handler */
158         if (c->handler==NULL)
159                 return c;
160
161         /* If this is a two level command, e.g. "flash banks", then the
162          * "unknown" proc in startup.tcl must redirect to  this command.
163          * 
164          * "flash banks" is translated by "unknown" to "flash_banks"
165          * if such a proc exists
166          */
167         /* Print help for command */
168         const char *t1="";
169         const char *t2="";
170         const char *t3="";
171         /* maximum of two levels :-) */
172         if (c->parent!=NULL)
173         {
174                 t1=c->parent->name;
175                 t2="_";
176         }
177         t3=c->name;
178         const char *full_name=alloc_printf("%s%s%s", t1, t2, t3);
179         Jim_CreateCommand(interp, full_name, script_command, c, NULL);
180         free((void *)full_name);
181         
182         
183         /* accumulate help text in Tcl helptext list.  */
184     Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
185     if (Jim_IsShared(helptext))
186         helptext = Jim_DuplicateObj(interp, helptext);
187         Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
188         
189         Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
190
191         /* maximum of two levels :-) */
192         if (c->parent!=NULL)
193         {
194                 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
195         } 
196         Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
197         
198         Jim_ListAppendElement(interp, cmd_entry, cmd_list);
199         Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
200         Jim_ListAppendElement(interp, helptext, cmd_entry);
201         return c;
202 }
203
204 int unregister_all_commands(command_context_t *context)
205 {
206         command_t *c, *c2;
207         
208         if (context == NULL)
209                 return ERROR_OK;
210         
211         
212         while(NULL != context->commands)
213         {
214                 c = context->commands;
215                 
216                 while(NULL != c->children)
217                 {
218                         c2 = c->children;
219                         c->children = c->children->next;
220                         free(c2->name);
221                         c2->name = NULL;
222                         free(c2);
223                         c2 = NULL;
224                 }
225                 
226                 context->commands = context->commands->next;
227                 
228                 free(c->name);
229                 c->name = NULL;
230                 free(c);
231                 c = NULL;               
232         }
233         
234         return ERROR_OK;
235 }
236
237 int unregister_command(command_context_t *context, char *name)
238 {
239         command_t *c, *p = NULL, *c2;
240         
241         if ((!context) || (!name))
242                 return ERROR_INVALID_ARGUMENTS;
243         
244         /* find command */
245         for (c = context->commands; c; c = c->next)
246         {
247                 if (strcmp(name, c->name) == 0)
248                 {
249                         /* unlink command */
250                         if (p)
251                         {
252                                 p->next = c->next;
253                         }
254                         else
255                         {
256                                 context->commands = c->next;
257                         }
258                         
259                         /* unregister children */
260                         if (c->children)
261                         {
262                                 for (c2 = c->children; c2; c2 = c2->next)
263                                 {
264                                         free(c2->name);
265                                         free(c2);
266                                 }
267                         }
268                         
269                         /* delete command */
270                         free(c->name);
271                         free(c);
272                 }
273                 
274                 /* remember the last command for unlinking */
275                 p = c;
276         }
277         
278         return ERROR_OK;
279 }
280
281
282 void command_output_text(command_context_t *context, const char *data)
283 {
284         if( context && context->output_handler && data  ){
285                 context->output_handler( context, data );
286         }
287 }
288
289 void command_print_n(command_context_t *context, char *format, ...)
290 {
291         char *string;
292         
293         va_list ap;
294         va_start(ap, format);
295
296         string = alloc_vprintf(format, ap);
297         if (string != NULL)
298         {
299                 /* we want this collected in the log + we also want to pick it up as a tcl return
300                  * value.
301                  * 
302                  * The latter bit isn't precisely neat, but will do for now.
303                  */
304                 LOG_USER_N("%s", string);
305                 // We already printed it above
306                 //command_output_text(context, string);
307                 free(string);
308         }
309
310         va_end(ap);
311 }
312
313 void command_print(command_context_t *context, char *format, ...)
314 {
315         char *string;
316
317         va_list ap;
318         va_start(ap, format);
319
320         string = alloc_vprintf(format, ap);
321         if (string != NULL)
322         {
323                 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
324                 /* we want this collected in the log + we also want to pick it up as a tcl return
325                  * value.
326                  * 
327                  * The latter bit isn't precisely neat, but will do for now.
328                  */
329                 LOG_USER_N("%s", string);
330                 // We already printed it above
331                 //command_output_text(context, string);
332                 free(string);
333         }
334
335         va_end(ap);
336 }
337
338 int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
339 {
340         int start_word=0;
341         if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) ))
342         {
343                 /* Config commands can not run after the config stage */
344                 return ERROR_FAIL;
345         }
346         
347         int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
348         if (retval == ERROR_COMMAND_SYNTAX_ERROR)
349         {
350                 /* Print help for command */
351                 const char *t1="";
352                 const char *t2="";
353                 const char *t3="";
354                 /* maximum of two levels :-) */
355                 if (c->parent!=NULL)
356                 {
357                         t1=c->parent->name;
358                         t2=" ";
359                 }
360                 t3=c->name;
361                 command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
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(command_context_t *context, char *line)
379 {
380         /* all the parent commands have been registered with the interpreter
381          * so, can just evaluate the line as a script and check for
382          * results
383          */
384         /* run the line thru a script engine */
385         int retval;
386         int retcode;
387         Jim_DeleteAssocData(interp, "context"); /* remove existing */
388         retcode = Jim_SetAssocData(interp, "context", NULL, context);
389         if (retcode != JIM_OK)
390                 return ERROR_FAIL;
391
392         /* associated the return value */
393         retval = ERROR_OK;
394         Jim_DeleteAssocData(interp, "retval"); /* remove existing */
395         retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
396         if (retcode != JIM_OK)
397                 return ERROR_FAIL;
398
399         active_cmd_ctx = context;
400         retcode = Jim_Eval(interp, line);       
401         if (retcode == JIM_ERR) {
402                 if (retval!=ERROR_COMMAND_CLOSE_CONNECTION)
403                 {
404                         /* We do not print the connection closed error message */
405                         Jim_PrintErrorMessage(interp);
406                 }
407                 if (retval==ERROR_OK)
408                 {
409                         /* It wasn't a low level OpenOCD command that failed */
410                         return ERROR_FAIL; 
411                 }
412                 return retval;
413         } else if (retcode == JIM_EXIT) {
414                 /* ignore. */
415                 /* exit(Jim_GetExitCode(interp)); */
416         } else {
417                 const char *result;
418                 int reslen;
419
420                 result = Jim_GetString(Jim_GetResult(interp), &reslen);
421                 if (reslen) {
422                         int i;
423                         char buff[256+1];
424                         for (i = 0; i < reslen; i += 256)
425                         {
426                                 int chunk;
427                                 chunk = reslen - i;
428                                 if (chunk > 256)
429                                         chunk = 256;
430                                 strncpy(buff, result+i, chunk);
431                                 buff[chunk] = 0; 
432                                 LOG_USER_N("%s", buff);
433                         }
434                         LOG_USER_N("%s", "\n");
435                 }
436         }
437         return retval;
438 }
439
440
441 int command_run_linef(command_context_t *context, char *format, ...)
442 {
443         int retval=ERROR_FAIL;
444         char *string;
445         va_list ap;
446         va_start(ap, format);
447         string = alloc_vprintf(format, ap);
448         if (string!=NULL)
449         {
450                 retval=command_run_line(context, string);
451         }
452         va_end(ap);
453         return retval;
454 }
455
456
457
458 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
459 {
460         context->output_handler = output_handler;
461         context->output_handler_priv = priv;
462 }
463
464 command_context_t* copy_command_context(command_context_t* context)
465 {
466         command_context_t* copy_context = malloc(sizeof(command_context_t));
467
468         *copy_context = *context;
469
470         return copy_context;
471 }
472
473 int command_done(command_context_t *context)
474 {
475         free(context);
476         context = NULL;
477         
478         return ERROR_OK;
479 }
480
481 command_context_t* command_init()
482 {
483         command_context_t* context = malloc(sizeof(command_context_t));
484         
485         context->mode = COMMAND_EXEC;
486         context->commands = NULL;
487         context->current_target = 0;
488         context->output_handler = NULL;
489         context->output_handler_priv = NULL;
490         
491         register_command(context, NULL, "sleep", handle_sleep_command,
492                                          COMMAND_ANY, "sleep for <n> milliseconds");
493         
494         register_command(context, NULL, "fast", handle_fast_command,
495                                          COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
496         
497         return context;
498 }
499
500 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
501 {
502         if (!cmd_ctx)
503                 return ERROR_INVALID_ARGUMENTS;
504
505         cmd_ctx->mode = mode;
506         return ERROR_OK;
507 }
508
509 /* sleep command sleeps for <n> miliseconds
510  * this is useful in target startup scripts
511  */
512 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
513 {
514         unsigned long duration = 0;
515         
516         if (argc == 1)
517         {
518                 duration = strtoul(args[0], NULL, 0);
519                 usleep(duration * 1000);
520         }
521
522         return ERROR_OK;
523 }
524
525 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
526 {
527         if (argc!=1)
528                 return ERROR_COMMAND_SYNTAX_ERROR;
529         
530         fast_and_dangerous = strcmp("enable", args[0])==0;
531         
532         return ERROR_OK;
533 }