add command registration chaining
[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 int retval = ERROR_OK;
335 unsigned i;
336 for (i = 0; cmds[i].name || cmds[i].chain; i++)
337 {
338 const struct command_registration *cr = cmds + i;
339
340 struct command *c = NULL;
341 if (NULL != cr->name)
342 {
343 c = register_command(cmd_ctx, parent, cr);
344 if (NULL == c)
345 {
346 retval = ERROR_FAIL;
347 break;
348 }
349 }
350 if (NULL != cr->chain)
351 {
352 struct command *p = c ? : parent;
353 retval = register_commands(cmd_ctx, p, cr->chain);
354 if (ERROR_OK != retval)
355 break;
356 }
357 }
358 if (ERROR_OK != retval)
359 {
360 for (unsigned j = 0; j < i; j++)
361 unregister_command(cmd_ctx, parent, cmds[j].name);
362 }
363 return retval;
364 }
365
366 int unregister_all_commands(struct command_context *context,
367 struct command *parent)
368 {
369 if (context == NULL)
370 return ERROR_OK;
371
372 struct command **head = command_list_for_parent(context, parent);
373 while (NULL != *head)
374 {
375 struct command *tmp = *head;
376 *head = tmp->next;
377 command_free(tmp);
378 }
379
380 return ERROR_OK;
381 }
382
383 int unregister_command(struct command_context *context,
384 struct command *parent, const char *name)
385 {
386 if ((!context) || (!name))
387 return ERROR_INVALID_ARGUMENTS;
388
389 struct command *p = NULL;
390 struct command **head = command_list_for_parent(context, parent);
391 for (struct command *c = *head; NULL != c; p = c, c = c->next)
392 {
393 if (strcmp(name, c->name) != 0)
394 continue;
395
396 if (p)
397 p->next = c->next;
398 else
399 *head = c->next;
400
401 command_free(c);
402 return ERROR_OK;
403 }
404
405 return ERROR_OK;
406 }
407
408 void command_output_text(struct command_context *context, const char *data)
409 {
410 if (context && context->output_handler && data) {
411 context->output_handler(context, data);
412 }
413 }
414
415 void command_print_sameline(struct command_context *context, const char *format, ...)
416 {
417 char *string;
418
419 va_list ap;
420 va_start(ap, format);
421
422 string = alloc_vprintf(format, ap);
423 if (string != NULL)
424 {
425 /* we want this collected in the log + we also want to pick it up as a tcl return
426 * value.
427 *
428 * The latter bit isn't precisely neat, but will do for now.
429 */
430 LOG_USER_N("%s", string);
431 /* We already printed it above */
432 /* command_output_text(context, string); */
433 free(string);
434 }
435
436 va_end(ap);
437 }
438
439 void command_print(struct command_context *context, const char *format, ...)
440 {
441 char *string;
442
443 va_list ap;
444 va_start(ap, format);
445
446 string = alloc_vprintf(format, ap);
447 if (string != NULL)
448 {
449 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
450 /* we want this collected in the log + we also want to pick it up as a tcl return
451 * value.
452 *
453 * The latter bit isn't precisely neat, but will do for now.
454 */
455 LOG_USER_N("%s", string);
456 /* We already printed it above */
457 /* command_output_text(context, string); */
458 free(string);
459 }
460
461 va_end(ap);
462 }
463
464 static char *__command_name(struct command *c, char delim, unsigned extra)
465 {
466 char *name;
467 unsigned len = strlen(c->name);
468 if (NULL == c->parent) {
469 // allocate enough for the name, child names, and '\0'
470 name = malloc(len + extra + 1);
471 strcpy(name, c->name);
472 } else {
473 // parent's extra must include both the space and name
474 name = __command_name(c->parent, delim, 1 + len + extra);
475 char dstr[2] = { delim, 0 };
476 strcat(name, dstr);
477 strcat(name, c->name);
478 }
479 return name;
480 }
481 char *command_name(struct command *c, char delim)
482 {
483 return __command_name(c, delim, 0);
484 }
485
486 static int run_command(struct command_context *context,
487 struct command *c, const char *words[], unsigned num_words)
488 {
489 if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode)))
490 {
491 /* Config commands can not run after the config stage */
492 LOG_ERROR("Command '%s' only runs during configuration stage", c->name);
493 return ERROR_FAIL;
494 }
495
496 struct command_invocation cmd = {
497 .ctx = context,
498 .name = c->name,
499 .argc = num_words - 1,
500 .argv = words + 1,
501 };
502 int retval = c->handler(&cmd);
503 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
504 {
505 /* Print help for command */
506 char *full_name = command_name(c, ' ');
507 if (NULL != full_name) {
508 command_run_linef(context, "help %s", full_name);
509 free(full_name);
510 } else
511 retval = -ENOMEM;
512 }
513 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
514 {
515 /* just fall through for a shutdown request */
516 }
517 else if (retval != ERROR_OK)
518 {
519 /* we do not print out an error message because the command *should*
520 * have printed out an error
521 */
522 LOG_DEBUG("Command failed with error code %d", retval);
523 }
524
525 return retval;
526 }
527
528 int command_run_line(struct command_context *context, char *line)
529 {
530 /* all the parent commands have been registered with the interpreter
531 * so, can just evaluate the line as a script and check for
532 * results
533 */
534 /* run the line thru a script engine */
535 int retval = ERROR_FAIL;
536 int retcode;
537 /* Beware! This code needs to be reentrant. It is also possible
538 * for OpenOCD commands to be invoked directly from Tcl. This would
539 * happen when the Jim Tcl interpreter is provided by eCos for
540 * instance.
541 */
542 Jim_DeleteAssocData(interp, "context");
543 retcode = Jim_SetAssocData(interp, "context", NULL, context);
544 if (retcode == JIM_OK)
545 {
546 /* associated the return value */
547 Jim_DeleteAssocData(interp, "retval");
548 retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
549 if (retcode == JIM_OK)
550 {
551 retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__);
552
553 Jim_DeleteAssocData(interp, "retval");
554 }
555 Jim_DeleteAssocData(interp, "context");
556 }
557 if (retcode == JIM_ERR) {
558 if (retval != ERROR_COMMAND_CLOSE_CONNECTION)
559 {
560 /* We do not print the connection closed error message */
561 Jim_PrintErrorMessage(interp);
562 }
563 if (retval == ERROR_OK)
564 {
565 /* It wasn't a low level OpenOCD command that failed */
566 return ERROR_FAIL;
567 }
568 return retval;
569 } else if (retcode == JIM_EXIT) {
570 /* ignore. */
571 /* exit(Jim_GetExitCode(interp)); */
572 } else {
573 const char *result;
574 int reslen;
575
576 result = Jim_GetString(Jim_GetResult(interp), &reslen);
577 if (reslen > 0)
578 {
579 int i;
580 char buff[256 + 1];
581 for (i = 0; i < reslen; i += 256)
582 {
583 int chunk;
584 chunk = reslen - i;
585 if (chunk > 256)
586 chunk = 256;
587 strncpy(buff, result + i, chunk);
588 buff[chunk] = 0;
589 LOG_USER_N("%s", buff);
590 }
591 LOG_USER_N("%s", "\n");
592 }
593 retval = ERROR_OK;
594 }
595 return retval;
596 }
597
598 int command_run_linef(struct command_context *context, const char *format, ...)
599 {
600 int retval = ERROR_FAIL;
601 char *string;
602 va_list ap;
603 va_start(ap, format);
604 string = alloc_vprintf(format, ap);
605 if (string != NULL)
606 {
607 retval = command_run_line(context, string);
608 }
609 va_end(ap);
610 return retval;
611 }
612
613 void command_set_output_handler(struct command_context* context,
614 command_output_handler_t output_handler, void *priv)
615 {
616 context->output_handler = output_handler;
617 context->output_handler_priv = priv;
618 }
619
620 struct command_context* copy_command_context(struct command_context* context)
621 {
622 struct command_context* copy_context = malloc(sizeof(struct command_context));
623
624 *copy_context = *context;
625
626 return copy_context;
627 }
628
629 int command_done(struct command_context *context)
630 {
631 free(context);
632 context = NULL;
633
634 return ERROR_OK;
635 }
636
637 /* find full path to file */
638 static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
639 {
640 if (argc != 2)
641 return JIM_ERR;
642 const char *file = Jim_GetString(argv[1], NULL);
643 char *full_path = find_file(file);
644 if (full_path == NULL)
645 return JIM_ERR;
646 Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
647 free(full_path);
648
649 Jim_SetResult(interp, result);
650 return JIM_OK;
651 }
652
653 static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
654 {
655 if (argc != 2)
656 return JIM_ERR;
657 const char *str = Jim_GetString(argv[1], NULL);
658 LOG_USER("%s", str);
659 return JIM_OK;
660 }
661
662 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
663 {
664 size_t nbytes;
665 const char *ptr;
666 Jim_Interp *interp;
667
668 /* make it a char easier to read code */
669 ptr = _ptr;
670 interp = cookie;
671 nbytes = size * n;
672 if (ptr == NULL || interp == NULL || nbytes == 0) {
673 return 0;
674 }
675
676 /* do we have to chunk it? */
677 if (ptr[nbytes] == 0)
678 {
679 /* no it is a C style string */
680 LOG_USER_N("%s", ptr);
681 return strlen(ptr);
682 }
683 /* GRR we must chunk - not null terminated */
684 while (nbytes) {
685 char chunk[128 + 1];
686 int x;
687
688 x = nbytes;
689 if (x > 128) {
690 x = 128;
691 }
692 /* copy it */
693 memcpy(chunk, ptr, x);
694 /* terminate it */
695 chunk[n] = 0;
696 /* output it */
697 LOG_USER_N("%s", chunk);
698 ptr += x;
699 nbytes -= x;
700 }
701
702 return n;
703 }
704
705 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
706 {
707 /* TCL wants to read... tell him no */
708 return 0;
709 }
710
711 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
712 {
713 char *cp;
714 int n;
715 Jim_Interp *interp;
716
717 n = -1;
718 interp = cookie;
719 if (interp == NULL)
720 return n;
721
722 cp = alloc_vprintf(fmt, ap);
723 if (cp)
724 {
725 LOG_USER_N("%s", cp);
726 n = strlen(cp);
727 free(cp);
728 }
729 return n;
730 }
731
732 static int openocd_jim_fflush(void *cookie)
733 {
734 /* nothing to flush */
735 return 0;
736 }
737
738 static char* openocd_jim_fgets(char *s, int size, void *cookie)
739 {
740 /* not supported */
741 errno = ENOTSUP;
742 return NULL;
743 }
744
745 static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
746 {
747 if (argc != 2)
748 return JIM_ERR;
749 int retcode;
750 const char *str = Jim_GetString(argv[1], NULL);
751
752 /* capture log output and return it */
753 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
754 /* a garbage collect can happen, so we need a reference count to this object */
755 Jim_IncrRefCount(tclOutput);
756
757 log_add_callback(tcl_output, tclOutput);
758
759 retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
760
761 log_remove_callback(tcl_output, tclOutput);
762
763 /* We dump output into this local variable */
764 Jim_SetResult(interp, tclOutput);
765 Jim_DecrRefCount(interp, tclOutput);
766
767 return retcode;
768 }
769
770 static COMMAND_HELPER(command_help_find, struct command *head,
771 struct command **out)
772 {
773 if (0 == CMD_ARGC)
774 return ERROR_INVALID_ARGUMENTS;
775 *out = command_find(head, CMD_ARGV[0]);
776 if (NULL == *out)
777 return ERROR_INVALID_ARGUMENTS;
778 if (--CMD_ARGC == 0)
779 return ERROR_OK;
780 CMD_ARGV++;
781 return CALL_COMMAND_HANDLER(command_help_find, (*out)->children, out);
782 }
783
784 static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
785 bool show_help);
786
787 static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n,
788 bool show_help)
789 {
790 for (struct command *c = head; NULL != c; c = c->next)
791 CALL_COMMAND_HANDLER(command_help_show, c, n, show_help);
792 return ERROR_OK;
793 }
794 static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
795 bool show_help)
796 {
797 const char *usage = c->usage ? : "";
798 const char *help = "";
799 const char *sep = "";
800 if (show_help && c->help)
801 {
802 help = c->help ? : "";
803 sep = c->usage ? " | " : "";
804 }
805 command_run_linef(CMD_CTX, "cmd_help {%s} {%s%s%s} %d",
806 command_name(c, ' '), usage, sep, help, n);
807
808 if (++n >= 2)
809 return ERROR_OK;
810
811 return CALL_COMMAND_HANDLER(command_help_show_list,
812 c->children, n, show_help);
813 }
814 COMMAND_HANDLER(handle_help_command)
815 {
816 struct command *c = CMD_CTX->commands;
817
818 if (0 == CMD_ARGC)
819 return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, true);
820
821 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
822 if (ERROR_OK != retval)
823 return retval;
824
825 return CALL_COMMAND_HANDLER(command_help_show, c, 0, true);
826 }
827
828 COMMAND_HANDLER(handle_usage_command)
829 {
830 struct command *c = CMD_CTX->commands;
831
832 if (0 == CMD_ARGC)
833 return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, false);
834
835 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
836 if (ERROR_OK != retval)
837 return retval;
838
839 return CALL_COMMAND_HANDLER(command_help_show, c, 0, false);
840 }
841
842
843 int help_add_command(struct command_context *cmd_ctx, struct command *parent,
844 const char *cmd_name, const char *help_text, const char *usage)
845 {
846 struct command **head = command_list_for_parent(cmd_ctx, parent);
847 struct command *nc = command_find(*head, cmd_name);
848 if (NULL == nc)
849 {
850 // add a new command with help text
851 struct command_registration cr = {
852 .name = cmd_name,
853 .mode = COMMAND_ANY,
854 .help = help_text,
855 .usage = usage,
856 };
857 nc = register_command(cmd_ctx, parent, &cr);
858 if (NULL == nc)
859 {
860 LOG_ERROR("failed to add '%s' help text", cmd_name);
861 return ERROR_FAIL;
862 }
863 LOG_DEBUG("added '%s' help text", cmd_name);
864 }
865 else
866 {
867 bool replaced = false;
868 if (nc->help)
869 {
870 free((void *)nc->help);
871 replaced = true;
872 }
873 nc->help = strdup(help_text);
874
875 if (replaced)
876 LOG_INFO("replaced existing '%s' help", cmd_name);
877 else
878 LOG_DEBUG("added '%s' help text", cmd_name);
879 }
880 return ERROR_OK;
881 }
882
883 COMMAND_HANDLER(handle_help_add_command)
884 {
885 if (CMD_ARGC < 2)
886 {
887 LOG_ERROR("%s: insufficient arguments", CMD_NAME);
888 return ERROR_INVALID_ARGUMENTS;
889 }
890
891 // save help text and remove it from argument list
892 const char *help_text = CMD_ARGV[--CMD_ARGC];
893 // likewise for the leaf command name
894 const char *cmd_name = CMD_ARGV[--CMD_ARGC];
895
896 struct command *c = NULL;
897 if (CMD_ARGC > 0)
898 {
899 c = CMD_CTX->commands;
900 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
901 if (ERROR_OK != retval)
902 return retval;
903 }
904 return help_add_command(CMD_CTX, c, cmd_name, help_text, NULL);
905 }
906
907 /* sleep command sleeps for <n> miliseconds
908 * this is useful in target startup scripts
909 */
910 COMMAND_HANDLER(handle_sleep_command)
911 {
912 bool busy = false;
913 if (CMD_ARGC == 2)
914 {
915 if (strcmp(CMD_ARGV[1], "busy") == 0)
916 busy = true;
917 else
918 return ERROR_COMMAND_SYNTAX_ERROR;
919 }
920 else if (CMD_ARGC < 1 || CMD_ARGC > 2)
921 return ERROR_COMMAND_SYNTAX_ERROR;
922
923 unsigned long duration = 0;
924 int retval = parse_ulong(CMD_ARGV[0], &duration);
925 if (ERROR_OK != retval)
926 return retval;
927
928 if (!busy)
929 {
930 long long then = timeval_ms();
931 while (timeval_ms() - then < (long long)duration)
932 {
933 target_call_timer_callbacks_now();
934 usleep(1000);
935 }
936 }
937 else
938 busy_sleep(duration);
939
940 return ERROR_OK;
941 }
942
943 static const struct command_registration command_builtin_handlers[] = {
944 {
945 .name = "add_help_text",
946 .handler = &handle_help_add_command,
947 .mode = COMMAND_ANY,
948 .help = "add new command help text",
949 .usage = "<command> [...] <help_text>]",
950 },
951 {
952 .name = "sleep",
953 .handler = &handle_sleep_command,
954 .mode = COMMAND_ANY,
955 .help = "sleep for n milliseconds. "
956 "\"busy\" will busy wait",
957 .usage = "<n> [busy]",
958 },
959 {
960 .name = "help",
961 .handler = &handle_help_command,
962 .mode = COMMAND_ANY,
963 .help = "show built-in command help",
964 .usage = "[<command_name> ...]",
965 },
966 {
967 .name = "usage",
968 .handler = &handle_usage_command,
969 .mode = COMMAND_ANY,
970 .help = "show command usage",
971 .usage = "[<command_name> ...]",
972 },
973 COMMAND_REGISTRATION_DONE
974 };
975
976 struct command_context* command_init(const char *startup_tcl)
977 {
978 struct command_context* context = malloc(sizeof(struct command_context));
979 const char *HostOs;
980
981 context->mode = COMMAND_EXEC;
982 context->commands = NULL;
983 context->current_target = 0;
984 context->output_handler = NULL;
985 context->output_handler_priv = NULL;
986
987 #if !BUILD_ECOSBOARD
988 Jim_InitEmbedded();
989 /* Create an interpreter */
990 interp = Jim_CreateInterp();
991 /* Add all the Jim core commands */
992 Jim_RegisterCoreCommands(interp);
993 #endif
994
995 #if defined(_MSC_VER)
996 /* WinXX - is generic, the forward
997 * looking problem is this:
998 *
999 * "win32" or "win64"
1000 *
1001 * "winxx" is generic.
1002 */
1003 HostOs = "winxx";
1004 #elif defined(__linux__)
1005 HostOs = "linux";
1006 #elif defined(__DARWIN__)
1007 HostOs = "darwin";
1008 #elif defined(__CYGWIN__)
1009 HostOs = "cygwin";
1010 #elif defined(__MINGW32__)
1011 HostOs = "mingw32";
1012 #elif defined(__ECOS)
1013 HostOs = "ecos";
1014 #else
1015 #warn unrecognized host OS...
1016 HostOs = "other";
1017 #endif
1018 Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
1019 Jim_NewStringObj(interp, HostOs , strlen(HostOs)));
1020
1021 Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
1022 Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
1023 Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
1024
1025 /* Set Jim's STDIO */
1026 interp->cookie_stdin = interp;
1027 interp->cookie_stdout = interp;
1028 interp->cookie_stderr = interp;
1029 interp->cb_fwrite = openocd_jim_fwrite;
1030 interp->cb_fread = openocd_jim_fread ;
1031 interp->cb_vfprintf = openocd_jim_vfprintf;
1032 interp->cb_fflush = openocd_jim_fflush;
1033 interp->cb_fgets = openocd_jim_fgets;
1034
1035 register_commands(context, NULL, command_builtin_handlers);
1036
1037 #if !BUILD_ECOSBOARD
1038 Jim_EventLoopOnLoad(interp);
1039 #endif
1040 Jim_SetAssocData(interp, "context", NULL, context);
1041 if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
1042 {
1043 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
1044 Jim_PrintErrorMessage(interp);
1045 exit(-1);
1046 }
1047 Jim_DeleteAssocData(interp, "context");
1048
1049 return context;
1050 }
1051
1052 int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode)
1053 {
1054 if (!cmd_ctx)
1055 return ERROR_INVALID_ARGUMENTS;
1056
1057 cmd_ctx->mode = mode;
1058 return ERROR_OK;
1059 }
1060
1061 void process_jim_events(void)
1062 {
1063 #if !BUILD_ECOSBOARD
1064 static int recursion = 0;
1065
1066 if (!recursion)
1067 {
1068 recursion++;
1069 Jim_ProcessEvents (interp, JIM_ALL_EVENTS | JIM_DONT_WAIT);
1070 recursion--;
1071 }
1072 #endif
1073 }
1074
1075 void register_jim(struct command_context *cmd_ctx, const char *name,
1076 Jim_CmdProc cmd, const char *help)
1077 {
1078 Jim_CreateCommand(interp, name, cmd, NULL, NULL);
1079
1080 Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
1081 Jim_ListAppendElement(interp, cmd_list,
1082 Jim_NewStringObj(interp, name, -1));
1083
1084 help_add_command(cmd_ctx, NULL, name, help, NULL);
1085 }
1086
1087 #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
1088 int parse##name(const char *str, type *ul) \
1089 { \
1090 if (!*str) \
1091 { \
1092 LOG_ERROR("Invalid command argument"); \
1093 return ERROR_COMMAND_ARGUMENT_INVALID; \
1094 } \
1095 char *end; \
1096 *ul = func(str, &end, 0); \
1097 if (*end) \
1098 { \
1099 LOG_ERROR("Invalid command argument"); \
1100 return ERROR_COMMAND_ARGUMENT_INVALID; \
1101 } \
1102 if ((max == *ul) && (ERANGE == errno)) \
1103 { \
1104 LOG_ERROR("Argument overflow"); \
1105 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1106 } \
1107 if (min && (min == *ul) && (ERANGE == errno)) \
1108 { \
1109 LOG_ERROR("Argument underflow"); \
1110 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1111 } \
1112 return ERROR_OK; \
1113 }
1114 DEFINE_PARSE_NUM_TYPE(_ulong, unsigned long , strtoul, 0, ULONG_MAX)
1115 DEFINE_PARSE_NUM_TYPE(_ullong, unsigned long long, strtoull, 0, ULLONG_MAX)
1116 DEFINE_PARSE_NUM_TYPE(_long, long , strtol, LONG_MIN, LONG_MAX)
1117 DEFINE_PARSE_NUM_TYPE(_llong, long long, strtoll, LLONG_MIN, LLONG_MAX)
1118
1119 #define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
1120 int parse##name(const char *str, type *ul) \
1121 { \
1122 functype n; \
1123 int retval = parse##funcname(str, &n); \
1124 if (ERROR_OK != retval) \
1125 return retval; \
1126 if (n > max) \
1127 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1128 if (min) \
1129 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1130 *ul = n; \
1131 return ERROR_OK; \
1132 }
1133
1134 #define DEFINE_PARSE_ULONG(name, type, min, max) \
1135 DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long, _ulong)
1136 DEFINE_PARSE_ULONG(_uint, unsigned, 0, UINT_MAX)
1137 DEFINE_PARSE_ULONG(_u32, uint32_t, 0, UINT32_MAX)
1138 DEFINE_PARSE_ULONG(_u16, uint16_t, 0, UINT16_MAX)
1139 DEFINE_PARSE_ULONG(_u8, uint8_t, 0, UINT8_MAX)
1140
1141 #define DEFINE_PARSE_LONG(name, type, min, max) \
1142 DEFINE_PARSE_WRAPPER(name, type, min, max, long, _long)
1143 DEFINE_PARSE_LONG(_int, int, n < INT_MIN, INT_MAX)
1144 DEFINE_PARSE_LONG(_s32, int32_t, n < INT32_MIN, INT32_MAX)
1145 DEFINE_PARSE_LONG(_s16, int16_t, n < INT16_MIN, INT16_MAX)
1146 DEFINE_PARSE_LONG(_s8, int8_t, n < INT8_MIN, INT8_MAX)
1147
1148 static int command_parse_bool(const char *in, bool *out,
1149 const char *on, const char *off)
1150 {
1151 if (strcasecmp(in, on) == 0)
1152 *out = true;
1153 else if (strcasecmp(in, off) == 0)
1154 *out = false;
1155 else
1156 return ERROR_COMMAND_SYNTAX_ERROR;
1157 return ERROR_OK;
1158 }
1159
1160 int command_parse_bool_arg(const char *in, bool *out)
1161 {
1162 if (command_parse_bool(in, out, "on", "off") == ERROR_OK)
1163 return ERROR_OK;
1164 if (command_parse_bool(in, out, "enable", "disable") == ERROR_OK)
1165 return ERROR_OK;
1166 if (command_parse_bool(in, out, "true", "false") == ERROR_OK)
1167 return ERROR_OK;
1168 if (command_parse_bool(in, out, "yes", "no") == ERROR_OK)
1169 return ERROR_OK;
1170 if (command_parse_bool(in, out, "1", "0") == ERROR_OK)
1171 return ERROR_OK;
1172 return ERROR_INVALID_ARGUMENTS;
1173 }
1174
1175 COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label)
1176 {
1177 switch (CMD_ARGC) {
1178 case 1: {
1179 const char *in = CMD_ARGV[0];
1180 if (command_parse_bool_arg(in, out) != ERROR_OK)
1181 {
1182 LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in);
1183 return ERROR_INVALID_ARGUMENTS;
1184 }
1185 // fall through
1186 }
1187 case 0:
1188 LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled");
1189 break;
1190 default:
1191 return ERROR_INVALID_ARGUMENTS;
1192 }
1193 return ERROR_OK;
1194 }

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)