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

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)