- fix incorrectly registered function openocd_array2mem
[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, "openocd_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("%s%s%s", t1, t2, t3);
182 Jim_CreateCommand(interp, full_name, script_command, c, NULL);
183 free((void *)full_name);
184
185 /* accumulate help text in Tcl helptext list. */
186 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
187 if (Jim_IsShared(helptext))
188 helptext = Jim_DuplicateObj(interp, helptext);
189 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
190
191 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
192
193 /* maximum of two levels :-) */
194 if (c->parent!=NULL)
195 {
196 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
197 }
198 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
199
200 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
201 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
202 Jim_ListAppendElement(interp, helptext, cmd_entry);
203 return c;
204 }
205
206 int unregister_all_commands(command_context_t *context)
207 {
208 command_t *c, *c2;
209
210 if (context == NULL)
211 return ERROR_OK;
212
213 while(NULL != context->commands)
214 {
215 c = context->commands;
216
217 while(NULL != c->children)
218 {
219 c2 = c->children;
220 c->children = c->children->next;
221 free(c2->name);
222 c2->name = NULL;
223 free(c2);
224 c2 = NULL;
225 }
226
227 context->commands = context->commands->next;
228
229 free(c->name);
230 c->name = NULL;
231 free(c);
232 c = NULL;
233 }
234
235 return ERROR_OK;
236 }
237
238 int unregister_command(command_context_t *context, char *name)
239 {
240 command_t *c, *p = NULL, *c2;
241
242 if ((!context) || (!name))
243 return ERROR_INVALID_ARGUMENTS;
244
245 /* find command */
246 for (c = context->commands; c; c = c->next)
247 {
248 if (strcmp(name, c->name) == 0)
249 {
250 /* unlink command */
251 if (p)
252 {
253 p->next = c->next;
254 }
255 else
256 {
257 context->commands = c->next;
258 }
259
260 /* unregister children */
261 if (c->children)
262 {
263 for (c2 = c->children; c2; c2 = c2->next)
264 {
265 free(c2->name);
266 free(c2);
267 }
268 }
269
270 /* delete command */
271 free(c->name);
272 free(c);
273 }
274
275 /* remember the last command for unlinking */
276 p = c;
277 }
278
279 return ERROR_OK;
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 retcode = Jim_Eval(interp, line);
400 if (retcode == JIM_ERR) {
401 if (retval!=ERROR_COMMAND_CLOSE_CONNECTION)
402 {
403 /* We do not print the connection closed error message */
404 Jim_PrintErrorMessage(interp);
405 }
406 if (retval==ERROR_OK)
407 {
408 /* It wasn't a low level OpenOCD command that failed */
409 return ERROR_FAIL;
410 }
411 return retval;
412 } else if (retcode == JIM_EXIT) {
413 /* ignore. */
414 /* exit(Jim_GetExitCode(interp)); */
415 } else {
416 const char *result;
417 int reslen;
418
419 result = Jim_GetString(Jim_GetResult(interp), &reslen);
420 if (reslen) {
421 int i;
422 char buff[256+1];
423 for (i = 0; i < reslen; i += 256)
424 {
425 int chunk;
426 chunk = reslen - i;
427 if (chunk > 256)
428 chunk = 256;
429 strncpy(buff, result+i, chunk);
430 buff[chunk] = 0;
431 LOG_USER_N("%s", buff);
432 }
433 LOG_USER_N("%s", "\n");
434 }
435 }
436 return retval;
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 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
455 {
456 context->output_handler = output_handler;
457 context->output_handler_priv = priv;
458 }
459
460 command_context_t* copy_command_context(command_context_t* context)
461 {
462 command_context_t* copy_context = malloc(sizeof(command_context_t));
463
464 *copy_context = *context;
465
466 return copy_context;
467 }
468
469 int command_done(command_context_t *context)
470 {
471 free(context);
472 context = NULL;
473
474 return ERROR_OK;
475 }
476
477 /* find full path to file */
478 static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
479 {
480 if (argc != 2)
481 return JIM_ERR;
482 const char *file = Jim_GetString(argv[1], NULL);
483 char *full_path = find_file(file);
484 if (full_path == NULL)
485 return JIM_ERR;
486 Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
487 free(full_path);
488
489 Jim_SetResult(interp, result);
490 return JIM_OK;
491 }
492
493 static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
494 {
495 if (argc != 2)
496 return JIM_ERR;
497 const char *str = Jim_GetString(argv[1], NULL);
498 LOG_USER("%s", str);
499 return JIM_OK;
500 }
501
502 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
503 {
504 size_t nbytes;
505 const char *ptr;
506 Jim_Interp *interp;
507 command_context_t *context;
508
509 /* make it a char easier to read code */
510 ptr = _ptr;
511 interp = cookie;
512 nbytes = size * n;
513 if (ptr == NULL || interp == NULL || nbytes == 0) {
514 return 0;
515 }
516
517 context = Jim_GetAssocData(interp, "context");
518 if (context == NULL)
519 {
520 LOG_ERROR("openocd_jim_fwrite: no command context");
521 /* TODO: Where should this go? */
522 return n;
523 }
524
525 /* do we have to chunk it? */
526 if (ptr[nbytes] == 0)
527 {
528 /* no it is a C style string */
529 command_output_text(context, ptr);
530 return strlen(ptr);
531 }
532 /* GRR we must chunk - not null terminated */
533 while (nbytes) {
534 char chunk[128+1];
535 int x;
536
537 x = nbytes;
538 if (x > 128) {
539 x = 128;
540 }
541 /* copy it */
542 memcpy(chunk, ptr, x);
543 /* terminate it */
544 chunk[n] = 0;
545 /* output it */
546 command_output_text(context, chunk);
547 ptr += x;
548 nbytes -= x;
549 }
550
551 return n;
552 }
553
554 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
555 {
556 /* TCL wants to read... tell him no */
557 return 0;
558 }
559
560 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
561 {
562 char *cp;
563 int n;
564 Jim_Interp *interp;
565 command_context_t *context;
566
567 n = -1;
568 interp = cookie;
569 if (interp == NULL)
570 return n;
571
572 context = Jim_GetAssocData(interp, "context");
573 if (context == NULL)
574 {
575 LOG_ERROR("openocd_jim_vfprintf: no command context");
576 return n;
577 }
578
579 cp = alloc_vprintf(fmt, ap);
580 if (cp)
581 {
582 command_output_text(context, cp);
583 n = strlen(cp);
584 free(cp);
585 }
586 return n;
587 }
588
589 static int openocd_jim_fflush(void *cookie)
590 {
591 /* nothing to flush */
592 return 0;
593 }
594
595 static char* openocd_jim_fgets(char *s, int size, void *cookie)
596 {
597 /* not supported */
598 errno = ENOTSUP;
599 return NULL;
600 }
601
602 command_context_t* command_init()
603 {
604 command_context_t* context = malloc(sizeof(command_context_t));
605 extern unsigned const char startup_tcl[];
606
607 context->mode = COMMAND_EXEC;
608 context->commands = NULL;
609 context->current_target = 0;
610 context->output_handler = NULL;
611 context->output_handler_priv = NULL;
612
613 #ifdef JIM_EMBEDDED
614 Jim_InitEmbedded();
615 /* Create an interpreter */
616 interp = Jim_CreateInterp();
617 /* Add all the Jim core commands */
618 Jim_RegisterCoreCommands(interp);
619 #endif
620
621 Jim_CreateCommand(interp, "openocd_find", jim_find, NULL, NULL);
622 Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
623
624 /* Set Jim's STDIO */
625 interp->cookie_stdin = interp;
626 interp->cookie_stdout = interp;
627 interp->cookie_stderr = interp;
628 interp->cb_fwrite = openocd_jim_fwrite;
629 interp->cb_fread = openocd_jim_fread ;
630 interp->cb_vfprintf = openocd_jim_vfprintf;
631 interp->cb_fflush = openocd_jim_fflush;
632 interp->cb_fgets = openocd_jim_fgets;
633
634 add_default_dirs();
635
636 if (Jim_Eval(interp, startup_tcl)==JIM_ERR)
637 {
638 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD compile time)");
639 Jim_PrintErrorMessage(interp);
640 exit(-1);
641 }
642
643 register_command(context, NULL, "sleep", handle_sleep_command,
644 COMMAND_ANY, "sleep for <n> milliseconds");
645
646 register_command(context, NULL, "fast", handle_fast_command,
647 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
648
649 return context;
650 }
651
652 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
653 {
654 if (!cmd_ctx)
655 return ERROR_INVALID_ARGUMENTS;
656
657 cmd_ctx->mode = mode;
658 return ERROR_OK;
659 }
660
661 /* sleep command sleeps for <n> miliseconds
662 * this is useful in target startup scripts
663 */
664 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
665 {
666 unsigned long duration = 0;
667
668 if (argc == 1)
669 {
670 duration = strtoul(args[0], NULL, 0);
671 usleep(duration * 1000);
672 }
673
674 return ERROR_OK;
675 }
676
677 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
678 {
679 if (argc!=1)
680 return ERROR_COMMAND_SYNTAX_ERROR;
681
682 fast_and_dangerous = strcmp("enable", args[0])==0;
683
684 return ERROR_OK;
685 }
686
687 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)
688 {
689 Jim_CreateCommand(interp, name, cmd, NULL, NULL);
690
691 /* FIX!!! it would be prettier to invoke add_help_text...
692 accumulate help text in Tcl helptext list. */
693 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
694 if (Jim_IsShared(helptext))
695 helptext = Jim_DuplicateObj(interp, helptext);
696
697 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
698
699 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
700 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, name, -1));
701
702 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
703 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
704 Jim_ListAppendElement(interp, helptext, cmd_entry);
705 }

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)