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

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)