fix regression causing duplicated output
[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 int help_add_command(struct command_context *cmd_ctx, struct command *parent,
952 const char *cmd_name, const char *help_text, const char *usage)
953 {
954 struct command **head = command_list_for_parent(cmd_ctx, parent);
955 struct command *nc = command_find(*head, cmd_name);
956 if (NULL == nc)
957 {
958 // add a new command with help text
959 struct command_registration cr = {
960 .name = cmd_name,
961 .mode = COMMAND_ANY,
962 .help = help_text,
963 .usage = usage,
964 };
965 nc = register_command(cmd_ctx, parent, &cr);
966 if (NULL == nc)
967 {
968 LOG_ERROR("failed to add '%s' help text", cmd_name);
969 return ERROR_FAIL;
970 }
971 LOG_DEBUG("added '%s' help text", cmd_name);
972 return ERROR_OK;
973 }
974 if (help_text)
975 {
976 bool replaced = false;
977 if (nc->help)
978 {
979 free((void *)nc->help);
980 replaced = true;
981 }
982 nc->help = strdup(help_text);
983 if (replaced)
984 LOG_INFO("replaced existing '%s' help", cmd_name);
985 else
986 LOG_DEBUG("added '%s' help text", cmd_name);
987 }
988 if (usage)
989 {
990 bool replaced = false;
991 if (nc->usage)
992 {
993 free((void *)nc->usage);
994 replaced = true;
995 }
996 nc->usage = strdup(usage);
997 if (replaced)
998 LOG_INFO("replaced existing '%s' usage", cmd_name);
999 else
1000 LOG_DEBUG("added '%s' usage text", cmd_name);
1001 }
1002 return ERROR_OK;
1003 }
1004
1005 COMMAND_HANDLER(handle_help_add_command)
1006 {
1007 if (CMD_ARGC < 2)
1008 {
1009 LOG_ERROR("%s: insufficient arguments", CMD_NAME);
1010 return ERROR_INVALID_ARGUMENTS;
1011 }
1012
1013 // save help text and remove it from argument list
1014 const char *str = CMD_ARGV[--CMD_ARGC];
1015 const char *help = !strcmp(CMD_NAME, "add_help_text") ? str : NULL;
1016 const char *usage = !strcmp(CMD_NAME, "add_usage_text") ? str : NULL;
1017 if (!help && !usage)
1018 {
1019 LOG_ERROR("command name '%s' is unknown", CMD_NAME);
1020 return ERROR_INVALID_ARGUMENTS;
1021 }
1022 // likewise for the leaf command name
1023 const char *cmd_name = CMD_ARGV[--CMD_ARGC];
1024
1025 struct command *c = NULL;
1026 if (CMD_ARGC > 0)
1027 {
1028 c = CMD_CTX->commands;
1029 int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
1030 if (ERROR_OK != retval)
1031 return retval;
1032 }
1033 return help_add_command(CMD_CTX, c, cmd_name, help, usage);
1034 }
1035
1036 /* sleep command sleeps for <n> miliseconds
1037 * this is useful in target startup scripts
1038 */
1039 COMMAND_HANDLER(handle_sleep_command)
1040 {
1041 bool busy = false;
1042 if (CMD_ARGC == 2)
1043 {
1044 if (strcmp(CMD_ARGV[1], "busy") == 0)
1045 busy = true;
1046 else
1047 return ERROR_COMMAND_SYNTAX_ERROR;
1048 }
1049 else if (CMD_ARGC < 1 || CMD_ARGC > 2)
1050 return ERROR_COMMAND_SYNTAX_ERROR;
1051
1052 unsigned long duration = 0;
1053 int retval = parse_ulong(CMD_ARGV[0], &duration);
1054 if (ERROR_OK != retval)
1055 return retval;
1056
1057 if (!busy)
1058 {
1059 long long then = timeval_ms();
1060 while (timeval_ms() - then < (long long)duration)
1061 {
1062 target_call_timer_callbacks_now();
1063 usleep(1000);
1064 }
1065 }
1066 else
1067 busy_sleep(duration);
1068
1069 return ERROR_OK;
1070 }
1071
1072 static const struct command_registration command_builtin_handlers[] = {
1073 {
1074 .name = "add_help_text",
1075 .handler = &handle_help_add_command,
1076 .mode = COMMAND_ANY,
1077 .help = "add new command help text",
1078 .usage = "<command> [...] <help_text>]",
1079 },
1080 {
1081 .name = "add_usage_text",
1082 .handler = &handle_help_add_command,
1083 .mode = COMMAND_ANY,
1084 .help = "add new command usage text",
1085 .usage = "<command> [...] <usage_text>]",
1086 },
1087 {
1088 .name = "sleep",
1089 .handler = &handle_sleep_command,
1090 .mode = COMMAND_ANY,
1091 .help = "sleep for n milliseconds. "
1092 "\"busy\" will busy wait",
1093 .usage = "<n> [busy]",
1094 },
1095 {
1096 .name = "help",
1097 .handler = &handle_help_command,
1098 .mode = COMMAND_ANY,
1099 .help = "show full command help",
1100 .usage = "[<command> ...]",
1101 },
1102 {
1103 .name = "usage",
1104 .handler = &handle_help_command,
1105 .mode = COMMAND_ANY,
1106 .help = "show basic command usage",
1107 .usage = "[<command> ...]",
1108 },
1109 COMMAND_REGISTRATION_DONE
1110 };
1111
1112 struct command_context* command_init(const char *startup_tcl)
1113 {
1114 struct command_context* context = malloc(sizeof(struct command_context));
1115 const char *HostOs;
1116
1117 context->mode = COMMAND_EXEC;
1118 context->commands = NULL;
1119 context->current_target = 0;
1120 context->output_handler = NULL;
1121 context->output_handler_priv = NULL;
1122
1123 #if !BUILD_ECOSBOARD
1124 Jim_InitEmbedded();
1125 /* Create an interpreter */
1126 interp = Jim_CreateInterp();
1127 /* Add all the Jim core commands */
1128 Jim_RegisterCoreCommands(interp);
1129 #endif
1130
1131 #if defined(_MSC_VER)
1132 /* WinXX - is generic, the forward
1133 * looking problem is this:
1134 *
1135 * "win32" or "win64"
1136 *
1137 * "winxx" is generic.
1138 */
1139 HostOs = "winxx";
1140 #elif defined(__linux__)
1141 HostOs = "linux";
1142 #elif defined(__DARWIN__)
1143 HostOs = "darwin";
1144 #elif defined(__CYGWIN__)
1145 HostOs = "cygwin";
1146 #elif defined(__MINGW32__)
1147 HostOs = "mingw32";
1148 #elif defined(__ECOS)
1149 HostOs = "ecos";
1150 #else
1151 #warn unrecognized host OS...
1152 HostOs = "other";
1153 #endif
1154 Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
1155 Jim_NewStringObj(interp, HostOs , strlen(HostOs)));
1156
1157 Jim_CreateCommand(interp, "unknown", &command_unknown, NULL, NULL);
1158 Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
1159 Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
1160 Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
1161
1162 /* Set Jim's STDIO */
1163 interp->cookie_stdin = interp;
1164 interp->cookie_stdout = interp;
1165 interp->cookie_stderr = interp;
1166 interp->cb_fwrite = openocd_jim_fwrite;
1167 interp->cb_fread = openocd_jim_fread ;
1168 interp->cb_vfprintf = openocd_jim_vfprintf;
1169 interp->cb_fflush = openocd_jim_fflush;
1170 interp->cb_fgets = openocd_jim_fgets;
1171
1172 register_commands(context, NULL, command_builtin_handlers);
1173
1174 #if !BUILD_ECOSBOARD
1175 Jim_EventLoopOnLoad(interp);
1176 #endif
1177 Jim_SetAssocData(interp, "context", NULL, context);
1178 if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
1179 {
1180 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
1181 Jim_PrintErrorMessage(interp);
1182 exit(-1);
1183 }
1184 Jim_DeleteAssocData(interp, "context");
1185
1186 return context;
1187 }
1188
1189 int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode)
1190 {
1191 if (!cmd_ctx)
1192 return ERROR_INVALID_ARGUMENTS;
1193
1194 cmd_ctx->mode = mode;
1195 return ERROR_OK;
1196 }
1197
1198 void process_jim_events(void)
1199 {
1200 #if !BUILD_ECOSBOARD
1201 static int recursion = 0;
1202
1203 if (!recursion)
1204 {
1205 recursion++;
1206 Jim_ProcessEvents (interp, JIM_ALL_EVENTS | JIM_DONT_WAIT);
1207 recursion--;
1208 }
1209 #endif
1210 }
1211
1212 #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
1213 int parse##name(const char *str, type *ul) \
1214 { \
1215 if (!*str) \
1216 { \
1217 LOG_ERROR("Invalid command argument"); \
1218 return ERROR_COMMAND_ARGUMENT_INVALID; \
1219 } \
1220 char *end; \
1221 *ul = func(str, &end, 0); \
1222 if (*end) \
1223 { \
1224 LOG_ERROR("Invalid command argument"); \
1225 return ERROR_COMMAND_ARGUMENT_INVALID; \
1226 } \
1227 if ((max == *ul) && (ERANGE == errno)) \
1228 { \
1229 LOG_ERROR("Argument overflow"); \
1230 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1231 } \
1232 if (min && (min == *ul) && (ERANGE == errno)) \
1233 { \
1234 LOG_ERROR("Argument underflow"); \
1235 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1236 } \
1237 return ERROR_OK; \
1238 }
1239 DEFINE_PARSE_NUM_TYPE(_ulong, unsigned long , strtoul, 0, ULONG_MAX)
1240 DEFINE_PARSE_NUM_TYPE(_ullong, unsigned long long, strtoull, 0, ULLONG_MAX)
1241 DEFINE_PARSE_NUM_TYPE(_long, long , strtol, LONG_MIN, LONG_MAX)
1242 DEFINE_PARSE_NUM_TYPE(_llong, long long, strtoll, LLONG_MIN, LLONG_MAX)
1243
1244 #define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
1245 int parse##name(const char *str, type *ul) \
1246 { \
1247 functype n; \
1248 int retval = parse##funcname(str, &n); \
1249 if (ERROR_OK != retval) \
1250 return retval; \
1251 if (n > max) \
1252 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1253 if (min) \
1254 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1255 *ul = n; \
1256 return ERROR_OK; \
1257 }
1258
1259 #define DEFINE_PARSE_ULONG(name, type, min, max) \
1260 DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long, _ulong)
1261 DEFINE_PARSE_ULONG(_uint, unsigned, 0, UINT_MAX)
1262 DEFINE_PARSE_ULONG(_u32, uint32_t, 0, UINT32_MAX)
1263 DEFINE_PARSE_ULONG(_u16, uint16_t, 0, UINT16_MAX)
1264 DEFINE_PARSE_ULONG(_u8, uint8_t, 0, UINT8_MAX)
1265
1266 #define DEFINE_PARSE_LONG(name, type, min, max) \
1267 DEFINE_PARSE_WRAPPER(name, type, min, max, long, _long)
1268 DEFINE_PARSE_LONG(_int, int, n < INT_MIN, INT_MAX)
1269 DEFINE_PARSE_LONG(_s32, int32_t, n < INT32_MIN, INT32_MAX)
1270 DEFINE_PARSE_LONG(_s16, int16_t, n < INT16_MIN, INT16_MAX)
1271 DEFINE_PARSE_LONG(_s8, int8_t, n < INT8_MIN, INT8_MAX)
1272
1273 static int command_parse_bool(const char *in, bool *out,
1274 const char *on, const char *off)
1275 {
1276 if (strcasecmp(in, on) == 0)
1277 *out = true;
1278 else if (strcasecmp(in, off) == 0)
1279 *out = false;
1280 else
1281 return ERROR_COMMAND_SYNTAX_ERROR;
1282 return ERROR_OK;
1283 }
1284
1285 int command_parse_bool_arg(const char *in, bool *out)
1286 {
1287 if (command_parse_bool(in, out, "on", "off") == ERROR_OK)
1288 return ERROR_OK;
1289 if (command_parse_bool(in, out, "enable", "disable") == ERROR_OK)
1290 return ERROR_OK;
1291 if (command_parse_bool(in, out, "true", "false") == ERROR_OK)
1292 return ERROR_OK;
1293 if (command_parse_bool(in, out, "yes", "no") == ERROR_OK)
1294 return ERROR_OK;
1295 if (command_parse_bool(in, out, "1", "0") == ERROR_OK)
1296 return ERROR_OK;
1297 return ERROR_INVALID_ARGUMENTS;
1298 }
1299
1300 COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label)
1301 {
1302 switch (CMD_ARGC) {
1303 case 1: {
1304 const char *in = CMD_ARGV[0];
1305 if (command_parse_bool_arg(in, out) != ERROR_OK)
1306 {
1307 LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in);
1308 return ERROR_INVALID_ARGUMENTS;
1309 }
1310 // fall through
1311 }
1312 case 0:
1313 LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled");
1314 break;
1315 default:
1316 return ERROR_INVALID_ARGUMENTS;
1317 }
1318 return ERROR_OK;
1319 }

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)