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

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)