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

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)