- added myself to copyright on files i remember adding large contributions for over...
[openocd.git] / src / helper / command.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2007,2008 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
7 * *
8 * Copyright (C) 2008, Duane Ellis *
9 * openocd@duaneeellis.com *
10 * *
11 * part of this file is taken from libcli (libcli.sourceforge.net) *
12 * Copyright (C) David Parrish (david@dparrish.com) *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program; if not, write to the *
26 * Free Software Foundation, Inc., *
27 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
28 ***************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "replacements.h"
34 #include "target.h"
35 #include "command.h"
36 #include "configuration.h"
37
38 #include "log.h"
39 #include "time_support.h"
40 #include "jim-eventloop.h"
41
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <errno.h>
49
50 int fast_and_dangerous = 0;
51 Jim_Interp *interp = NULL;
52
53 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
54 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
55
56 int run_command(command_context_t *context, command_t *c, char *words[], int num_words);
57
58 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
59 {
60 Jim_Obj *tclOutput=(Jim_Obj *)privData;
61
62 Jim_AppendString(interp, tclOutput, string, strlen(string));
63 }
64
65 extern command_context_t *global_cmd_ctx;
66
67
68 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
69 {
70 /* the private data is stashed in the interp structure */
71 command_t *c;
72 command_context_t *context;
73 int retval;
74 int i;
75 int nwords;
76 char **words;
77
78 target_call_timer_callbacks_now();
79 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
80
81 c = interp->cmdPrivData;
82 LOG_DEBUG("script_command - %s", c->name);
83
84 words = malloc(sizeof(char *) * argc);
85 for (i = 0; i < argc; i++)
86 {
87 int len;
88 const char *w=Jim_GetString(argv[i], &len);
89 if (*w=='#')
90 {
91 /* hit an end of line comment */
92 break;
93 }
94 words[i] = strdup(w);
95 if (words[i] == NULL)
96 {
97 return JIM_ERR;
98 }
99 LOG_DEBUG("script_command - %s, argv[%u]=%s", c->name, i, words[i]);
100 }
101 nwords = i;
102
103 /* grab the command context from the associated data */
104 context = Jim_GetAssocData(interp, "context");
105 if (context == NULL)
106 {
107 /* Tcl can invoke commands directly instead of via command_run_line(). This would
108 * happen when the Jim Tcl interpreter is provided by eCos.
109 */
110 context = global_cmd_ctx;
111 }
112
113 /* capture log output and return it */
114 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
115 /* a garbage collect can happen, so we need a reference count to this object */
116 Jim_IncrRefCount(tclOutput);
117
118 log_add_callback(tcl_output, tclOutput);
119
120 retval = run_command(context, c, words, nwords);
121
122 log_remove_callback(tcl_output, tclOutput);
123
124 /* We dump output into this local variable */
125 Jim_SetResult(interp, tclOutput);
126 Jim_DecrRefCount(interp, tclOutput);
127
128 for (i = 0; i < nwords; i++)
129 free(words[i]);
130 free(words);
131
132 int *return_retval = Jim_GetAssocData(interp, "retval");
133 if (return_retval != NULL)
134 {
135 *return_retval = retval;
136 }
137
138 return (retval==ERROR_OK)?JIM_OK:JIM_ERR;
139 }
140
141 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)
142 {
143 command_t *c, *p;
144
145 if (!context || !name)
146 return NULL;
147
148 c = malloc(sizeof(command_t));
149
150 c->name = strdup(name);
151 c->parent = parent;
152 c->children = NULL;
153 c->handler = handler;
154 c->mode = mode;
155 if (!help)
156 help="";
157 c->next = NULL;
158
159 /* place command in tree */
160 if (parent)
161 {
162 if (parent->children)
163 {
164 /* find last child */
165 for (p = parent->children; p && p->next; p = p->next);
166 if (p)
167 p->next = c;
168 }
169 else
170 {
171 parent->children = c;
172 }
173 }
174 else
175 {
176 if (context->commands)
177 {
178 /* find last command */
179 for (p = context->commands; p && p->next; p = p->next);
180 if (p)
181 p->next = c;
182 }
183 else
184 {
185 context->commands = c;
186 }
187 }
188
189 /* just a placeholder, no handler */
190 if (c->handler==NULL)
191 return c;
192
193 /* If this is a two level command, e.g. "flash banks", then the
194 * "unknown" proc in startup.tcl must redirect to this command.
195 *
196 * "flash banks" is translated by "unknown" to "flash_banks"
197 * if such a proc exists
198 */
199 /* Print help for command */
200 const char *t1="";
201 const char *t2="";
202 const char *t3="";
203 /* maximum of two levels :-) */
204 if (c->parent!=NULL)
205 {
206 t1=c->parent->name;
207 t2="_";
208 }
209 t3=c->name;
210 const char *full_name=alloc_printf("ocd_%s%s%s", t1, t2, t3);
211 Jim_CreateCommand(interp, full_name, script_command, c, NULL);
212 free((void *)full_name);
213
214 /* we now need to add an overrideable proc */
215 const char *override_name=alloc_printf("proc %s%s%s {args} {if {[catch {eval \"ocd_%s%s%s $args\"}]==0} {return \"\"} else { return -code error }", t1, t2, t3, t1, t2, t3);
216 Jim_Eval_Named(interp, override_name, __FILE__, __LINE__ );
217 free((void *)override_name);
218
219 /* accumulate help text in Tcl helptext list. */
220 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
221 if (Jim_IsShared(helptext))
222 helptext = Jim_DuplicateObj(interp, helptext);
223 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
224
225 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
226
227 /* maximum of two levels :-) */
228 if (c->parent!=NULL)
229 {
230 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
231 }
232 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
233
234 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
235 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
236 Jim_ListAppendElement(interp, helptext, cmd_entry);
237 return c;
238 }
239
240 int unregister_all_commands(command_context_t *context)
241 {
242 command_t *c, *c2;
243
244 if (context == NULL)
245 return ERROR_OK;
246
247 while(NULL != context->commands)
248 {
249 c = context->commands;
250
251 while(NULL != c->children)
252 {
253 c2 = c->children;
254 c->children = c->children->next;
255 free(c2->name);
256 c2->name = NULL;
257 free(c2);
258 c2 = NULL;
259 }
260
261 context->commands = context->commands->next;
262
263 free(c->name);
264 c->name = NULL;
265 free(c);
266 c = NULL;
267 }
268
269 return ERROR_OK;
270 }
271
272 int unregister_command(command_context_t *context, char *name)
273 {
274 command_t *c, *p = NULL, *c2;
275
276 if ((!context) || (!name))
277 return ERROR_INVALID_ARGUMENTS;
278
279 /* find command */
280 for (c = context->commands; c; c = c->next)
281 {
282 if (strcmp(name, c->name) == 0)
283 {
284 /* unlink command */
285 if (p)
286 {
287 p->next = c->next;
288 }
289 else
290 {
291 context->commands = c->next;
292 }
293
294 /* unregister children */
295 if (c->children)
296 {
297 for (c2 = c->children; c2; c2 = c2->next)
298 {
299 free(c2->name);
300 free(c2);
301 }
302 }
303
304 /* delete command */
305 free(c->name);
306 free(c);
307 }
308
309 /* remember the last command for unlinking */
310 p = c;
311 }
312
313 return ERROR_OK;
314 }
315
316 void command_output_text(command_context_t *context, const char *data)
317 {
318 if( context && context->output_handler && data ){
319 context->output_handler( context, data );
320 }
321 }
322
323 void command_print_n(command_context_t *context, char *format, ...)
324 {
325 char *string;
326
327 va_list ap;
328 va_start(ap, format);
329
330 string = alloc_vprintf(format, ap);
331 if (string != NULL)
332 {
333 /* we want this collected in the log + we also want to pick it up as a tcl return
334 * value.
335 *
336 * The latter bit isn't precisely neat, but will do for now.
337 */
338 LOG_USER_N("%s", string);
339 // We already printed it above
340 //command_output_text(context, string);
341 free(string);
342 }
343
344 va_end(ap);
345 }
346
347 void command_print(command_context_t *context, char *format, ...)
348 {
349 char *string;
350
351 va_list ap;
352 va_start(ap, format);
353
354 string = alloc_vprintf(format, ap);
355 if (string != NULL)
356 {
357 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
358 /* we want this collected in the log + we also want to pick it up as a tcl return
359 * value.
360 *
361 * The latter bit isn't precisely neat, but will do for now.
362 */
363 LOG_USER_N("%s", string);
364 // We already printed it above
365 //command_output_text(context, string);
366 free(string);
367 }
368
369 va_end(ap);
370 }
371
372 int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
373 {
374 int start_word=0;
375 if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) ))
376 {
377 /* Config commands can not run after the config stage */
378 LOG_ERROR("Illegal mode for command");
379 return ERROR_FAIL;
380 }
381
382 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
383 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
384 {
385 /* Print help for command */
386 const char *t1="";
387 const char *t2="";
388 const char *t3="";
389 /* maximum of two levels :-) */
390 if (c->parent!=NULL)
391 {
392 t1=c->parent->name;
393 t2=" ";
394 }
395 t3=c->name;
396 command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
397 }
398 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
399 {
400 /* just fall through for a shutdown request */
401 }
402 else if (retval != ERROR_OK)
403 {
404 /* we do not print out an error message because the command *should*
405 * have printed out an error
406 */
407 LOG_DEBUG("Command failed with error code %d", retval);
408 }
409
410 return retval;
411 }
412
413 int command_run_line(command_context_t *context, char *line)
414 {
415 /* all the parent commands have been registered with the interpreter
416 * so, can just evaluate the line as a script and check for
417 * results
418 */
419 /* run the line thru a script engine */
420 int retval=ERROR_FAIL;
421 int retcode;
422 /* Beware! This code needs to be reentrant. It is also possible
423 * for OpenOCD commands to be invoked directly from Tcl. This would
424 * happen when the Jim Tcl interpreter is provided by eCos for
425 * instance.
426 */
427 Jim_DeleteAssocData(interp, "context");
428 retcode = Jim_SetAssocData(interp, "context", NULL, context);
429 if (retcode == JIM_OK)
430 {
431 /* associated the return value */
432 Jim_DeleteAssocData(interp, "retval");
433 retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
434 if (retcode == JIM_OK)
435 {
436 retcode = Jim_Eval_Named(interp, line, __FILE__, __LINE__ );
437
438 Jim_DeleteAssocData(interp, "retval");
439 }
440 Jim_DeleteAssocData(interp, "context");
441 }
442 if (retcode == JIM_ERR) {
443 if (retval!=ERROR_COMMAND_CLOSE_CONNECTION)
444 {
445 /* We do not print the connection closed error message */
446 Jim_PrintErrorMessage(interp);
447 }
448 if (retval==ERROR_OK)
449 {
450 /* It wasn't a low level OpenOCD command that failed */
451 return ERROR_FAIL;
452 }
453 return retval;
454 } else if (retcode == JIM_EXIT) {
455 /* ignore. */
456 /* exit(Jim_GetExitCode(interp)); */
457 } else {
458 const char *result;
459 int reslen;
460
461 result = Jim_GetString(Jim_GetResult(interp), &reslen);
462 if (reslen) {
463 int i;
464 char buff[256+1];
465 for (i = 0; i < reslen; i += 256)
466 {
467 int chunk;
468 chunk = reslen - i;
469 if (chunk > 256)
470 chunk = 256;
471 strncpy(buff, result+i, chunk);
472 buff[chunk] = 0;
473 LOG_USER_N("%s", buff);
474 }
475 LOG_USER_N("%s", "\n");
476 }
477 }
478 return retval;
479 }
480
481 int command_run_linef(command_context_t *context, char *format, ...)
482 {
483 int retval=ERROR_FAIL;
484 char *string;
485 va_list ap;
486 va_start(ap, format);
487 string = alloc_vprintf(format, ap);
488 if (string!=NULL)
489 {
490 retval=command_run_line(context, string);
491 }
492 va_end(ap);
493 return retval;
494 }
495
496 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
497 {
498 context->output_handler = output_handler;
499 context->output_handler_priv = priv;
500 }
501
502 command_context_t* copy_command_context(command_context_t* context)
503 {
504 command_context_t* copy_context = malloc(sizeof(command_context_t));
505
506 *copy_context = *context;
507
508 return copy_context;
509 }
510
511 int command_done(command_context_t *context)
512 {
513 free(context);
514 context = NULL;
515
516 return ERROR_OK;
517 }
518
519 /* find full path to file */
520 static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
521 {
522 if (argc != 2)
523 return JIM_ERR;
524 const char *file = Jim_GetString(argv[1], NULL);
525 char *full_path = find_file(file);
526 if (full_path == NULL)
527 return JIM_ERR;
528 Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
529 free(full_path);
530
531 Jim_SetResult(interp, result);
532 return JIM_OK;
533 }
534
535 static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
536 {
537 if (argc != 2)
538 return JIM_ERR;
539 const char *str = Jim_GetString(argv[1], NULL);
540 LOG_USER("%s", str);
541 return JIM_OK;
542 }
543
544 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
545 {
546 size_t nbytes;
547 const char *ptr;
548 Jim_Interp *interp;
549
550 /* make it a char easier to read code */
551 ptr = _ptr;
552 interp = cookie;
553 nbytes = size * n;
554 if (ptr == NULL || interp == NULL || nbytes == 0) {
555 return 0;
556 }
557
558 /* do we have to chunk it? */
559 if (ptr[nbytes] == 0)
560 {
561 /* no it is a C style string */
562 LOG_USER_N("%s", ptr);
563 return strlen(ptr);
564 }
565 /* GRR we must chunk - not null terminated */
566 while (nbytes) {
567 char chunk[128+1];
568 int x;
569
570 x = nbytes;
571 if (x > 128) {
572 x = 128;
573 }
574 /* copy it */
575 memcpy(chunk, ptr, x);
576 /* terminate it */
577 chunk[n] = 0;
578 /* output it */
579 LOG_USER_N("%s", chunk);
580 ptr += x;
581 nbytes -= x;
582 }
583
584 return n;
585 }
586
587 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
588 {
589 /* TCL wants to read... tell him no */
590 return 0;
591 }
592
593 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
594 {
595 char *cp;
596 int n;
597 Jim_Interp *interp;
598
599 n = -1;
600 interp = cookie;
601 if (interp == NULL)
602 return n;
603
604 cp = alloc_vprintf(fmt, ap);
605 if (cp)
606 {
607 LOG_USER_N("%s", cp);
608 n = strlen(cp);
609 free(cp);
610 }
611 return n;
612 }
613
614 static int openocd_jim_fflush(void *cookie)
615 {
616 /* nothing to flush */
617 return 0;
618 }
619
620 static char* openocd_jim_fgets(char *s, int size, void *cookie)
621 {
622 /* not supported */
623 errno = ENOTSUP;
624 return NULL;
625 }
626
627 command_context_t* command_init()
628 {
629 command_context_t* context = malloc(sizeof(command_context_t));
630 extern unsigned const char startup_tcl[];
631
632 context->mode = COMMAND_EXEC;
633 context->commands = NULL;
634 context->current_target = 0;
635 context->output_handler = NULL;
636 context->output_handler_priv = NULL;
637
638 #ifdef JIM_EMBEDDED
639 Jim_InitEmbedded();
640 /* Create an interpreter */
641 interp = Jim_CreateInterp();
642 /* Add all the Jim core commands */
643 Jim_RegisterCoreCommands(interp);
644 #endif
645
646 Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
647 Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
648
649 /* Set Jim's STDIO */
650 interp->cookie_stdin = interp;
651 interp->cookie_stdout = interp;
652 interp->cookie_stderr = interp;
653 interp->cb_fwrite = openocd_jim_fwrite;
654 interp->cb_fread = openocd_jim_fread ;
655 interp->cb_vfprintf = openocd_jim_vfprintf;
656 interp->cb_fflush = openocd_jim_fflush;
657 interp->cb_fgets = openocd_jim_fgets;
658
659 add_default_dirs();
660
661 #ifdef JIM_EMBEDDED
662 Jim_EventLoopOnLoad(interp);
663 #endif
664 if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1)==JIM_ERR)
665 {
666 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD compile time)");
667 Jim_PrintErrorMessage(interp);
668 exit(-1);
669 }
670
671 register_command(context, NULL, "sleep", handle_sleep_command,
672 COMMAND_ANY, "sleep for <n> milliseconds");
673
674 register_command(context, NULL, "fast", handle_fast_command,
675 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
676
677 return context;
678 }
679
680 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
681 {
682 if (!cmd_ctx)
683 return ERROR_INVALID_ARGUMENTS;
684
685 cmd_ctx->mode = mode;
686 return ERROR_OK;
687 }
688
689 /* sleep command sleeps for <n> miliseconds
690 * this is useful in target startup scripts
691 */
692 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
693 {
694 unsigned long duration = 0;
695
696 if (argc == 1)
697 {
698 duration = strtoul(args[0], NULL, 0);
699 alive_sleep(duration);
700 }
701
702 return ERROR_OK;
703 }
704
705 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
706 {
707 if (argc!=1)
708 return ERROR_COMMAND_SYNTAX_ERROR;
709
710 fast_and_dangerous = strcmp("enable", args[0])==0;
711
712 return ERROR_OK;
713 }
714
715 void process_jim_events(void)
716 {
717 #ifdef JIM_EMBEDDED
718 static int recursion = 0;
719
720 if (!recursion)
721 {
722 recursion++;
723 Jim_ProcessEvents (interp, JIM_ALL_EVENTS|JIM_DONT_WAIT);
724 recursion--;
725 }
726 #endif
727 }
728
729 void register_jim(struct command_context_s *cmd_ctx, const char *name, int (*cmd)(Jim_Interp *interp, int argc, Jim_Obj *const *argv), const char *help)
730 {
731 Jim_CreateCommand(interp, name, cmd, NULL, NULL);
732
733 /* FIX!!! it would be prettier to invoke add_help_text...
734 accumulate help text in Tcl helptext list. */
735 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
736 if (Jim_IsShared(helptext))
737 helptext = Jim_DuplicateObj(interp, helptext);
738
739 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
740
741 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
742 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, name, -1));
743
744 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
745 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
746 Jim_ListAppendElement(interp, helptext, cmd_entry);
747 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)