]> git.sur5r.net Git - openocd/blob - src/helper/command.c
Charles Hardin ckhardin at gmail.com Instead of stashing the context in a global...
[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_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
47 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
48 {               
49         Jim_Obj *tclOutput=(Jim_Obj *)privData;
50
51         Jim_AppendString(interp, tclOutput, string, strlen(string));
52 }
53
54 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
55 {
56         /* the private data is stashed in the interp structure */
57         command_t *c;
58         command_context_t *context;
59         int *retval;
60         int i;
61         int nwords;
62         char **words;
63
64         target_call_timer_callbacks_now();
65         LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ 
66         
67         c = interp->cmdPrivData;
68         LOG_DEBUG("script_command - %s", c->name);
69
70         nwords = argc;
71         words = malloc(sizeof(char *) * nwords);
72         for (i = 0; i < nwords; i++)
73         {
74                 int len;
75
76                 words[i] = strdup(Jim_GetString(argv[i], &len));
77                 if (words[i] == NULL) 
78                 {
79                         return JIM_ERR;
80                 }
81                 LOG_DEBUG("script_command - %s, argv[%u]=%s", c->name, i, words[i]);
82         }
83
84         /* grab the command context from the associated data */
85         context = Jim_GetAssocData(interp, "context");
86         retval = Jim_GetAssocData(interp, "retval"); 
87         if (context != NULL && retval != NULL)
88         {
89                 /* capture log output and return it */
90                 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
91                 log_add_callback(tcl_output, tclOutput);
92                 
93                 *retval = run_command(context, c, words, nwords);
94                 
95                 log_remove_callback(tcl_output, tclOutput);
96                 
97                 /* We dump output into this local variable */
98                 Jim_SetVariableStr(interp, "openocd_output", tclOutput);
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                 /* we want this collected in the log + we also want to pick it up as a tcl return
299                  * value.
300                  * 
301                  * The latter bit isn't precisely neat, but will do for now.
302                  */
303                 LOG_USER_N("%s", string);
304                 // We already printed it above
305                 //command_output_text(context, string);
306                 free(string);
307         }
308
309         va_end(ap);
310 }
311
312 void command_print(command_context_t *context, char *format, ...)
313 {
314         char *string;
315
316         va_list ap;
317         va_start(ap, format);
318
319         string = alloc_vprintf(format, ap);
320         if (string != NULL)
321         {
322                 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
323                 /* we want this collected in the log + we also want to pick it up as a tcl return
324                  * value.
325                  * 
326                  * The latter bit isn't precisely neat, but will do for now.
327                  */
328                 LOG_USER_N("%s", string);
329                 // We already printed it above
330                 //command_output_text(context, string);
331                 free(string);
332         }
333
334         va_end(ap);
335 }
336
337 int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
338 {
339         int start_word=0;
340         if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) ))
341         {
342                 /* Config commands can not run after the config stage */
343                 return ERROR_FAIL;
344         }
345         
346         int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
347         if (retval == ERROR_COMMAND_SYNTAX_ERROR)
348         {
349                 /* Print help for command */
350                 const char *t1="";
351                 const char *t2="";
352                 const char *t3="";
353                 /* maximum of two levels :-) */
354                 if (c->parent!=NULL)
355                 {
356                         t1=c->parent->name;
357                         t2=" ";
358                 }
359                 t3=c->name;
360                 command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
361         }
362         else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
363         {
364                 /* just fall through for a shutdown request */
365         }
366         else if (retval != ERROR_OK)
367         {
368                 /* we do not print out an error message because the command *should*
369                  * have printed out an error
370                  */
371                 LOG_DEBUG("Command failed with error code %d", retval); 
372         }
373         
374         return retval; 
375 }
376
377 int command_run_line(command_context_t *context, char *line)
378 {
379         /* all the parent commands have been registered with the interpreter
380          * so, can just evaluate the line as a script and check for
381          * results
382          */
383         /* run the line thru a script engine */
384         int retval;
385         int retcode;
386         Jim_DeleteAssocData(interp, "context"); /* remove existing */
387         retcode = Jim_SetAssocData(interp, "context", NULL, context);
388         if (retcode != JIM_OK)
389                 return ERROR_FAIL;
390
391         /* associated the return value */
392         retval = ERROR_OK;
393         Jim_DeleteAssocData(interp, "retval"); /* remove existing */
394         retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
395         if (retcode != JIM_OK)
396                 return ERROR_FAIL;
397
398         retcode = Jim_Eval(interp, line);       
399         if (retcode == JIM_ERR) {
400                 if (retval!=ERROR_COMMAND_CLOSE_CONNECTION)
401                 {
402                         /* We do not print the connection closed error message */
403                         Jim_PrintErrorMessage(interp);
404                 }
405                 if (retval==ERROR_OK)
406                 {
407                         /* It wasn't a low level OpenOCD command that failed */
408                         return ERROR_FAIL; 
409                 }
410                 return retval;
411         } else if (retcode == JIM_EXIT) {
412                 /* ignore. */
413                 /* exit(Jim_GetExitCode(interp)); */
414         } else {
415                 const char *result;
416                 int reslen;
417
418                 result = Jim_GetString(Jim_GetResult(interp), &reslen);
419                 if (reslen) {
420                         int i;
421                         char buff[256+1];
422                         for (i = 0; i < reslen; i += 256)
423                         {
424                                 int chunk;
425                                 chunk = reslen - i;
426                                 if (chunk > 256)
427                                         chunk = 256;
428                                 strncpy(buff, result+i, chunk);
429                                 buff[chunk] = 0; 
430                                 LOG_USER_N("%s", buff);
431                         }
432                         LOG_USER_N("%s", "\n");
433                 }
434         }
435         return retval;
436 }
437
438
439 int command_run_linef(command_context_t *context, char *format, ...)
440 {
441         int retval=ERROR_FAIL;
442         char *string;
443         va_list ap;
444         va_start(ap, format);
445         string = alloc_vprintf(format, ap);
446         if (string!=NULL)
447         {
448                 retval=command_run_line(context, string);
449         }
450         va_end(ap);
451         return retval;
452 }
453
454
455
456 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
457 {
458         context->output_handler = output_handler;
459         context->output_handler_priv = priv;
460 }
461
462 command_context_t* copy_command_context(command_context_t* context)
463 {
464         command_context_t* copy_context = malloc(sizeof(command_context_t));
465
466         *copy_context = *context;
467
468         return copy_context;
469 }
470
471 int command_done(command_context_t *context)
472 {
473         free(context);
474         context = NULL;
475         
476         return ERROR_OK;
477 }
478
479 command_context_t* command_init()
480 {
481         command_context_t* context = malloc(sizeof(command_context_t));
482         
483         context->mode = COMMAND_EXEC;
484         context->commands = NULL;
485         context->current_target = 0;
486         context->output_handler = NULL;
487         context->output_handler_priv = NULL;
488         
489         register_command(context, NULL, "sleep", handle_sleep_command,
490                                          COMMAND_ANY, "sleep for <n> milliseconds");
491         
492         register_command(context, NULL, "fast", handle_fast_command,
493                                          COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
494         
495         return context;
496 }
497
498 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
499 {
500         if (!cmd_ctx)
501                 return ERROR_INVALID_ARGUMENTS;
502
503         cmd_ctx->mode = mode;
504         return ERROR_OK;
505 }
506
507 /* sleep command sleeps for <n> miliseconds
508  * this is useful in target startup scripts
509  */
510 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
511 {
512         unsigned long duration = 0;
513         
514         if (argc == 1)
515         {
516                 duration = strtoul(args[0], NULL, 0);
517                 usleep(duration * 1000);
518         }
519
520         return ERROR_OK;
521 }
522
523 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
524 {
525         if (argc!=1)
526                 return ERROR_COMMAND_SYNTAX_ERROR;
527         
528         fast_and_dangerous = strcmp("enable", args[0])==0;
529         
530         return ERROR_OK;
531 }