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

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)