9cc996c226fbe563ad79a87b95d771574f53bc3c
[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 #if !BUILD_ECOSBOARD
34 /* see Embedder-HOWTO.txt in Jim Tcl project hosted on BerliOS*/
35 #define JIM_EMBEDDED
36 #endif
37
38 // @todo the inclusion of target.h here is a layering violation
39 #include "target.h"
40 #include "command.h"
41 #include "configuration.h"
42 #include "log.h"
43 #include "time_support.h"
44 #include "jim-eventloop.h"
45
46
47 Jim_Interp *interp = NULL;
48
49 static int run_command(struct command_context *context,
50 struct command *c, const char *words[], unsigned num_words);
51
52 static void tcl_output(void *privData, const char *file, unsigned line,
53 const char *function, const char *string)
54 {
55 Jim_Obj *tclOutput = (Jim_Obj *)privData;
56 Jim_AppendString(interp, tclOutput, string, strlen(string));
57 }
58
59 extern struct command_context *global_cmd_ctx;
60
61 void script_debug(Jim_Interp *interp, const char *name,
62 unsigned argc, Jim_Obj *const *argv)
63 {
64 LOG_DEBUG("command - %s", name);
65 for (unsigned i = 0; i < argc; i++)
66 {
67 int len;
68 const char *w = Jim_GetString(argv[i], &len);
69
70 /* end of line comment? */
71 if (*w == '#')
72 break;
73
74 LOG_DEBUG("%s - argv[%d]=%s", name, i, w);
75 }
76 }
77
78 static void script_command_args_free(const char **words, unsigned nwords)
79 {
80 for (unsigned i = 0; i < nwords; i++)
81 free((void *)words[i]);
82 free(words);
83 }
84 static const char **script_command_args_alloc(
85 unsigned argc, Jim_Obj *const *argv, unsigned *nwords)
86 {
87 const char **words = malloc(argc * sizeof(char *));
88 if (NULL == words)
89 return NULL;
90
91 unsigned i;
92 for (i = 0; i < argc; i++)
93 {
94 int len;
95 const char *w = Jim_GetString(argv[i], &len);
96 /* a comment may end the line early */
97 if (*w == '#')
98 break;
99
100 words[i] = strdup(w);
101 if (words[i] == NULL)
102 {
103 script_command_args_free(words, i);
104 return NULL;
105 }
106 }
107 *nwords = i;
108 return words;
109 }
110
111 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
112 {
113 /* the private data is stashed in the interp structure */
114 struct command *c;
115 struct command_context *context;
116 int retval;
117
118 /* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might
119 * get overwritten by running other Jim commands! Treat it as an
120 * emphemeral global variable that is used in lieu of an argument
121 * to the fn and fish it out manually.
122 */
123 c = interp->cmdPrivData;
124 if (c == NULL)
125 {
126 LOG_ERROR("BUG: interp->cmdPrivData == NULL");
127 return JIM_ERR;
128 }
129 target_call_timer_callbacks_now();
130 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
131
132 script_debug(interp, c->name, argc, argv);
133
134 unsigned nwords;
135 const char **words = script_command_args_alloc(argc, argv, &nwords);
136 if (NULL == words)
137 return JIM_ERR;
138
139 /* grab the command context from the associated data */
140 context = Jim_GetAssocData(interp, "context");
141 if (context == NULL)
142 {
143 /* Tcl can invoke commands directly instead of via command_run_line(). This would
144 * happen when the Jim Tcl interpreter is provided by eCos.
145 */
146 context = global_cmd_ctx;
147 }
148
149 /* capture log output and return it */
150 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
151 /* a garbage collect can happen, so we need a reference count to this object */
152 Jim_IncrRefCount(tclOutput);
153
154 log_add_callback(tcl_output, tclOutput);
155
156 retval = run_command(context, c, (const char **)words, nwords);
157
158 log_remove_callback(tcl_output, tclOutput);
159
160 /* We dump output into this local variable */
161 Jim_SetResult(interp, tclOutput);
162 Jim_DecrRefCount(interp, tclOutput);
163
164 script_command_args_free(words, nwords);
165
166 int *return_retval = Jim_GetAssocData(interp, "retval");
167 if (return_retval != NULL)
168 {
169 *return_retval = retval;
170 }
171
172 return (retval == ERROR_OK)?JIM_OK:JIM_ERR;
173 }
174
175 /* nice short description of source file */
176 #define __THIS__FILE__ "command.c"
177
178 /**
179 * Find a command by name from a list of commands.
180 * @returns Returns the named command if it exists in the list.
181 * Returns NULL otherwise.
182 */
183 static struct command *command_find(struct command *head, const char *name)
184 {
185 for (struct command *cc = head; cc; cc = cc->next)
186 {
187 if (strcmp(cc->name, name) == 0)
188 return cc;
189 }
190 return NULL;
191 }
192
193 /**
194 * Add the command into the linked list, sorted by name.
195 * @param head Address to head of command list pointer, which may be
196 * updated if @c c gets inserted at the beginning of the list.
197 * @param c The command to add to the list pointed to by @c head.
198 */
199 static void command_add_child(struct command **head, struct command *c)
200 {
201 assert(head);
202 if (NULL == *head)
203 {
204 *head = c;
205 return;
206 }
207
208 while ((*head)->next && (strcmp(c->name, (*head)->name) > 0))
209 head = &(*head)->next;
210
211 if (strcmp(c->name, (*head)->name) > 0) {
212 c->next = (*head)->next;
213 (*head)->next = c;
214 } else {
215 c->next = *head;
216 *head = c;
217 }
218 }
219
220 static struct command **command_list_for_parent(
221 struct command_context *cmd_ctx, struct command *parent)
222 {
223 return parent ? &parent->children : &cmd_ctx->commands;
224 }
225
226 static struct command *command_new(struct command_context *cmd_ctx,
227 struct command *parent, const char *name,
228 command_handler_t handler, enum command_mode mode,
229 const char *help, const char *usage)
230 {
231 assert(name);
232
233 struct command *c = malloc(sizeof(struct command));
234 memset(c, 0, sizeof(struct command));
235
236 c->name = strdup(name);
237 if (help)
238 c->help = strdup(help);
239 if (usage)
240 c->usage = strdup(usage);
241 c->parent = parent;
242 c->handler = handler;
243 c->mode = mode;
244
245 command_add_child(command_list_for_parent(cmd_ctx, parent), c);
246
247 return c;
248 }
249 static void command_free(struct command *c)
250 {
251 /// @todo if command has a handler, unregister its jim command!
252
253 while (NULL != c->children)
254 {
255 struct command *tmp = c->children;
256 c->children = tmp->next;
257 command_free(tmp);
258 }
259
260 if (c->name)
261 free(c->name);
262 if (c->help)
263 free((void*)c->help);
264 if (c->usage)
265 free((void*)c->usage);
266 free(c);
267 }
268
269 static int register_command_handler(struct command *c)
270 {
271 int retval = -ENOMEM;
272 const char *full_name = command_name(c, '_');
273 if (NULL == full_name)
274 return retval;
275
276 const char *ocd_name = alloc_printf("ocd_%s", full_name);
277 if (NULL == full_name)
278 goto free_full_name;
279
280 Jim_CreateCommand(interp, ocd_name, script_command, c, NULL);
281 free((void *)ocd_name);
282
283 /* we now need to add an overrideable proc */
284 const char *override_name = alloc_printf("proc %s {args} {"
285 "if {[catch {eval ocd_%s $args}] == 0} "
286 "{return \"\"} else {return -code error}}",
287 full_name, full_name);
288 if (NULL == full_name)
289 goto free_full_name;
290
291 Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__);
292 free((void *)override_name);
293
294 retval = ERROR_OK;
295
296 free_full_name:
297 free((void *)full_name);
298 return retval;
299 }
300
301 struct command* register_command(struct command_context *context,
302 struct command *parent, const struct command_registration *cr)
303 {
304 if (!context || !cr->name)
305 return NULL;
306
307 const char *name = cr->name;
308 struct command **head = command_list_for_parent(context, parent);
309 struct command *c = command_find(*head, name);
310 if (NULL != c)
311 {
312 LOG_ERROR("command '%s' is already registered in '%s' context",
313 name, parent ? parent->name : "<global>");
314 return c;
315 }
316
317 c = command_new(context, parent, name, cr->handler, cr->mode, cr->help, cr->usage);
318 /* if allocation failed or it is a placeholder (no handler), we're done */
319 if (NULL == c || NULL == c->handler)
320 return c;
321
322 int retval = register_command_handler(c);
323 if (ERROR_OK != retval)
324 {
325 unregister_command(context, parent, name);
326 c = NULL;
327 }
328 return c;
329 }
330
331 int register_commands(struct command_context *cmd_ctx, struct command *parent,
332 const struct command_registration *cmds)
333 {
334 unsigned i;
335 for (i = 0; cmds[i].name; i++)
336 {
337 struct command *c = register_command(cmd_ctx, parent, cmds + i);
338 if (NULL != c)
339 continue;
340
341 for (unsigned j = 0; j < i; j++)
342 unregister_command(cmd_ctx, parent, cmds[j].name);
343 return ERROR_FAIL;
344 }
345 return ERROR_OK;
346 }
347
348 int unregister_all_commands(struct command_context *context,
349 struct command *parent)
350 {
351 if (context == NULL)
352 return ERROR_OK;
353
354 struct command **head = command_list_for_parent(context, parent);
355 while (NULL != *head)
356 {
357 struct command *tmp = *head;
358 *head = tmp->next;
359 command_free(tmp);
360 }
361
362 return ERROR_OK;
363 }
364
365 int unregister_command(struct command_context *context,
366 struct command *parent, const char *name)
367 {
368 if ((!context) || (!name))
369 return ERROR_INVALID_ARGUMENTS;
370
371 struct command *p = NULL;
372 struct command **head = command_list_for_parent(context, parent);
373 for (struct command *c = *head; NULL != c; p = c, c = c->next)
374 {
375 if (strcmp(name, c->name) != 0)
376 continue;
377
378 if (p)
379 p->next = c->next;
380 else
381 *head = c->next;
382
383 command_free(c);
384 return ERROR_OK;
385 }
386
387 return ERROR_OK;
388 }
389
390 void command_output_text(struct command_context *context, const char *data)
391 {
392 if (context && context->output_handler && data) {
393 context->output_handler(context, data);
394 }
395 }
396
397 void command_print_sameline(struct command_context *context, const char *format, ...)
398 {
399 char *string;
400
401 va_list ap;
402 va_start(ap, format);
403
404 string = alloc_vprintf(format, ap);
405 if (string != NULL)
406 {
407 /* we want this collected in the log + we also want to pick it up as a tcl return
408 * value.
409 *
410 * The latter bit isn't precisely neat, but will do for now.
411 */
412 LOG_USER_N("%s", string);
413 /* We already printed it above */
414 /* command_output_text(context, string); */
415 free(string);
416 }
417
418 va_end(ap);
419 }
420
421 void command_print(struct command_context *context, const char *format, ...)
422 {
423 char *string;
424
425 va_list ap;
426 va_start(ap, format);
427
428 string = alloc_vprintf(format, ap);
429 if (string != NULL)
430 {
431 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
432 /* we want this collected in the log + we also want to pick it up as a tcl return
433 * value.
434 *
435 * The latter bit isn't precisely neat, but will do for now.
436 */
437 LOG_USER_N("%s", string);
438 /* We already printed it above */
439 /* command_output_text(context, string); */
440 free(string);
441 }
442
443 va_end(ap);
444 }
445
446 static char *__command_name(struct command *c, char delim, unsigned extra)
447 {
448 char *name;
449 unsigned len = strlen(c->name);
450 if (NULL == c->parent) {
451 // allocate enough for the name, child names, and '\0'
452 name = malloc(len + extra + 1);
453 strcpy(name, c->name);
454 } else {
455 // parent's extra must include both the space and name
456 name = __command_name(c->parent, delim, 1 + len + extra);
457 char dstr[2] = { delim, 0 };
458 strcat(name, dstr);
459 strcat(name, c->name);
460 }
461 return name;
462 }
463 char *command_name(struct command *c, char delim)
464 {
465 return __command_name(c, delim, 0);
466 }
467
468 static int run_command(struct command_context *context,
469 struct command *c, const char *words[], unsigned num_words)
470 {
471 if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode)))
472 {
473 /* Config commands can not run after the config stage */
474 LOG_ERROR("Command '%s' only runs during configuration stage", c->name);
475 return ERROR_FAIL;
476 }
477
478 struct command_invocation cmd = {
479 .ctx = context,
480 .name = c->name,
481 .argc = num_words - 1,
482 .argv = words + 1,
483 };
484 int retval = c->handler(&cmd);
485 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
486 {
487 /* Print help for command */
488 char *full_name = command_name(c, ' ');
489 if (NULL != full_name) {
490 command_run_linef(context, "help %s", full_name);
491 free(full_name);
492 } else
493 retval = -ENOMEM;
494 }
495 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
496 {
497 /* just fall through for a shutdown request */
498 }
499 else if (retval != ERROR_OK)
500 {
501 /* we do not print out an error message because the command *should*
502 * have printed out an error
503 */
504 LOG_DEBUG("Command failed with error code %d", retval);
505 }
506
507 return retval;
508 }
509
510 int command_run_line(struct command_context *context, char *line)
511 {
512 /* all the parent commands have been registered with the interpreter
513 * so, can just evaluate the line as a script and check for
514 * results
515 */
516 /* run the line thru a script engine */
517 int retval = ERROR_FAIL;
518 int retcode;
519 /* Beware! This code needs to be reentrant. It is also possible
520 * for OpenOCD commands to be invoked directly from Tcl. This would
521 * happen when the Jim Tcl interpreter is provided by eCos for
522 * instance.
523 */
524 Jim_DeleteAssocData(interp, "context");
525 retcode = Jim_SetAssocData(interp, "context", NULL, context);
526 if (retcode == JIM_OK)
527 {
528 /* associated the return value */
529 Jim_DeleteAssocData(interp, "retval");
530 retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
531 if (retcode == JIM_OK)
532 {
533 retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__);
534
535 Jim_DeleteAssocData(interp, "retval");
536 }
537 Jim_DeleteAssocData(interp, "context");
538 }
539 if (retcode == JIM_ERR) {
540 if (retval != ERROR_COMMAND_CLOSE_CONNECTION)
541 {
542 /* We do not print the connection closed error message */
543 Jim_PrintErrorMessage(interp);
544 }
545 if (retval == ERROR_OK)
546 {
547 /* It wasn't a low level OpenOCD command that failed */
548 return ERROR_FAIL;
549 }
550 return retval;
551 } else if (retcode == JIM_EXIT) {
552 /* ignore. */
553 /* exit(Jim_GetExitCode(interp)); */
554 } else {
555 const char *result;
556 int reslen;
557
558 result = Jim_GetString(Jim_GetResult(interp), &reslen);
559 if (reslen > 0)
560 {
561 int i;
562 char buff[256 + 1];
563 for (i = 0; i < reslen; i += 256)
564 {
565 int chunk;
566 chunk = reslen - i;
567 if (chunk > 256)
568 chunk = 256;
569 strncpy(buff, result + i, chunk);
570 buff[chunk] = 0;
571 LOG_USER_N("%s", buff);
572 }
573 LOG_USER_N("%s", "\n");
574 }
575 retval = ERROR_OK;
576 }
577 return retval;
578 }
579
580 int command_run_linef(struct command_context *context, const char *format, ...)
581 {
582 int retval = ERROR_FAIL;
583 char *string;
584 va_list ap;
585 va_start(ap, format);
586 string = alloc_vprintf(format, ap);
587 if (string != NULL)
588 {
589 retval = command_run_line(context, string);
590 }
591 va_end(ap);
592 return retval;
593 }
594
595 void command_set_output_handler(struct command_context* context,
596 command_output_handler_t output_handler, void *priv)
597 {
598 context->output_handler = output_handler;
599 context->output_handler_priv = priv;
600 }
601
602 struct command_context* copy_command_context(struct command_context* context)
603 {
604 struct command_context* copy_context = malloc(sizeof(struct command_context));
605
606 *copy_context = *context;
607
608 return copy_context;
609 }
610
611 int command_done(struct command_context *context)
612 {
613 free(context);
614 context = NULL;
615
616 return ERROR_OK;
617 }
618
619 /* find full path to file */
620 static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
621 {
622 if (argc != 2)
623 return JIM_ERR;
624 const char *file = Jim_GetString(argv[1], NULL);
625 char *full_path = find_file(file);
626 if (full_path == NULL)
627 return JIM_ERR;
628 Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
629 free(full_path);
630
631 Jim_SetResult(interp, result);
632 return JIM_OK;
633 }
634
635 static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
636 {
637 if (argc != 2)
638 return JIM_ERR;
639 const char *str = Jim_GetString(argv[1], NULL);
640 LOG_USER("%s", str);
641 return JIM_OK;
642 }
643
644 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
645 {
646 size_t nbytes;
647 const char *ptr;
648 Jim_Interp *interp;
649
650 /* make it a char easier to read code */
651 ptr = _ptr;
652 interp = cookie;
653 nbytes = size * n;
654 if (ptr == NULL || interp == NULL || nbytes == 0) {
655 return 0;
656 }
657
658 /* do we have to chunk it? */
659 if (ptr[nbytes] == 0)
660 {
661 /* no it is a C style string */
662 LOG_USER_N("%s", ptr);
663 return strlen(ptr);
664 }
665 /* GRR we must chunk - not null terminated */
666 while (nbytes) {
667 char chunk[128 + 1];
668 int x;
669
670 x = nbytes;
671 if (x > 128) {
672 x = 128;
673 }
674 /* copy it */
675 memcpy(chunk, ptr, x);
676 /* terminate it */
677 chunk[n] = 0;
678 /* output it */
679 LOG_USER_N("%s", chunk);
680 ptr += x;
681 nbytes -= x;
682 }
683
684 return n;
685 }
686
687 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
688 {
689 /* TCL wants to read... tell him no */
690 return 0;
691 }
692
693 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
694 {
695 char *cp;
696 int n;
697 Jim_Interp *interp;
698
699 n = -1;
700 interp = cookie;
701 if (interp == NULL)
702 return n;
703
704 cp = alloc_vprintf(fmt, ap);
705 if (cp)
706 {
707 LOG_USER_N("%s", cp);
708 n = strlen(cp);
709 free(cp);
710 }
711 return n;
712 }
713
714 static int openocd_jim_fflush(void *cookie)
715 {
716 /* nothing to flush */
717 return 0;
718 }
719
720 static char* openocd_jim_fgets(char *s, int size, void *cookie)
721 {
722 /* not supported */
723 errno = ENOTSUP;
724 return NULL;
725 }
726
727 static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
728 {
729 if (argc != 2)
730 return JIM_ERR;
731 int retcode;
732 const char *str = Jim_GetString(argv[1], NULL);
733
734 /* capture log output and return it */
735 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
736 /* a garbage collect can happen, so we need a reference count to this object */
737 Jim_IncrRefCount(tclOutput);
738
739 log_add_callback(tcl_output, tclOutput);
740
741 retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
742
743 log_remove_callback(tcl_output, tclOutput);
744
745 /* We dump output into this local variable */
746 Jim_SetResult(interp, tclOutput);
747 Jim_DecrRefCount(interp, tclOutput);
748
749 return retcode;
750 }
751
752 static COMMAND_HELPER(command_help_find, struct command *head,
753 struct command **out)
754 {
755 if (0 == CMD_ARGC)
756 return ERROR_INVALID_ARGUMENTS;
757 *out = command_find(head, CMD_ARGV[0]);
758 if (NULL == *out)
759 return ERROR_INVALID_ARGUMENTS;
760 if (--CMD_ARGC == 0)
761 return ERROR_OK;
762 CMD_ARGV++;
763 return CALL_COMMAND_HANDLER(command_help_find, (*out)->children, out);
764 }
765
766 static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
767 bool show_help);
768
769 static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n,
770 bool show_help)
771 {
772 for (struct command *c = head; NULL != c; c = c->next)
773 CALL_COMMAND_HANDLER(command_help_show, c, n, show_help);
774 return ERROR_OK;
775 }
776 static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
777 bool show_help)
778 {
779 const char *usage = c->usage ? : "";
780 const char *help = "";
781 const char *sep = "";
782 if (show_help && c->help)
783 {
784 help = c->help ? : "";
785 sep = c->usage ? " | " : "";
786 }
787 command_run_linef(CMD_CTX, "cmd_help {%s} {%s%s%s} %d",
788 command_name(c, ' '), usage, sep, help, n);
789
790 if (++n >= 2)
791 return ERROR_OK;
792
793 return CALL_COMMAND_HANDLER(command_help_show_list,
794 c->children, n, show_help);
795 }
796 COMMAND_HANDLER(handle_help_command)
797 {
798 struct command *c = CMD_CTX->commands;
799
800 if (0 == CMD_ARGC)
801 return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, true);
802
803 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
804 if (ERROR_OK != retval)
805 return retval;
806
807 return CALL_COMMAND_HANDLER(command_help_show, c, 0, true);
808 }
809
810 COMMAND_HANDLER(handle_usage_command)
811 {
812 struct command *c = CMD_CTX->commands;
813
814 if (0 == CMD_ARGC)
815 return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, false);
816
817 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
818 if (ERROR_OK != retval)
819 return retval;
820
821 return CALL_COMMAND_HANDLER(command_help_show, c, 0, false);
822 }
823
824
825 int help_add_command(struct command_context *cmd_ctx, struct command *parent,
826 const char *cmd_name, const char *help_text, const char *usage)
827 {
828 struct command **head = command_list_for_parent(cmd_ctx, parent);
829 struct command *nc = command_find(*head, cmd_name);
830 if (NULL == nc)
831 {
832 // add a new command with help text
833 struct command_registration cr = {
834 .name = cmd_name,
835 .mode = COMMAND_ANY,
836 .help = help_text,
837 .usage = usage,
838 };
839 nc = register_command(cmd_ctx, parent, &cr);
840 if (NULL == nc)
841 {
842 LOG_ERROR("failed to add '%s' help text", cmd_name);
843 return ERROR_FAIL;
844 }
845 LOG_DEBUG("added '%s' help text", cmd_name);
846 }
847 else
848 {
849 bool replaced = false;
850 if (nc->help)
851 {
852 free((void *)nc->help);
853 replaced = true;
854 }
855 nc->help = strdup(help_text);
856
857 if (replaced)
858 LOG_INFO("replaced existing '%s' help", cmd_name);
859 else
860 LOG_DEBUG("added '%s' help text", cmd_name);
861 }
862 return ERROR_OK;
863 }
864
865 COMMAND_HANDLER(handle_help_add_command)
866 {
867 if (CMD_ARGC < 2)
868 {
869 LOG_ERROR("%s: insufficient arguments", CMD_NAME);
870 return ERROR_INVALID_ARGUMENTS;
871 }
872
873 // save help text and remove it from argument list
874 const char *help_text = CMD_ARGV[--CMD_ARGC];
875 // likewise for the leaf command name
876 const char *cmd_name = CMD_ARGV[--CMD_ARGC];
877
878 struct command *c = NULL;
879 if (CMD_ARGC > 0)
880 {
881 c = CMD_CTX->commands;
882 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
883 if (ERROR_OK != retval)
884 return retval;
885 }
886 return help_add_command(CMD_CTX, c, cmd_name, help_text, NULL);
887 }
888
889 /* sleep command sleeps for <n> miliseconds
890 * this is useful in target startup scripts
891 */
892 COMMAND_HANDLER(handle_sleep_command)
893 {
894 bool busy = false;
895 if (CMD_ARGC == 2)
896 {
897 if (strcmp(CMD_ARGV[1], "busy") == 0)
898 busy = true;
899 else
900 return ERROR_COMMAND_SYNTAX_ERROR;
901 }
902 else if (CMD_ARGC < 1 || CMD_ARGC > 2)
903 return ERROR_COMMAND_SYNTAX_ERROR;
904
905 unsigned long duration = 0;
906 int retval = parse_ulong(CMD_ARGV[0], &duration);
907 if (ERROR_OK != retval)
908 return retval;
909
910 if (!busy)
911 {
912 long long then = timeval_ms();
913 while (timeval_ms() - then < (long long)duration)
914 {
915 target_call_timer_callbacks_now();
916 usleep(1000);
917 }
918 }
919 else
920 busy_sleep(duration);
921
922 return ERROR_OK;
923 }
924
925 static const struct command_registration command_builtin_handlers[] = {
926 {
927 .name = "add_help_text",
928 .handler = &handle_help_add_command,
929 .mode = COMMAND_ANY,
930 .help = "add new command help text",
931 .usage = "<command> [...] <help_text>]",
932 },
933 {
934 .name = "sleep",
935 .handler = &handle_sleep_command,
936 .mode = COMMAND_ANY,
937 .help = "sleep for n milliseconds. "
938 "\"busy\" will busy wait",
939 .usage = "<n> [busy]",
940 },
941 {
942 .name = "help",
943 .handler = &handle_help_command,
944 .mode = COMMAND_ANY,
945 .help = "show built-in command help",
946 .usage = "[<command_name> ...]",
947 },
948 {
949 .name = "usage",
950 .handler = &handle_usage_command,
951 .mode = COMMAND_ANY,
952 .help = "show command usage",
953 .usage = "[<command_name> ...]",
954 },
955 COMMAND_REGISTRATION_DONE
956 };
957
958 struct command_context* command_init(const char *startup_tcl)
959 {
960 struct command_context* context = malloc(sizeof(struct command_context));
961 const char *HostOs;
962
963 context->mode = COMMAND_EXEC;
964 context->commands = NULL;
965 context->current_target = 0;
966 context->output_handler = NULL;
967 context->output_handler_priv = NULL;
968
969 #if !BUILD_ECOSBOARD
970 Jim_InitEmbedded();
971 /* Create an interpreter */
972 interp = Jim_CreateInterp();
973 /* Add all the Jim core commands */
974 Jim_RegisterCoreCommands(interp);
975 #endif
976
977 #if defined(_MSC_VER)
978 /* WinXX - is generic, the forward
979 * looking problem is this:
980 *
981 * "win32" or "win64"
982 *
983 * "winxx" is generic.
984 */
985 HostOs = "winxx";
986 #elif defined(__linux__)
987 HostOs = "linux";
988 #elif defined(__DARWIN__)
989 HostOs = "darwin";
990 #elif defined(__CYGWIN__)
991 HostOs = "cygwin";
992 #elif defined(__MINGW32__)
993 HostOs = "mingw32";
994 #elif defined(__ECOS)
995 HostOs = "ecos";
996 #else
997 #warn unrecognized host OS...
998 HostOs = "other";
999 #endif
1000 Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
1001 Jim_NewStringObj(interp, HostOs , strlen(HostOs)));
1002
1003 Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
1004 Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
1005 Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
1006
1007 /* Set Jim's STDIO */
1008 interp->cookie_stdin = interp;
1009 interp->cookie_stdout = interp;
1010 interp->cookie_stderr = interp;
1011 interp->cb_fwrite = openocd_jim_fwrite;
1012 interp->cb_fread = openocd_jim_fread ;
1013 interp->cb_vfprintf = openocd_jim_vfprintf;
1014 interp->cb_fflush = openocd_jim_fflush;
1015 interp->cb_fgets = openocd_jim_fgets;
1016
1017 register_commands(context, NULL, command_builtin_handlers);
1018
1019 #if !BUILD_ECOSBOARD
1020 Jim_EventLoopOnLoad(interp);
1021 #endif
1022 Jim_SetAssocData(interp, "context", NULL, context);
1023 if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
1024 {
1025 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
1026 Jim_PrintErrorMessage(interp);
1027 exit(-1);
1028 }
1029 Jim_DeleteAssocData(interp, "context");
1030
1031 return context;
1032 }
1033
1034 int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode)
1035 {
1036 if (!cmd_ctx)
1037 return ERROR_INVALID_ARGUMENTS;
1038
1039 cmd_ctx->mode = mode;
1040 return ERROR_OK;
1041 }
1042
1043 void process_jim_events(void)
1044 {
1045 #if !BUILD_ECOSBOARD
1046 static int recursion = 0;
1047
1048 if (!recursion)
1049 {
1050 recursion++;
1051 Jim_ProcessEvents (interp, JIM_ALL_EVENTS | JIM_DONT_WAIT);
1052 recursion--;
1053 }
1054 #endif
1055 }
1056
1057 void register_jim(struct command_context *cmd_ctx, const char *name,
1058 Jim_CmdProc cmd, const char *help)
1059 {
1060 Jim_CreateCommand(interp, name, cmd, NULL, NULL);
1061
1062 Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
1063 Jim_ListAppendElement(interp, cmd_list,
1064 Jim_NewStringObj(interp, name, -1));
1065
1066 help_add_command(cmd_ctx, NULL, name, help, NULL);
1067 }
1068
1069 #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
1070 int parse##name(const char *str, type *ul) \
1071 { \
1072 if (!*str) \
1073 { \
1074 LOG_ERROR("Invalid command argument"); \
1075 return ERROR_COMMAND_ARGUMENT_INVALID; \
1076 } \
1077 char *end; \
1078 *ul = func(str, &end, 0); \
1079 if (*end) \
1080 { \
1081 LOG_ERROR("Invalid command argument"); \
1082 return ERROR_COMMAND_ARGUMENT_INVALID; \
1083 } \
1084 if ((max == *ul) && (ERANGE == errno)) \
1085 { \
1086 LOG_ERROR("Argument overflow"); \
1087 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1088 } \
1089 if (min && (min == *ul) && (ERANGE == errno)) \
1090 { \
1091 LOG_ERROR("Argument underflow"); \
1092 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1093 } \
1094 return ERROR_OK; \
1095 }
1096 DEFINE_PARSE_NUM_TYPE(_ulong, unsigned long , strtoul, 0, ULONG_MAX)
1097 DEFINE_PARSE_NUM_TYPE(_ullong, unsigned long long, strtoull, 0, ULLONG_MAX)
1098 DEFINE_PARSE_NUM_TYPE(_long, long , strtol, LONG_MIN, LONG_MAX)
1099 DEFINE_PARSE_NUM_TYPE(_llong, long long, strtoll, LLONG_MIN, LLONG_MAX)
1100
1101 #define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
1102 int parse##name(const char *str, type *ul) \
1103 { \
1104 functype n; \
1105 int retval = parse##funcname(str, &n); \
1106 if (ERROR_OK != retval) \
1107 return retval; \
1108 if (n > max) \
1109 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1110 if (min) \
1111 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1112 *ul = n; \
1113 return ERROR_OK; \
1114 }
1115
1116 #define DEFINE_PARSE_ULONG(name, type, min, max) \
1117 DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long, _ulong)
1118 DEFINE_PARSE_ULONG(_uint, unsigned, 0, UINT_MAX)
1119 DEFINE_PARSE_ULONG(_u32, uint32_t, 0, UINT32_MAX)
1120 DEFINE_PARSE_ULONG(_u16, uint16_t, 0, UINT16_MAX)
1121 DEFINE_PARSE_ULONG(_u8, uint8_t, 0, UINT8_MAX)
1122
1123 #define DEFINE_PARSE_LONG(name, type, min, max) \
1124 DEFINE_PARSE_WRAPPER(name, type, min, max, long, _long)
1125 DEFINE_PARSE_LONG(_int, int, n < INT_MIN, INT_MAX)
1126 DEFINE_PARSE_LONG(_s32, int32_t, n < INT32_MIN, INT32_MAX)
1127 DEFINE_PARSE_LONG(_s16, int16_t, n < INT16_MIN, INT16_MAX)
1128 DEFINE_PARSE_LONG(_s8, int8_t, n < INT8_MIN, INT8_MAX)
1129
1130 static int command_parse_bool(const char *in, bool *out,
1131 const char *on, const char *off)
1132 {
1133 if (strcasecmp(in, on) == 0)
1134 *out = true;
1135 else if (strcasecmp(in, off) == 0)
1136 *out = false;
1137 else
1138 return ERROR_COMMAND_SYNTAX_ERROR;
1139 return ERROR_OK;
1140 }
1141
1142 int command_parse_bool_arg(const char *in, bool *out)
1143 {
1144 if (command_parse_bool(in, out, "on", "off") == ERROR_OK)
1145 return ERROR_OK;
1146 if (command_parse_bool(in, out, "enable", "disable") == ERROR_OK)
1147 return ERROR_OK;
1148 if (command_parse_bool(in, out, "true", "false") == ERROR_OK)
1149 return ERROR_OK;
1150 if (command_parse_bool(in, out, "yes", "no") == ERROR_OK)
1151 return ERROR_OK;
1152 if (command_parse_bool(in, out, "1", "0") == ERROR_OK)
1153 return ERROR_OK;
1154 return ERROR_INVALID_ARGUMENTS;
1155 }
1156
1157 COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label)
1158 {
1159 switch (CMD_ARGC) {
1160 case 1: {
1161 const char *in = CMD_ARGV[0];
1162 if (command_parse_bool_arg(in, out) != ERROR_OK)
1163 {
1164 LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in);
1165 return ERROR_INVALID_ARGUMENTS;
1166 }
1167 // fall through
1168 }
1169 case 0:
1170 LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled");
1171 break;
1172 default:
1173 return ERROR_INVALID_ARGUMENTS;
1174 }
1175 return ERROR_OK;
1176 }

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)